diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..7575478a1 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +#root = true + +[*] +indent_style = space +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +max_line_length = 100 +indent_size = 4 + +[*.md] +trim_trailing_whitespace = false diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 1ff0c4230..000000000 --- a/.gitattributes +++ /dev/null @@ -1,63 +0,0 @@ -############################################################################### -# Set default behavior to automatically normalize line endings. -############################################################################### -* text=auto - -############################################################################### -# Set default behavior for command prompt diff. -# -# This is need for earlier builds of msysgit that does not have it on by -# default for csharp files. -# Note: This is only used by command line -############################################################################### -#*.cs diff=csharp - -############################################################################### -# Set the merge driver for project and solution files -# -# Merging from the command prompt will add diff markers to the files if there -# are conflicts (Merging from VS is not affected by the settings below, in VS -# the diff markers are never inserted). Diff markers may cause the following -# file extensions to fail to load in VS. An alternative would be to treat -# these files as binary and thus will always conflict and require user -# intervention with every merge. To do so, just uncomment the entries below -############################################################################### -#*.sln merge=binary -#*.csproj merge=binary -#*.vbproj merge=binary -#*.vcxproj merge=binary -#*.vcproj merge=binary -#*.dbproj merge=binary -#*.fsproj merge=binary -#*.lsproj merge=binary -#*.wixproj merge=binary -#*.modelproj merge=binary -#*.sqlproj merge=binary -#*.wwaproj merge=binary - -############################################################################### -# behavior for image files -# -# image files are treated as binary by default. -############################################################################### -#*.jpg binary -#*.png binary -#*.gif binary - -############################################################################### -# diff behavior for common document formats -# -# Convert binary document formats to text before diffing them. This feature -# is only available from the command line. Turn it on by uncommenting the -# entries below. -############################################################################### -#*.doc diff=astextplain -#*.DOC diff=astextplain -#*.docx diff=astextplain -#*.DOCX diff=astextplain -#*.dot diff=astextplain -#*.DOT diff=astextplain -#*.pdf diff=astextplain -#*.PDF diff=astextplain -#*.rtf diff=astextplain -#*.RTF diff=astextplain diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index c917ae996..b7c06f6c3 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,12 +1,19 @@ --- name: 🕷 Bug report -about: Report a bug +about: Report a bug. --- - https://github.com/CoderLine/alphaTab - git - music,notation,engraver,renderer - - - \ No newline at end of file diff --git a/Documentation/config.wyam b/Documentation/config.wyam deleted file mode 100644 index 900374dc8..000000000 --- a/Documentation/config.wyam +++ /dev/null @@ -1,13 +0,0 @@ -#recipe Docs - -Settings[Keys.Host] = "docs.alphaTab.net"; -Settings[Keys.LinksUseHttps] = true; -Settings[DocsKeys.Title] = "alphaTab Documentation"; -Settings[DocsKeys.Logo] = "/assets/img/alphaTab.png"; -Settings[DocsKeys.BaseEditUrl] = "https://github.com/CoderLine/alphaTab/tree/develop/Documentation/input/"; -Settings[DocsKeys.ImplicitInheritDoc] = true; -Settings[DocsKeys.ProjectFiles] = null; -Settings[DocsKeys.ProjectFiles] = null; -Settings[DocsKeys.SourceFiles] = @"../../Source/**/{!bin,!obj,!packages,!*.Tests,!Haxe*,!Microsoft*,}/**/*.cs"; - -// Add any pipeline customizations here \ No newline at end of file diff --git a/Documentation/config.wyam.dll b/Documentation/config.wyam.dll deleted file mode 100644 index a9f2f70bf..000000000 Binary files a/Documentation/config.wyam.dll and /dev/null differ diff --git a/Documentation/config.wyam.hash b/Documentation/config.wyam.hash deleted file mode 100644 index c1e01fbe1..000000000 --- a/Documentation/config.wyam.hash +++ /dev/null @@ -1 +0,0 @@ -D7720BCEBDAA7466921A9EA9B2BD9E13CAD1E2B19A8D6EB4B12F99D43F5F5115D9204A0D54F1F81F81427DD75B8D7CB83B30280B2550CD52DB37A3C1E1A66F7C \ No newline at end of file diff --git a/Documentation/config.wyam.packages.xml b/Documentation/config.wyam.packages.xml deleted file mode 100644 index df22d11b6..000000000 --- a/Documentation/config.wyam.packages.xml +++ /dev/null @@ -1,155 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Documentation/generate.bat b/Documentation/generate.bat deleted file mode 100644 index 7a65350ef..000000000 --- a/Documentation/generate.bat +++ /dev/null @@ -1,20 +0,0 @@ -@echo off - -pushd %~dp0 - -SET GitBranch=%APPVEYOR_REPO_BRANCH% -SET WYAM=%USERPROFILE%\.nuget\packages\wyam\1.4.1\tools\net462\Wyam.exe -IF "%GitBranch%"=="" ( - git rev-parse HEAD > head.txt - set /p GitRevision= - -@{ - // Group by category - IList> categoryGroups = Model - .GroupBy(x => x.String(DocsKeys.Category)) - .OrderBy(x => x.Key) - .ToList(); - foreach(IGrouping categoryGroup in categoryGroups) - { - if(!string.IsNullOrWhiteSpace(categoryGroup.Key)) - { -
  • @categoryGroup.Key
  • - } - - // Iterate documents in this category - foreach(IDocument child in categoryGroup - .OrderBy(x => x.Get(DocsKeys.Order, 1000)) - .ThenBy(x => x.WithoutSettings.String(Keys.Title))) - { - object[] currentTreePath = Document.Get(Keys.TreePath); - object[] childTreePath = child.Get(Keys.TreePath); - string childTitle = child.WithoutSettings.String(Keys.Title, childTreePath.Last().ToString()); - string parentActive = currentTreePath.Take(childTreePath.Length).SequenceEqual(childTreePath) ? "active" : null; - string childSelected = parentActive != null && currentTreePath.Length == childTreePath.Length ? "selected" : null; - IReadOnlyList children = child.DocumentList(Keys.Children); - if(children != null && children.Count > 0) - { - // Parent -
  • - @childTitle -
      - @Html.Partial("Sidebar/_ChildPagesMenu", children) -
    -
  • - } - else - { - // Leaf -
  • - @if(child.ContainsKey("Todo")) - { - - @childTitle - (work in progress) - - } - else - { - @childTitle - } - -
  • - } - } - } -} \ No newline at end of file diff --git a/Documentation/input/Shared/_ChildPages.cshtml b/Documentation/input/Shared/_ChildPages.cshtml deleted file mode 100644 index db1abbcd8..000000000 --- a/Documentation/input/Shared/_ChildPages.cshtml +++ /dev/null @@ -1,63 +0,0 @@ -@{ - IReadOnlyList children = Model.DocumentList(Keys.Children); - if(children != null && children.Count > 0) - { - IList> categoryGroups = - children - .GroupBy(x => x.String(DocsKeys.Category)) - .OrderBy(x => x.Key) - .ToList(); - - foreach(IGrouping categoryGroup in categoryGroups) - { -
    -
    - - @if(!string.IsNullOrWhiteSpace(categoryGroup.Key)) - { - - - - } - - - @foreach(IDocument child in categoryGroup - .OrderBy(x => x.Get(DocsKeys.Order, 1000)) - .ThenBy(x => x.WithoutSettings.String(Keys.Title))) - { - object[] childTreePath = child.Get(Keys.TreePath); - - - - - } - -
    @categoryGroup.Key
    - - @if(child.ContainsKey("Todo")) - { - - @(child.WithoutSettings.String(Keys.Title, childTreePath.Last().ToString())) - (work in progress) - - } - else - { - - @(child.WithoutSettings.String(Keys.Title, childTreePath.Last().ToString())) - - } - - - @{ - if(child.ContainsKey(DocsKeys.Description)) - { - @(child.String(DocsKeys.Description)) - } - } -
    -
    -
    - } - } -} \ No newline at end of file diff --git a/Documentation/input/_ApiBeforeContent.cshtml b/Documentation/input/_ApiBeforeContent.cshtml deleted file mode 100644 index e69de29bb..000000000 diff --git a/Documentation/input/_ApiIndex.cshtml b/Documentation/input/_ApiIndex.cshtml deleted file mode 100644 index 027952cc9..000000000 --- a/Documentation/input/_ApiIndex.cshtml +++ /dev/null @@ -1,22 +0,0 @@ -@section Search { - @Html.Partial("_ApiSearch") -} - -@{ - Layout = "/_Layout.cshtml"; -} - -

    - Please be aware that this API documentation is generated based on the C# source code. Names of properties and methods can slightly differ when using AlphaTab from JavaScript. -

    - -@Html.Partial("Section\\_DocsTable", new DocsTable -{ - Docs = Documents.FromPipeline("Api") - .Where(x => x.String("Kind") == "Namespace") - .OrderBy(x => x.String("DisplayName")) - .ToList(), - Title = "Namespaces", - Header = "Namespace", - HasSummary = true -}) \ No newline at end of file diff --git a/Documentation/input/_Footer.cshtml b/Documentation/input/_Footer.cshtml deleted file mode 100644 index be49eed7b..000000000 --- a/Documentation/input/_Footer.cshtml +++ /dev/null @@ -1,2 +0,0 @@ -Copyright © @DateTime.Now.Year, Daniel Kuschny and Contributors, Branch: @Context.String("GitBranch", "Unknown"), -Revision: @Context.String("GitRevision", "Unknown") \ No newline at end of file diff --git a/Documentation/input/_Master.cshtml b/Documentation/input/_Master.cshtml deleted file mode 100644 index c157d1081..000000000 --- a/Documentation/input/_Master.cshtml +++ /dev/null @@ -1,192 +0,0 @@ - - - - - - - - - - - @Context.String(DocsKeys.Title, "Docs") - @ViewData[Keys.Title] - - - - - - - - - - - - - - - - - - - @Html.Partial("_Head") - - @{ - string layoutTopNav = Model.Bool(DocsKeys.NoSidebar, false) ? "layout-top-nav" : string.Empty; - string noGutter = Model.Bool(DocsKeys.NoGutter, false) ? "no-gutter" : string.Empty; - } - -
    - @{ - string withContainer = Model.Bool(DocsKeys.NoContainer, false) ? null : "with-container"; - } -
    - -
    - - - - -
    - - - - - -
    - @RenderBody() -
    - - -
    -
    - - @* - - - -
    - *@ -
    -
    -
    - @Html.Partial("_Footer") -
    -
    - - - - - - - @Html.Partial("_Bottom") - - - - \ No newline at end of file diff --git a/Documentation/input/_Navbar.cshtml b/Documentation/input/_Navbar.cshtml deleted file mode 100644 index 37c2c5f60..000000000 --- a/Documentation/input/_Navbar.cshtml +++ /dev/null @@ -1,14 +0,0 @@ -@{ - List> pages = new List> - { - Tuple.Create("Features", Context.GetLink("features")), - Tuple.Create("Examples", Context.GetLink("examples")), - Tuple.Create("AlphaTex", Context.GetLink("alphatex")), - Tuple.Create("API", Context.GetLink("api")) - }; - foreach(Tuple page in pages) - { - string active = Context.GetLink(Document).StartsWith(page.Item2) ? "active" : null; -
  • @Html.Raw(page.Item1)
  • - } -} \ No newline at end of file diff --git a/Documentation/input/alphatex/bar-meta.cshtml b/Documentation/input/alphatex/bar-meta.cshtml deleted file mode 100644 index 9e57d4ff4..000000000 --- a/Documentation/input/alphatex/bar-meta.cshtml +++ /dev/null @@ -1,94 +0,0 @@ -Title: Bar Metadata -Order: 2 -TexSample: true ---- - -

    - For bars various tags can be specified which change information like - clefs, key signatures repeats etc. They follow the format - \tag value value just like the score metadata. Be sure not to - mix the score metadata and the bar metadata of the first bar. -

    - -

    Time Signatures

    - -

    - Time signatures have the format \ts Numerator Denominator -

    - -
    -\ts 3 4 | \ts 4 4 | \ts 6 8 -
    - - -

    Repeats

    - -

    - Repeats can be started with \ro and - be closed with \rc Count. Count specifies how often - the bar range is repeated. -

    - -
    -\ro 1.3 2.3 3.3 4.3 | -5.3 6.3 7.3 8.3 | -\rc 2 1.3 2.3 3.3 4.3 | -\ro \rc 3 1.3 2.3 3.3 4.3 | - - -
    - - -

    Key Signatures

    - -

    - The key signature can be specified with \ks Value where the value is one - of the following: Cb, Gb, Db, Ab, Eb, Bb, F, C, G, D, A, E, B, F#, C# -

    - -
    -\ks Cb | \ks C | \ks C# - - -
    - - -

    Clef

    - -

    - To change the clef simply specify \clef Clef where Clef is one of the following - values: G2, Treble, F4, Bass, C3, Tenor, C4, Alto, N, Neutral -

    - -
    -\clef G2 | \clef F4 | \clef C3 | \clef C4 | \clef N | -\clef Treble | \clef Bass | \clef Tenor | \clef Alto | \clef Neutral | - -
    - - -

    Tempo

    - -

    - To change the tempo of the score you can specify \tempo BPM. - -

    - -
    -// score meta also supports \tempo, therefore we skip the score meta with a dot -. -// bars -\tempo 30 1.3 2.3 3.3 4.3 | -\tempo 80 1.3 2.3 3.3 4.3 | -
    - diff --git a/Documentation/input/alphatex/beat-effects.cshtml b/Documentation/input/alphatex/beat-effects.cshtml deleted file mode 100644 index 2aabb8f0f..000000000 --- a/Documentation/input/alphatex/beat-effects.cshtml +++ /dev/null @@ -1,69 +0,0 @@ -Title: Beat Effects -Order: 3 -TexSample: true ---- - -

    - There are various effects that can be applied to a beat. All beat - effects are specified in braces after the beat. - Beat{Effects} -

    - -

    Simple Effects

    - -

    - Please find the list of support effects in the example below. -

    - -
    -// fade in -3.3{f} -// vibrato -3.3{v} -// slap -3.3{s} -// pop -3.3{p} -| -// dotted -3.3{d} -// double dotted -3.3{dd} -// pick stroke (su => up, sd => down) -3.3{su} -3.3{sd} -| -// grace (on beat) -3.3{gr ob} -3.3 -// grace (before beat) -3.3{gr} -3.3 -| -// Tuplets (supported variants: 3,5,6,7,9,10,11,12) -3.3{tu 3} 3.3{tu 3} 3.3{tu 3} -3.3{tu 5} 3.3{tu 5} 3.3{tu 5} 3.3{tu 5} 3.3{tu 5} -| -// tremolo picking (`tp duration` where duration can be 8,16 or 32) -3.3{tp 8} 3.3{tp 16} 3.3{tp 32} -
    - - -

    Tremolo / Whammy Bar

    - -

    - The tremolo/whammy bar effect is a bit more complex than the others. - You can specify a number of values which are evenly distributed over the - time when the note is played. The values indicate the number of quarter notes - increased or decreased on playing. tb (value1 value2 ...) -

    - -
    -3.3.1{tb (0 4 0 8)} | -3.3.1{tb (0 -4 0 -8)} | -
    - diff --git a/Documentation/input/alphatex/exporter.cshtml b/Documentation/input/alphatex/exporter.cshtml deleted file mode 100644 index 1b3bbf54d..000000000 --- a/Documentation/input/alphatex/exporter.cshtml +++ /dev/null @@ -1,74 +0,0 @@ -Title: Exporter -Order: 5 -TexSample: true ---- - -

    - AlphaTab can export an already loaded track as alphaTex. -

    - -
    - Open a Guitar Pro file by clicking here to export the - first track as alphaTex. - -
    - -
    -
    function toTex(track) {
    -var exporter = new AlphaTab.Exporter.AlphaTexExporter();
    -exporter.Export(track);
    -return exporter.ToTex(); 
    -}
    -
    - -

    Exported AlphaTex

    - -
    - -Open a file to see the generated alphaTex here. - - - - - -
    diff --git a/Documentation/input/alphatex/index.cshtml b/Documentation/input/alphatex/index.cshtml deleted file mode 100644 index 8632b1223..000000000 --- a/Documentation/input/alphatex/index.cshtml +++ /dev/null @@ -1,40 +0,0 @@ -Title: AlphaTex -TexSample: true ---- - -

    Introduction

    - -

    - In this section you find all details about how to write music notation using AlphaTex. - AlphaTex is a text format for writing music notation for AlphaTab. AlphaTex loading - can be enabled by specifying data-tex="true" on the container elmement. - AlphaTab will load the tex code from the element contents and parse it. - AlphaTex currently only allows writing of a single track. -

    - -

    Showcase

    - -

    - Here is an example score fully rendered using alphaTex. -

    - -
    -\title "Canon Rock" -\subtitle "JerryC" -\tempo 90 -. -:2 19.2{v f} 17.2{v f} | -15.2{v f} 14.2{v f}| -12.2{v f} 10.2{v f}| -12.2{v f} 14.2{v f}.4 :8 15.2 17.2 | -14.1.2 :8 17.2 15.1 14.1{h} 17.2 | -15.2{v d}.4 :16 17.2{h} 15.2 :8 14.2 14.1 17.1{b(0 4 4 0)}.4 | -15.1.8 :16 14.1{tu 3} 15.1{tu 3} 14.1{tu 3} :8 17.2 15.1 14.1 :16 12.1{tu 3} 14.1{tu 3} 12.1{tu 3} :8 15.2 14.2 | -12.2 14.3 12.3 15.2 :32 14.2{h} 15.2{h} 14.2{h} 15.2{h}14.2{h} 15.2{h}14.2{h} 15.2{h}14.2{h} 15.2{h}14.2{h} 15.2{h}14.2{h} 15.2{h}14.2{h} 15.2{h} - -
    - diff --git a/Documentation/input/alphatex/metadata.cshtml b/Documentation/input/alphatex/metadata.cshtml deleted file mode 100644 index a26345b31..000000000 --- a/Documentation/input/alphatex/metadata.cshtml +++ /dev/null @@ -1,34 +0,0 @@ -Title: Metadata -Order: 0 -TexSample: true ---- - -

    - Metadata is specified using \tagname value value at the beginning of the score. - It can be used to specify global information like title, tempo or track tuning. - Metadata is optional, but if specified, it must be followed by a dot do indicate the end - of the metadata. -

    - -
    -// Song information -\title "Song Title" -\subtitle Subtitle -\artist Artist -\album 'My Album' -\words Daniel -\music alphaTab -\copyright Daniel -// Tempo in BPM -\tempo 200 -// Instrument as General Midi number or name -\instrument 30 -// Capo fret -\capo 2 -// String tuning starting at the most bottom string -\tuning e5 b4 g4 d4 a3 e3 -. -
    - diff --git a/Documentation/input/alphatex/note-effects.cshtml b/Documentation/input/alphatex/note-effects.cshtml deleted file mode 100644 index 2f0726e51..000000000 --- a/Documentation/input/alphatex/note-effects.cshtml +++ /dev/null @@ -1,64 +0,0 @@ -Title: Note Effects -Order: 4 -TexSample: true ---- - -

    - Just like beat effects, there are also effects for single notes like harmonics, - bends, accentuations etc. The syntax is - Fret.String{NoteEffects} -

    - -

    Simple Effects

    - -

    - Please find the list of support effects in the example below. -

    - -
    - // Harmonics (natural, articifial, tapped, pinch and semi) - :8 3.3{nh} 3.3{ah} 3.3{th} 3.3{ph} 3.3{sh} | - // trill (tr fret duration) - :4 3.3{tr 4 16} 3.3{tr 5 32} 3.3{tr 6 64} - // vibrato - 3.3{v} | - // slides (legato and shift) - 3.3{sl} 3.3{ss} - // hammer on/pull offs - 3.3{h} 4.3{h} | - // ghost notes - 3.3{g} - // accentuations (normal and heavy) - 3.3{ac} 3.3{hac} - // staccato - 3.3{st} | - // palm mute - 3.3{pm} 3.3{pm} 3.3{pm} 3.3{pm} | - // let ring - 3.3{lr} 3.3{lr} 3.3{lr} 3.3{lr} | - // dead notes - 3.3{x} x.3 | - // fingering (left and right; 1 => Thumb, 2 => Index, 3 => Middle, 4 => Annual, 5 => Little) - :8 3.3{lf 1} 3.3{lf 2} 3.3{lf 3} 3.3{lf 4} 3.3{lf 5} (2.2{lf 4} 2.3{lf 3} 2.4{lf 2}) | - :8 3.3{rf 1} 3.3{rf 2} 3.3{rf 3} 3.3{rf 4} 3.3{lf 5} | -
    - - -

    Bends

    - -

    - The bend effect works like the tremolo/whammy bar effect. - You can specify a number of values which are evenly distributed over the - time when the note is played. The values indicate the number of quarter notes - increased bend up upon playing. b (value1 value2 ...) -

    - -
    -3.3{b (0 4 )} | -3.3{b (0 4 0 8)} | -
    - diff --git a/Documentation/input/alphatex/notes.cshtml b/Documentation/input/alphatex/notes.cshtml deleted file mode 100644 index 2a91e0154..000000000 --- a/Documentation/input/alphatex/notes.cshtml +++ /dev/null @@ -1,76 +0,0 @@ -Title: Notes -Order: 1 -TexSample: true ---- - -

    - The following examples show how to general notes. Multiple bars are - separated by |. -

    - -

    Single notes and rests

    - -

    - Notes follow the format fret.string.duration. For rests - the fret is simply replaced with r. The duration is specified - as a number where 1 represents a full note, 2 a half note and so on. -

    - -
    -0.6.2 1.5.4 3.4.4 | -5.3.8 5.3.8 5.3.8 5.3.8 r.2 -
    - - -

    Chords

    - -

    - To specify multiple notes on a beat, group them in parenthesis. The full format is - (fret.string fret.string ...).duration -

    - -
    -(0.3 0.4).4 (3.3 3.4).4 (5.3 5.4).4 r.8 (0.3 0.4).8 | -r.8 (3.3 3.4).8 r.8 (6.3 6.4).8 (5.3 5.4).4 r.4 | -(0.3 0.4).4 (3.3 3.4).4 (5.3 5.4).4 r.8 (3.3 3.4).8 | -r.8 (0.3 0.4).8 - -
    - - -

    Duration Ranges

    - -

    - To make it simpler to write notes, you can apply the same duration to - multiple notes. Start a new duration range with :duration. - The following notes will all have this duration. AlphaTex also remembers - the last explictely set duration and will take use it in case of a missing duration. -

    - -
    -// explicit ranges via :duration -:4 2.3 3.3 :8 3.3 4.3 3.3 4.3 | -// implicit ranges via beat duration -2.3.4 3.3 3.3.8 4.3 3.3 4.3 -
    - - - -

    Repeat beats

    - -

    - You can specify a multiplier after a beat to add the same beat multiple times to the score. -

    - -
    -3.3*4 | 4.3*4 -
    - \ No newline at end of file diff --git a/Documentation/input/assets/css/override.css b/Documentation/input/assets/css/override.css deleted file mode 100644 index e49e96b29..000000000 --- a/Documentation/input/assets/css/override.css +++ /dev/null @@ -1,96 +0,0 @@ -.main-header { - background-image: linear-gradient(to bottom,#fff 0,#f8f8f8 100%); - background-repeat: repeat-x; - box-shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 5px rgba(0,0,0,.075); -} - -.main-header .logo, .main-header .navbar { - background: none !important; -} - -.main-header .logo img { - height: 30px; -} - -.main-header .navbar .nav > li > a { - color: #777; -} -.main-header .navbar .nav > li > a:hover { - color: #333; - background-color: transparent; -} - - .main-header .navbar .nav > .active > a, - .main-header .navbar .nav > .open > a { - color: #555; - background-color: #e7e7e7; - background-image: linear-gradient(to bottom,#dbdbdb 0,#e2e2e2 100%); - background-repeat: repeat-x; - box-shadow: inset 0 3px 9px rgba(0,0,0,.075); - } - - .main-header .navbar .nav > .active > a:hover, - .main-header .navbar .nav > .open > a:hover { - color: #555; - background-color: #e7e7e7; - } - -.content-header h1 { - color: #426d9d; -} - -.menu-container { - column-count: 2; -} - -.tab-content { - border-left: 1px solid #ddd; - border-right: 1px solid #ddd; - border-bottom: 1px solid #ddd; - padding: 10px; -} - -.alphaTabSurface { - box-shadow: 0 0 10px rgba(0,0,0,0.3); - background: #FFF; - border-radius: 3px; -} - -.example-source, .description { - margin-bottom: 10px; -} - -.infobar { - position: static; - right: auto !important; - top: auto !important; - width: auto !important; - padding: 30px 12px 0 12px !important; -} - -.infobar.affix { - position: static !important; - left: auto !important; - margin-left: 0 !important; -} - -.sidebar { - padding-top: 0 !important; -} - -@media only screen and (max-width: 1670px) and (min-width: 1250px) { - .wrapper.with-container { - max-width: none; - left: 0; - } -} - -.wip { - color: #999; - font-size: 12px; - font-weight: normal; -} - -.menu-block td { - width: 100%; -} \ No newline at end of file diff --git a/Documentation/input/assets/files/MultiTrack.gp5 b/Documentation/input/assets/files/MultiTrack.gp5 deleted file mode 100644 index e3f1be755..000000000 Binary files a/Documentation/input/assets/files/MultiTrack.gp5 and /dev/null differ diff --git a/Documentation/input/assets/files/features/Bends.gp5 b/Documentation/input/assets/files/features/Bends.gp5 deleted file mode 100644 index d04cf6ba7..000000000 Binary files a/Documentation/input/assets/files/features/Bends.gp5 and /dev/null differ diff --git a/Documentation/input/assets/files/features/BendsAdvanced.gp b/Documentation/input/assets/files/features/BendsAdvanced.gp deleted file mode 100644 index ca20961e6..000000000 Binary files a/Documentation/input/assets/files/features/BendsAdvanced.gp and /dev/null differ diff --git a/Documentation/input/assets/files/features/BendsAdvanced.gpx b/Documentation/input/assets/files/features/BendsAdvanced.gpx deleted file mode 100644 index be8c961e7..000000000 Binary files a/Documentation/input/assets/files/features/BendsAdvanced.gpx and /dev/null differ diff --git a/Documentation/input/assets/files/features/Fingering.gp5 b/Documentation/input/assets/files/features/Fingering.gp5 deleted file mode 100644 index 839cc2388..000000000 Binary files a/Documentation/input/assets/files/features/Fingering.gp5 and /dev/null differ diff --git a/Documentation/input/assets/files/features/TremoloBar.gp5 b/Documentation/input/assets/files/features/TremoloBar.gp5 deleted file mode 100644 index 4dec14cd4..000000000 Binary files a/Documentation/input/assets/files/features/TremoloBar.gp5 and /dev/null differ diff --git a/Documentation/input/assets/files/features/TripletFeel.gp5 b/Documentation/input/assets/files/features/TripletFeel.gp5 deleted file mode 100644 index a6df1821e..000000000 Binary files a/Documentation/input/assets/files/features/TripletFeel.gp5 and /dev/null differ diff --git a/Documentation/input/assets/files/player.html b/Documentation/input/assets/files/player.html deleted file mode 100644 index da600578a..000000000 --- a/Documentation/input/assets/files/player.html +++ /dev/null @@ -1,348 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - - - - - \ No newline at end of file diff --git a/Documentation/input/assets/img/alphaTab.png b/Documentation/input/assets/img/alphaTab.png deleted file mode 100644 index 98400bde6..000000000 Binary files a/Documentation/input/assets/img/alphaTab.png and /dev/null differ diff --git a/Documentation/input/assets/js/examples.js b/Documentation/input/assets/js/examples.js deleted file mode 100644 index 749915309..000000000 --- a/Documentation/input/assets/js/examples.js +++ /dev/null @@ -1,81 +0,0 @@ -Prism.languages.alphaTex = { - 'comment': [ - { - pattern: /(^|[^\\])\/\*[\w\W]*?\*\//, - lookbehind: true - }, - { - pattern: /(^|[^\\:])\/\/.*/, - lookbehind: true - } - ], - - 'number': /\b-?(?:0x[\da-f]+|\d*\.?\d+(?:e[+-]?\d+)?)\b/i, - - 'string': { - pattern: /(["'])(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/, - greedy: true - }, - - 'keyword': /\\([^ ]+)/, - 'constant': /([cdefgabh][0-9])/, -}; - -$('.example').each(function() { - var html = $(this).find('.html').html(); - var js = $(this).find('.js').html(); - - var wrap = $('
    '); - var ul = $('').appendTo(wrap); - var tabContent = $('
    ').appendTo(wrap); - - $('
  • HTML
  • ').appendTo(ul); - $('
    ') - .append($('
    ').append($('').text(html)))
    -		.appendTo(tabContent);
    -	
    -	$('
  • JavaScript
  • ').appendTo(ul); - $('
    ') - .append($('
    ').append($('').text(js)))
    -		.appendTo(tabContent);
    -		
    -	$(this).prepend(wrap);
    -	
    -	ul.on('click', 'a', function(e){
    -		e.preventDefault();
    -		
    -		var parent = $(this).closest('.example-source'),
    -			li = $(this).closest('li'),
    -			index = li.index(),
    -			tabs = parent.find('.tab-content')
    -		;
    -		
    -		parent.find('li.active').removeClass('active');
    -		li.addClass('active');
    -		tabs.find('.tab-pane.active').removeClass('active');
    -		tabs.find('.tab-pane').eq(index).addClass('active');
    -		
    -	});
    -});
    -
    -function prepareTex(tex) {
    -   return tex.replace(/[\t ]*(.*)[ \t]*/g, "$1");     
    -}
    -if(window.texSample) {
    -	$('[data-tex]').each(function() {
    -		var code = $('')
    -			.text(prepareTex($(this).text()))   
    -
    -		var pre = $('
    ').append(code);    
    -		$(this).after($('
    ').append(pre)); - }); -} - -Prism.highlightAll(); - -$('script[type="text/x-alphatab"]').each(function() { - var code = $(this).html(); - var newScript = document.createElement('script'); - newScript.innerHTML = code; - $(this).replaceWith(newScript); -}); diff --git a/Documentation/input/docs/command-line.md b/Documentation/input/docs/command-line.md deleted file mode 100644 index 3a9a56c27..000000000 --- a/Documentation/input/docs/command-line.md +++ /dev/null @@ -1,3 +0,0 @@ -Description: How to use the command line. ---- -Here are some instructions on how to use the command line. \ No newline at end of file diff --git a/Documentation/input/docs/usage.md b/Documentation/input/docs/usage.md deleted file mode 100644 index 127a961b1..000000000 --- a/Documentation/input/docs/usage.md +++ /dev/null @@ -1,3 +0,0 @@ -Description: Library usage instructions. ---- -To use this library, take these steps... \ No newline at end of file diff --git a/Documentation/input/examples/api/errors.cshtml b/Documentation/input/examples/api/errors.cshtml deleted file mode 100644 index 12486942f..000000000 --- a/Documentation/input/examples/api/errors.cshtml +++ /dev/null @@ -1,6 +0,0 @@ -Title: Handling errors -Order: 4 -Todo: true ---- - -

    This example is not yet available.

    \ No newline at end of file diff --git a/Documentation/input/examples/api/index.cshtml b/Documentation/input/examples/api/index.cshtml deleted file mode 100644 index 3d16a464b..000000000 --- a/Documentation/input/examples/api/index.cshtml +++ /dev/null @@ -1,8 +0,0 @@ -Title: API -Description: This section contains examples on how to perform certain tasks using the raw API. -Order: 5 ---- - -

    @Html.Raw(Model.String(DocsKeys.Description))

    - -@Html.Partial("_ChildPages") \ No newline at end of file diff --git a/Documentation/input/examples/api/layout.cshtml b/Documentation/input/examples/api/layout.cshtml deleted file mode 100644 index ce1933391..000000000 --- a/Documentation/input/examples/api/layout.cshtml +++ /dev/null @@ -1,6 +0,0 @@ -Title: Change layout -Order: 3 -Todo: true ---- - -

    This example is not yet available.

    \ No newline at end of file diff --git a/Documentation/input/examples/api/lazy.cshtml b/Documentation/input/examples/api/lazy.cshtml deleted file mode 100644 index 9e6ac29fb..000000000 --- a/Documentation/input/examples/api/lazy.cshtml +++ /dev/null @@ -1,6 +0,0 @@ -Title: Disable lazy loading -Order: 6 -Todo: true ---- - -

    This example is not yet available.

    \ No newline at end of file diff --git a/Documentation/input/examples/api/load.cshtml b/Documentation/input/examples/api/load.cshtml deleted file mode 100644 index 1cf10ce8f..000000000 --- a/Documentation/input/examples/api/load.cshtml +++ /dev/null @@ -1,6 +0,0 @@ -Title: Change file -Order: 1 -Todo: true ---- - -

    This example is not yet available.

    \ No newline at end of file diff --git a/Documentation/input/examples/api/loadevent.cshtml b/Documentation/input/examples/api/loadevent.cshtml deleted file mode 100644 index fe839ca7e..000000000 --- a/Documentation/input/examples/api/loadevent.cshtml +++ /dev/null @@ -1,6 +0,0 @@ -Title: Load event -Order: 8 -Todo: true ---- - -

    This example is not yet available.

    \ No newline at end of file diff --git a/Documentation/input/examples/api/print.cshtml b/Documentation/input/examples/api/print.cshtml deleted file mode 100644 index 635fa6db8..000000000 --- a/Documentation/input/examples/api/print.cshtml +++ /dev/null @@ -1,6 +0,0 @@ -Title: Print dialog -Order: 5 -Todo: true ---- - -

    This example is not yet available.

    \ No newline at end of file diff --git a/Documentation/input/examples/api/resize.cshtml b/Documentation/input/examples/api/resize.cshtml deleted file mode 100644 index b0332e0c5..000000000 --- a/Documentation/input/examples/api/resize.cshtml +++ /dev/null @@ -1,6 +0,0 @@ -Title: Handle resize -Order: 7 -Todo: true ---- - -

    This example is not yet available.

    \ No newline at end of file diff --git a/Documentation/input/examples/api/scoreloader.cshtml b/Documentation/input/examples/api/scoreloader.cshtml deleted file mode 100644 index d9918f5b4..000000000 --- a/Documentation/input/examples/api/scoreloader.cshtml +++ /dev/null @@ -1,6 +0,0 @@ -Title: Load score manually -Order: 2 -Todo: true ---- - -

    This example is not yet available.

    \ No newline at end of file diff --git a/Documentation/input/examples/api/settings.cshtml b/Documentation/input/examples/api/settings.cshtml deleted file mode 100644 index cabd3e146..000000000 --- a/Documentation/input/examples/api/settings.cshtml +++ /dev/null @@ -1,6 +0,0 @@ -Title: List of all settings and methods -Order: 0 -Todo: true ---- - -

    This example is not yet available.

    \ No newline at end of file diff --git a/Documentation/input/examples/general/advanced.cshtml b/Documentation/input/examples/general/advanced.cshtml deleted file mode 100644 index b03308b18..000000000 --- a/Documentation/input/examples/general/advanced.cshtml +++ /dev/null @@ -1,11 +0,0 @@ -Title: Advanced Usage -Order: 5 ---- - -
    - The player demo available here shows - how the individual features of alphaTab can be combined to create a full featured web player. -
    - - \ No newline at end of file diff --git a/Documentation/input/examples/general/alphaTex.cshtml b/Documentation/input/examples/general/alphaTex.cshtml deleted file mode 100644 index a7afebef8..000000000 --- a/Documentation/input/examples/general/alphaTex.cshtml +++ /dev/null @@ -1,33 +0,0 @@ -Title: Use alphaTex -Order: 2 ---- - -
    - AlphaTex is a text based language for describing music notations. A full documentation - on the syntax can be found here. - To use AlphaTex put the code into the div and enable the usage via the data attribute data-tex="true" -
    - -
    -
    -
    - \title "Canon Rock" - \subtitle "JerryC" - \tempo 90 - . - :2 19.2{v f} 17.2{v f} | - 15.2{v f} 14.2{v f}| - 12.2{v f} 10.2{v f}| - 12.2{v f} 14.2{v f}.4 :8 15.2 17.2 | - 14.1.2 :8 17.2 15.1 14.1{h} 17.2 | - 15.2{v d}.4 :16 17.2{h} 15.2 :8 14.2 14.1 17.1{b(0 4 4 0)}.4 | - 15.1.8 :16 14.1{tu 3} 15.1{tu 3} 14.1{tu 3} :8 17.2 15.1 14.1 :16 12.1{tu 3} 14.1{tu 3} 12.1{tu 3} :8 15.2 14.2 | - 12.2 14.3 12.3 15.2 :32 14.2{h} 15.2{h} 14.2{h} 15.2{h}14.2{h} 15.2{h}14.2{h} 15.2{h}14.2{h} 15.2{h}14.2{h} 15.2{h}14.2{h} 15.2{h}14.2{h} 15.2{h} -
    -
    -
    - -
    -
    diff --git a/Documentation/input/examples/general/html5.cshtml b/Documentation/input/examples/general/html5.cshtml deleted file mode 100644 index 0b87d5b2d..000000000 --- a/Documentation/input/examples/general/html5.cshtml +++ /dev/null @@ -1,39 +0,0 @@ -Title: HTML5 canvas rendering -Order: 3 ---- - -
    - The music notation is rendered as SVG by default. To use HTML5 canvas as render - engine specify the option engine: 'html5' when initializing or by using data-engine="html5". -
    - -
    - -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    \ No newline at end of file diff --git a/Documentation/input/examples/general/index.cshtml b/Documentation/input/examples/general/index.cshtml deleted file mode 100644 index e5e0f765e..000000000 --- a/Documentation/input/examples/general/index.cshtml +++ /dev/null @@ -1,8 +0,0 @@ -Title: General -Description: This section contains examples on how to get started with alphaTab. -Order: 0 ---- - -

    @Html.Raw(Model.String(DocsKeys.Description))

    - -@Html.Partial("_ChildPages") \ No newline at end of file diff --git a/Documentation/input/examples/general/simple.cshtml b/Documentation/input/examples/general/simple.cshtml deleted file mode 100644 index 08d735914..000000000 --- a/Documentation/input/examples/general/simple.cshtml +++ /dev/null @@ -1,42 +0,0 @@ -Title: Simple initialization -Order: 0 ---- - -
    - AlphaTab can be initialized on any div element. There are 2 main options that need to be specified: - The file to load, and which tracks to render. - The file can be specified via file option or via data-file attribute - The tracks can be specified via tracks option or via data-tracks attribute. -
    - -
    - -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    \ No newline at end of file diff --git a/Documentation/input/examples/general/tracks.cshtml b/Documentation/input/examples/general/tracks.cshtml deleted file mode 100644 index b5c9278e4..000000000 --- a/Documentation/input/examples/general/tracks.cshtml +++ /dev/null @@ -1,37 +0,0 @@ -Title: Configuring tracks -Order: 1 ---- - -
    - The displayed tracks can be controlled via tracks option or data-tracks. - The value is a single index or an array of indexes of the tracks within the file that is loaded. -
    - -
    - -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    \ No newline at end of file diff --git a/Documentation/input/examples/index.cshtml b/Documentation/input/examples/index.cshtml deleted file mode 100644 index 677f31367..000000000 --- a/Documentation/input/examples/index.cshtml +++ /dev/null @@ -1,23 +0,0 @@ -Title: Examples ---- - -

    - We have prepared a huge list of examples to show different alphaTab features by code. - Those examples are the easiest way to understand what alphaTab is capable of and how to use it. - The samples shown here are focusing on the usage on websites but many of the settings shown in this samples are also valid for the .net version of alphaTab. -

    - - \ No newline at end of file diff --git a/Documentation/input/examples/layouts/horizontal.cshtml b/Documentation/input/examples/layouts/horizontal.cshtml deleted file mode 100644 index 8f5d98650..000000000 --- a/Documentation/input/examples/layouts/horizontal.cshtml +++ /dev/null @@ -1,41 +0,0 @@ -Title: Horizontal layout -Order: 1 ---- - -
    - The horizontal layout be enabled via layout: 'horizontal' or data-layout="horizontal". - The whole music notation is arranged as a single line. This layout is useful for small screen sizes. -
    - - -
    - -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    \ No newline at end of file diff --git a/Documentation/input/examples/layouts/index.cshtml b/Documentation/input/examples/layouts/index.cshtml deleted file mode 100644 index ab198c43b..000000000 --- a/Documentation/input/examples/layouts/index.cshtml +++ /dev/null @@ -1,8 +0,0 @@ -Title: Layouts -Description: This section contains examples on how to use the different layouts. -Order: 1 ---- - -

    @Html.Raw(Model.String(DocsKeys.Description))

    - -@Html.Partial("_ChildPages") \ No newline at end of file diff --git a/Documentation/input/examples/layouts/page.cshtml b/Documentation/input/examples/layouts/page.cshtml deleted file mode 100644 index 13e4115ed..000000000 --- a/Documentation/input/examples/layouts/page.cshtml +++ /dev/null @@ -1,41 +0,0 @@ -Title: Page layout -Order: 0 ---- - -
    - The page layout is the default layout of alphaTab. The bars are aligned - in a page like manner but without separation into individual pages. - The layout can explicitely be set via layout: 'page' or data-layout="page" -
    - -
    - -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    \ No newline at end of file diff --git a/Documentation/input/examples/player/cursorstyle.cshtml b/Documentation/input/examples/player/cursorstyle.cshtml deleted file mode 100644 index a2d0ebd9d..000000000 --- a/Documentation/input/examples/player/cursorstyle.cshtml +++ /dev/null @@ -1,6 +0,0 @@ -Title: Adjust cursor style -Order: 5 -Todo: true ---- - -

    This example is not yet available.

    \ No newline at end of file diff --git a/Documentation/input/examples/player/index.cshtml b/Documentation/input/examples/player/index.cshtml deleted file mode 100644 index 67f398a8f..000000000 --- a/Documentation/input/examples/player/index.cshtml +++ /dev/null @@ -1,8 +0,0 @@ -Title: Player -Description: This section contains examples on how to setup the audio playback. -Order: 4 ---- - -

    @Html.Raw(Model.String(DocsKeys.Description))

    - -@Html.Partial("_ChildPages") \ No newline at end of file diff --git a/Documentation/input/examples/player/init.cshtml b/Documentation/input/examples/player/init.cshtml deleted file mode 100644 index 189fca0a1..000000000 --- a/Documentation/input/examples/player/init.cshtml +++ /dev/null @@ -1,6 +0,0 @@ -Title: Basic initialization -Order: 0 -Todo: true ---- - -

    This example is not yet available.

    \ No newline at end of file diff --git a/Documentation/input/examples/player/loop.cshtml b/Documentation/input/examples/player/loop.cshtml deleted file mode 100644 index 8b400193b..000000000 --- a/Documentation/input/examples/player/loop.cshtml +++ /dev/null @@ -1,6 +0,0 @@ -Title: Enable auto looping -Order: 13 -Todo: true ---- - -

    This example is not yet available.

    \ No newline at end of file diff --git a/Documentation/input/examples/player/metronome.cshtml b/Documentation/input/examples/player/metronome.cshtml deleted file mode 100644 index c649de824..000000000 --- a/Documentation/input/examples/player/metronome.cshtml +++ /dev/null @@ -1,6 +0,0 @@ -Title: Metronome -Order: 9 -Todo: true ---- - -

    This example is not yet available.

    \ No newline at end of file diff --git a/Documentation/input/examples/player/mutesolo.cshtml b/Documentation/input/examples/player/mutesolo.cshtml deleted file mode 100644 index 903219e6b..000000000 --- a/Documentation/input/examples/player/mutesolo.cshtml +++ /dev/null @@ -1,6 +0,0 @@ -Title: Mute/Solo individual tracks -Order: 1 -Todo: true ---- - -

    This example is not yet available.

    \ No newline at end of file diff --git a/Documentation/input/examples/player/position.cshtml b/Documentation/input/examples/player/position.cshtml deleted file mode 100644 index 19cff905b..000000000 --- a/Documentation/input/examples/player/position.cshtml +++ /dev/null @@ -1,6 +0,0 @@ -Title: Handle position change -Order: 7 -Todo: true ---- - -

    This example is not yet available.

    \ No newline at end of file diff --git a/Documentation/input/examples/player/range.cshtml b/Documentation/input/examples/player/range.cshtml deleted file mode 100644 index 6a1f48bc1..000000000 --- a/Documentation/input/examples/player/range.cshtml +++ /dev/null @@ -1,6 +0,0 @@ -Title: Control playback range -Order: 12 -Todo: true ---- - -

    This example is not yet available.

    \ No newline at end of file diff --git a/Documentation/input/examples/player/scrolldirection.cshtml b/Documentation/input/examples/player/scrolldirection.cshtml deleted file mode 100644 index 5e93f7e3a..000000000 --- a/Documentation/input/examples/player/scrolldirection.cshtml +++ /dev/null @@ -1,6 +0,0 @@ -Title: Change scroll direction -Order: 2 -Todo: true ---- - -

    This example is not yet available.

    \ No newline at end of file diff --git a/Documentation/input/examples/player/scrollelement.cshtml b/Documentation/input/examples/player/scrollelement.cshtml deleted file mode 100644 index 18551c7a2..000000000 --- a/Documentation/input/examples/player/scrollelement.cshtml +++ /dev/null @@ -1,6 +0,0 @@ -Title: Change scroll element -Order: 3 -Todo: true ---- - -

    This example is not yet available.

    \ No newline at end of file diff --git a/Documentation/input/examples/player/scrolloffset.cshtml b/Documentation/input/examples/player/scrolloffset.cshtml deleted file mode 100644 index 2798c3e39..000000000 --- a/Documentation/input/examples/player/scrolloffset.cshtml +++ /dev/null @@ -1,6 +0,0 @@ -Title: Adjust scroll offset -Order: 4 -Todo: true ---- - -

    This example is not yet available.

    \ No newline at end of file diff --git a/Documentation/input/examples/player/seek.cshtml b/Documentation/input/examples/player/seek.cshtml deleted file mode 100644 index 65fb296f4..000000000 --- a/Documentation/input/examples/player/seek.cshtml +++ /dev/null @@ -1,6 +0,0 @@ -Title: Seeking -Order: 6 -Todo: true ---- - -

    This example is not yet available.

    \ No newline at end of file diff --git a/Documentation/input/examples/player/speed.cshtml b/Documentation/input/examples/player/speed.cshtml deleted file mode 100644 index 41a5abbec..000000000 --- a/Documentation/input/examples/player/speed.cshtml +++ /dev/null @@ -1,6 +0,0 @@ -Title: Control playback speed -Order: 11 -Todo: true ---- - -

    This example is not yet available.

    \ No newline at end of file diff --git a/Documentation/input/examples/player/state.cshtml b/Documentation/input/examples/player/state.cshtml deleted file mode 100644 index 69ebe82fa..000000000 --- a/Documentation/input/examples/player/state.cshtml +++ /dev/null @@ -1,6 +0,0 @@ -Title: Handle state change -Order: 8 -Todo: true ---- - -

    This example is not yet available.

    \ No newline at end of file diff --git a/Documentation/input/examples/player/volume.cshtml b/Documentation/input/examples/player/volume.cshtml deleted file mode 100644 index 346e91e8f..000000000 --- a/Documentation/input/examples/player/volume.cshtml +++ /dev/null @@ -1,6 +0,0 @@ -Title: Control audio volume -Order: 10 -Todo: true ---- - -

    This example is not yet available.

    \ No newline at end of file diff --git a/Documentation/input/examples/render-customization/hideinfo.cshtml b/Documentation/input/examples/render-customization/hideinfo.cshtml deleted file mode 100644 index ec629ebee..000000000 --- a/Documentation/input/examples/render-customization/hideinfo.cshtml +++ /dev/null @@ -1,44 +0,0 @@ -Title: Hide song info -Order: 3 ---- - -
    - If you want to hide the song information like title or artist, the layout option hideInfo or data attribute data-layout-hide-info can be set. -
    - -
    - -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    \ No newline at end of file diff --git a/Documentation/input/examples/render-customization/hidenames.cshtml b/Documentation/input/examples/render-customization/hidenames.cshtml deleted file mode 100644 index ebb5f0fa4..000000000 --- a/Documentation/input/examples/render-customization/hidenames.cshtml +++ /dev/null @@ -1,43 +0,0 @@ -Title: Hide track names -Order: 5 ---- -
    - The track names are shown by default. To hide it the layout option hideTrackNames or data attribute data-layout-hide-track-names can be set. -
    - -
    - -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    \ No newline at end of file diff --git a/Documentation/input/examples/render-customization/hidetuning.cshtml b/Documentation/input/examples/render-customization/hidetuning.cshtml deleted file mode 100644 index 8c82d7be8..000000000 --- a/Documentation/input/examples/render-customization/hidetuning.cshtml +++ /dev/null @@ -1,44 +0,0 @@ -Title: Hide tuning -Order: 4 ---- - -
    - The guitar tuning is shown by default. To hide it the layout option hideTuning or data attribute data-layout-hide-tuning can be set. -
    - -
    - -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    \ No newline at end of file diff --git a/Documentation/input/examples/render-customization/index.cshtml b/Documentation/input/examples/render-customization/index.cshtml deleted file mode 100644 index c037f0bc2..000000000 --- a/Documentation/input/examples/render-customization/index.cshtml +++ /dev/null @@ -1,8 +0,0 @@ -Title: Render Customization -Description: This section contains examples on how to adjust how elements are layouted and rendered. -Order: 3 ---- - -

    @Html.Raw(Model.String(DocsKeys.Description))

    - -@Html.Partial("_ChildPages") \ No newline at end of file diff --git a/Documentation/input/examples/render-customization/pianofingering.cshtml b/Documentation/input/examples/render-customization/pianofingering.cshtml deleted file mode 100644 index 80575a7b9..000000000 --- a/Documentation/input/examples/render-customization/pianofingering.cshtml +++ /dev/null @@ -1,39 +0,0 @@ -Title: Force piano fingering -Order: 7 ---- - -
    - For piano tracks the fingering is notated with the numbers 1-5 whereas for stringed instruments they are annotated with p,i,m,a,c and T,1,2,3,4. By setting the forcePianoFingering, always the piano notation is used. -
    - -
    - -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    \ No newline at end of file diff --git a/Documentation/input/examples/render-customization/restrictbars.cshtml b/Documentation/input/examples/render-customization/restrictbars.cshtml deleted file mode 100644 index 3dd0673bc..000000000 --- a/Documentation/input/examples/render-customization/restrictbars.cshtml +++ /dev/null @@ -1,47 +0,0 @@ -Title: Restricting bars -Order: 2 ---- - -
    - If you only want to render only a dedicated range of bars you can specify them as - layout settings. The layout settings start and count define the range - and can be specified also as data attribute. -
    - -
    - -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    \ No newline at end of file diff --git a/Documentation/input/examples/render-customization/scale.cshtml b/Documentation/input/examples/render-customization/scale.cshtml deleted file mode 100644 index 23ffd7b5f..000000000 --- a/Documentation/input/examples/render-customization/scale.cshtml +++ /dev/null @@ -1,39 +0,0 @@ -Title: Scale tablature -Order: 0 ---- - -
    - The scale of the rendered music notation can be adjusted via the following options: scale: 0.8 / data-scale="0.8". -
    - -
    - -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    \ No newline at end of file diff --git a/Documentation/input/examples/render-customization/size.cshtml b/Documentation/input/examples/render-customization/size.cshtml deleted file mode 100644 index c67882b7d..000000000 --- a/Documentation/input/examples/render-customization/size.cshtml +++ /dev/null @@ -1,40 +0,0 @@ -Title: Change render size -Order: 1 ---- - -
    - Normally the page layout scales automatically to the available width. If you - want to have a fixed width (e.g. for printing) the size can be specified via width: 800 or data-width="800" -
    - -
    - -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    \ No newline at end of file diff --git a/Documentation/input/examples/render-customization/stretchforce.cshtml b/Documentation/input/examples/render-customization/stretchforce.cshtml deleted file mode 100644 index 966f2a603..000000000 --- a/Documentation/input/examples/render-customization/stretchforce.cshtml +++ /dev/null @@ -1,39 +0,0 @@ -Title: Adjust stretch force -Order: 6 ---- - -
    - The stretch force of alphaTab specifies how compact/wide the music notation is rendered. The force is 1 by default, bigger values make the notation wider, smaller more compact. -
    - -
    - -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    \ No newline at end of file diff --git a/Documentation/input/examples/render-customization/transposedisplay.cshtml b/Documentation/input/examples/render-customization/transposedisplay.cshtml deleted file mode 100644 index c34be4541..000000000 --- a/Documentation/input/examples/render-customization/transposedisplay.cshtml +++ /dev/null @@ -1,39 +0,0 @@ -Title: Transpose display -Order: 8 ---- - -
    - By default standard music notation for guitar tabs is shifted by 1 octave for display. This does not have an impact on the audio. This feature can also be used manually. For all tracks the number of semitones can be specified. The option displayTranspositionPitches or the data attribute data-display-transposition-pitches define the pitches for transposition. -
    - -
    - -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    \ No newline at end of file diff --git a/Documentation/input/examples/render-customization/transposedisplayplay.cshtml b/Documentation/input/examples/render-customization/transposedisplayplay.cshtml deleted file mode 100644 index 0f5b372a8..000000000 --- a/Documentation/input/examples/render-customization/transposedisplayplay.cshtml +++ /dev/null @@ -1,41 +0,0 @@ -Title: Transpose display and playback -Order: 9 ---- - -
    - The whole music notation can be transposed by a given number of semitones. - Be aware that this might lead to negative numbers for guitar tablatures since guitar tabs - are not recalculated to be within the fretboard. The option transpositionPitches or the data attribute data-transposition-pitches define the pitches for transposition. -
    - -
    - -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    \ No newline at end of file diff --git a/Documentation/input/examples/stave-configurations/index.cshtml b/Documentation/input/examples/stave-configurations/index.cshtml deleted file mode 100644 index 34df1371e..000000000 --- a/Documentation/input/examples/stave-configurations/index.cshtml +++ /dev/null @@ -1,8 +0,0 @@ -Title: Stave Configurations -Description: This section contains examples on how to configure the staves shown in alphaTab. -Order: 2 ---- - -

    @Html.Raw(Model.String(DocsKeys.Description))

    - -@Html.Partial("_ChildPages") \ No newline at end of file diff --git a/Documentation/input/examples/stave-configurations/rhythm.cshtml b/Documentation/input/examples/stave-configurations/rhythm.cshtml deleted file mode 100644 index 3ec6fecd5..000000000 --- a/Documentation/input/examples/stave-configurations/rhythm.cshtml +++ /dev/null @@ -1,129 +0,0 @@ -Title: Guitar tablature with rhythm -Order: 3 ---- - -
    - When using the tab stave configuration, additionaly the rhythm - can be displayed via the additional stave setting rhythm: true or the attribute data-staves-rhythm="true" -
    - -
    - -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    - -
    - The rhythm height can be adjusted via rhythmHeight: 50 or data-staves-rhythm-height="50" -
    - -
    - -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    - -
    - You can prevent beaming by setting rhythmBeams: false or data-staves-rhythm-beams="false" -
    - -
    - -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    \ No newline at end of file diff --git a/Documentation/input/examples/stave-configurations/score.cshtml b/Documentation/input/examples/stave-configurations/score.cshtml deleted file mode 100644 index f1b88af64..000000000 --- a/Documentation/input/examples/stave-configurations/score.cshtml +++ /dev/null @@ -1,39 +0,0 @@ -Title: Score only -Order: 1 ---- - -
    - To show the standard music notation only, set either staves: 'score' or data-staves="score" -
    - -
    - -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    \ No newline at end of file diff --git a/Documentation/input/examples/stave-configurations/scoretab.cshtml b/Documentation/input/examples/stave-configurations/scoretab.cshtml deleted file mode 100644 index f41f91632..000000000 --- a/Documentation/input/examples/stave-configurations/scoretab.cshtml +++ /dev/null @@ -1,42 +0,0 @@ -Title: Score and guitar tablature -Order: 0 ---- - -
    - This stave configuration is the default setting. It will show the standard music notation - and the guitar tablature for the rendered tracks. If the instrument is not stringed, the tab - staff is hidden automatically. - The layout can explicitely be set via staves: 'score-tab' or data-staves="score-tab" -
    - -
    - -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    \ No newline at end of file diff --git a/Documentation/input/examples/stave-configurations/tab.cshtml b/Documentation/input/examples/stave-configurations/tab.cshtml deleted file mode 100644 index eb00af34f..000000000 --- a/Documentation/input/examples/stave-configurations/tab.cshtml +++ /dev/null @@ -1,39 +0,0 @@ -Title: Guitar tablature only -Order: 2 ---- - -
    - To show the guitar tablature only, set either staves: 'tab' or data-staves="tab" -
    - -
    - -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    \ No newline at end of file diff --git a/Documentation/input/features/effects.cshtml b/Documentation/input/features/effects.cshtml deleted file mode 100644 index b0f26f5f0..000000000 --- a/Documentation/input/features/effects.cshtml +++ /dev/null @@ -1,226 +0,0 @@ -Title: Effects and Annotations -Order: 6 ---- - -

    Markers

    - -

    - Markers are a nice element to annotate different sections of the song. -

    -
    - - -

    Tempo

    - -

    - You might already have mentioned them: Tempo markers are supported too. -

    - -
    - - -

    Text

    - -

    - Each beat can have a text attached. -

    - -
    - - -

    Chords

    - -

    - Chord names can be useful to indicate how to play a song. -

    - -
    - - -

    Vibrato

    - -

    - Beat vibrators and note vibratos are both available. -

    - -
    - - -

    Dynamics

    - -

    - From piano pianissimo to forte fortissimo, they are all there -

    - -
    - - -

    Tap/Slap/Pop

    - -

    - Especially bass guitar players will like this feature. -

    - -
    - - -

    Fade-In

    - -

    - This annotation is shown if you need to increase the note volume from silent to normal when - playing it. -

    - -
    - - -

    Let Ring

    - -

    - If this annotation is placed on a note, you don't stop the string after the beat ends. -

    - -
    - - -

    Palm Mute

    - -

    - A feature you won't miss: Palm Mute markers. -

    - -
    - - -

    String Bends

    - -

    - String bendings can be rendered by alphaTab as well. -

    - -
    - - -

    Whammy Bar

    - -

    - A lot of electrical guitars come with whammy bars. alphaTab - can show whammy bar effects. -

    - -
    - - -

    Tremolo Picking

    - -

    - Tremolo Pickings are rendered too. -

    - -
    - - -

    Brush Strokes

    - -

    - Brush Strokes are shown as arrows on the tablature stave. -

    - -
    - - -

    Slides

    - -

    - There are several types of slides alphaTab can render. -

    - -
    - - -

    Trill

    - -

    - Trill effects are rendered on the tablature stave and on an effect line. -

    - -
    - - -

    Pick Stroke

    - -

    - Pick strokes indicate how to play the strings. -

    - -
    - - -

    Tuplets

    - -

    - Tuplets are used to divide beats into equal subdivisions. -

    - -
    - - -

    Fingering

    - -

    - Finger indicators can be used to highlight which fingers should be used to play - the respective note on the instrument. -

    - -
    - - -

    Triplet-Feel

    - -

    - Triplet feels are used to indicate that a pair of particular notes are played with a different - durations as they are indicated by the annotations. -

    - -
    - diff --git a/Documentation/input/features/general.cshtml b/Documentation/input/features/general.cshtml deleted file mode 100644 index 572f92921..000000000 --- a/Documentation/input/features/general.cshtml +++ /dev/null @@ -1,98 +0,0 @@ -Title: General -Order: 0 ---- - -

    Score Details

    - -

    - The full score details are available as object dom. This way you can read and process any - file like you need. -

    - -
    - Loading... -
    - - -

    Song Details

    - -

    - Some of the layout engines can render the song details on top of the music sheet. -

    - -
    - - -

    HTML5 Canvas

    - -

    - Do you prefer an HTML5 canvas output instead of SVG? No problem. -

    - -
    - - -

    Repeats

    - -

    - Need display repeat marks? No problem, single repeats and multiple repeats come out of the box. -

    - -
    - - - -

    Alternate Endings

    - -

    - Even alternate repeat endings are no problem. -

    - -
    - - -

    Tunings

    - -

    - The guitar tuning of the current track is rendered on the top of the music notation. -

    - -
    - \ No newline at end of file diff --git a/Documentation/input/features/guitar-tabs.cshtml b/Documentation/input/features/guitar-tabs.cshtml deleted file mode 100644 index 4e8aa8661..000000000 --- a/Documentation/input/features/guitar-tabs.cshtml +++ /dev/null @@ -1,45 +0,0 @@ -Title: Guitar Tabs -Order: 3 ---- - -

    Rhythm

    - -

    - If you only display guitar tablatures, it might be interesting to add - a rhythm stave to show the durations. You can do this either above or below - the tablature. -

    - -
    - - -

    Rhythm With Beams

    - -

    - If you prefer beams instead of bars for single notes you can have that too. -

    - -
    - - -

    String Variations

    - -

    - alphaTab will render tabs for stringed instruments. It takes care of - the number of strings and displays the tabs as you would expect. -

    - -
    - diff --git a/Documentation/input/features/index.cshtml b/Documentation/input/features/index.cshtml deleted file mode 100644 index b028b865f..000000000 --- a/Documentation/input/features/index.cshtml +++ /dev/null @@ -1,12 +0,0 @@ -Title: Features -TexSample: true ---- - -

    - These pages provide an insight on what features alphaTab offers. Navigate through the sections in the sidebar to check out the individual features. -

    - -
    - \ No newline at end of file diff --git a/Documentation/input/features/layouts.cshtml b/Documentation/input/features/layouts.cshtml deleted file mode 100644 index 733d1a6cb..000000000 --- a/Documentation/input/features/layouts.cshtml +++ /dev/null @@ -1,117 +0,0 @@ -Title: Layouts -Order: 1 ---- -

    - alphaTab supports several different layouts which align bars in a various manner. -

    - -

    Page Layout

    - -

    - All bars are aligned in rows side by side till the row is full. -

    - -
    - - - - -

    Page Layout (custom width)

    - -

    - All bars are aligned in rows side by side till the row is full. -

    - -
    - - - -

    Page Layout (5 bars per row)

    - -

    - You can configure how many bars should be placed per row -

    - -
    - - -

    Page Layout (bar 5 to 8)

    - -

    - It's also possible to only render some of the bars available in a track. -

    - -
    - - -

    Horizontal Layout

    - -

    - This is another layout type available. All bars are aligned horizontally. -

    - -
    - - -

    Horizontal Layout (bar 5 to 8)

    - -

    - You can also filter the rendered bars using this layout. -

    - -
    - diff --git a/Documentation/input/features/music-notation.cshtml b/Documentation/input/features/music-notation.cshtml deleted file mode 100644 index 41b9b0faf..000000000 --- a/Documentation/input/features/music-notation.cshtml +++ /dev/null @@ -1,83 +0,0 @@ -Title: Music Notation -Order: 2 ---- - -

    - Rendering standard music notation is one of the essential features of alphaTab. -

    - -

    Clefs

    - -

    - Clefs are important for music notation too. -

    - -
    - \title "Clefs" - . - \clef G2 | \clef F4 | \clef C3 | \clef C4 -
    - - -

    Key Signatures

    - -

    - Want key signatures? No problem, alphaTab will render them for you. -

    - -
    - - -

    Time Signatures

    - - -

    - Time signatures are important too. -

    - -
    - - -

    Notes, Rests and Beams

    - -

    - We automatically create beams for notes and ensure they are aligned in the most readable way. -

    - -
    - - -

    Accidentals

    - -

    - Accidentals are an essential element of music notation. -

    - -
    - diff --git a/Documentation/input/features/special-notes.cshtml b/Documentation/input/features/special-notes.cshtml deleted file mode 100644 index f9cfc7f07..000000000 --- a/Documentation/input/features/special-notes.cshtml +++ /dev/null @@ -1,49 +0,0 @@ -Title: Special Notes -Order: 5 ---- - -

    Tied Notes

    - -

    - Tied notes are notes which are not hit a second time, - the note is being held. -

    - -
    - - -

    Grace Notes

    - -

    - Yes, alphaTab can render grace notes. -

    - -
    - - -

    Dead Notes

    - -

    - Dead notes are notes without a defined note height. On stringed instruments
    - they are played by hitting the string without pressing but with touching the finger on the fret. -

    - -
    - - -

    Ghost Notes

    - -

    - Ghost notes are played more slient than normal notes. -

    - -
    - \ No newline at end of file diff --git a/Documentation/input/features/special-tracks.cshtml b/Documentation/input/features/special-tracks.cshtml deleted file mode 100644 index 415ef71c9..000000000 --- a/Documentation/input/features/special-tracks.cshtml +++ /dev/null @@ -1,40 +0,0 @@ -Title: Special Tracks -Order: 4 ---- - -

    Drum Tabs

    - -

    - AlphaTab uses the same drum tab notation as Guitar Pro 5. Since the guitar tab staff is not meaningful on - percussion track, it's hidden in case a drum tab is displayed. -

    - -
    - - -

    Grand Staff

    - -

    - With Guitar Pro 6 files also grand staffs can be rendered. -

    - -
    - - -

    Multiple Tracks

    - -

    - AlphaTab can also render multiple tracks at the same time. -

    - -
    - \ No newline at end of file diff --git a/Font/bravura/eot/Bravura.eot b/Font/bravura/eot/Bravura.eot deleted file mode 100644 index 230df848a..000000000 Binary files a/Font/bravura/eot/Bravura.eot and /dev/null differ diff --git a/Font/bravura/otf/Bravura.otf b/Font/bravura/otf/Bravura.otf deleted file mode 100644 index 3a4264f72..000000000 Binary files a/Font/bravura/otf/Bravura.otf and /dev/null differ diff --git a/Font/bravura/svg/Bravura.svg b/Font/bravura/svg/Bravura.svg deleted file mode 100644 index ddbcae5ae..000000000 --- a/Font/bravura/svg/Bravura.svg +++ /dev/null @@ -1,3865 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Font/bravura/woff/Bravura.woff b/Font/bravura/woff/Bravura.woff deleted file mode 100644 index cfd576d5d..000000000 Binary files a/Font/bravura/woff/Bravura.woff and /dev/null differ diff --git a/LICENSE b/LICENSE index 3a3b445fc..fa0086a95 100644 --- a/LICENSE +++ b/LICENSE @@ -1,165 +1,373 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 +Mozilla Public License Version 2.0 +================================== - Copyright (C) 2007 Free Software Foundation, Inc. [http://fsf.org/] - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. +1. Definitions +-------------- +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. - This version of the GNU Lesser General Public License incorporates -the terms and conditions of version 3 of the GNU General Public -License, supplemented by the additional permissions listed below. +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. - 0. Additional Definitions. +1.3. "Contribution" + means Covered Software of a particular Contributor. - As used herein, "this License" refers to version 3 of the GNU Lesser -General Public License, and the "GNU GPL" refers to version 3 of the GNU -General Public License. +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. - "The Library" refers to a covered work governed by this License, -other than an Application or a Combined Work as defined below. - - An "Application" is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - - A "Combined Work" is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the "Linked -Version". - - The "Minimal Corresponding Source" for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - - The "Corresponding Application Code" for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. - - 1. Exception to Section 3 of the GNU GPL. - - You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. - - 2. Conveying Modified Versions. - - If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - - a) under this License, provided that you make a good faith effort to - ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or - - b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. - - 3. Object Code Incorporating Material from Library Header Files. - - The object code form of an Application may incorporate material from -a header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - - a) Give prominent notice with each copy of the object code that the - Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the object code with a copy of the GNU GPL and this license - document. - - 4. Combined Works. - - You may convey a Combined Work under terms of your choice that, -taken together, effectively do not restrict modification of the -portions of the Library contained in the Combined Work and reverse -engineering for debugging such modifications, if you also do each of -the following: - - a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the Combined Work with a copy of the GNU GPL and this license - document. - - c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. - - d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of this - License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (a) uses at run time - a copy of the Library already present on the user's computer - system, and (b) will operate properly with a modified version - of the Library that is interface-compatible with the Linked - Version. - - e) Provide Installation Information, but only if you would otherwise - be required to provide such information under section 6 of the - GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the - Application with a modified version of the Linked Version. (If - you use option 4d0, the Installation Information must accompany - the Minimal Corresponding Source and Corresponding Application - Code. If you use option 4d1, you must provide the Installation - Information in the manner specified by section 6 of the GNU GPL - for conveying Corresponding Source.) - - 5. Combined Libraries. - - You may place library facilities that are a work based on the -Library side by side in a single library together with other library -facilities that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - - a) Accompany the combined library with a copy of the same work based - on the Library, uncombined with any other library facilities, - conveyed under the terms of this License. - - b) Give prominent notice with the combined library that part of it - is a work based on the Library, and explaining where to find the - accompanying uncombined form of the same work. - - 6. Revised Versions of the GNU Lesser General Public License. - - The Free Software Foundation may publish revised and/or new versions -of the GNU Lesser General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - - Each version is given a distinguishing version number. If the -Library as you received it specifies that a certain numbered version -of the GNU Lesser General Public License "or any later version" -applies to it, you have the option of following the terms and -conditions either of that published version or of any later version -published by the Free Software Foundation. If the Library as you -received it does not specify a version number of the GNU Lesser -General Public License, you may choose any version of the GNU Lesser -General Public License ever published by the Free Software Foundation. - - If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the -Library. \ No newline at end of file +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + 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/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. \ No newline at end of file diff --git a/LICENSE.header b/LICENSE.header new file mode 100644 index 000000000..ab16ba851 --- /dev/null +++ b/LICENSE.header @@ -0,0 +1,13 @@ +alphaTab v<%= pkg.version %> (<%= data.branch %>, build <%= data.build %>) + +Copyright © <%= moment().format('YYYY') %>, 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/. + +SoundFont loading and Audio Synthesis based on TinySoundFont (licensed under MIT) +Copyright (C) 2017, 2018 Bernhard Schelling (https://github.com/schellingb/TinySoundFont) + +TinySoundFont is based on SFZero (licensed under MIT) +Copyright (C) 2012 Steve Folta (https://github.com/stevefolta/SFZero) \ No newline at end of file diff --git a/Phase/Compiler/CopyNewCompiler.bat b/Phase/Compiler/CopyNewCompiler.bat deleted file mode 100644 index 16780a76e..000000000 --- a/Phase/Compiler/CopyNewCompiler.bat +++ /dev/null @@ -1,2 +0,0 @@ -taskkill /im:msbuild.exe /f -xcopy D:\Dev\C#\Projects\Phase.netstandard\Phase.MsBuild\bin\Debug\net471\* . /Y \ No newline at end of file diff --git a/Phase/Compiler/Microsoft.Build.Framework.dll b/Phase/Compiler/Microsoft.Build.Framework.dll deleted file mode 100644 index 5b63a6c70..000000000 Binary files a/Phase/Compiler/Microsoft.Build.Framework.dll and /dev/null differ diff --git a/Phase/Compiler/Microsoft.Build.Tasks.Core.dll b/Phase/Compiler/Microsoft.Build.Tasks.Core.dll deleted file mode 100644 index 2389c63b0..000000000 Binary files a/Phase/Compiler/Microsoft.Build.Tasks.Core.dll and /dev/null differ diff --git a/Phase/Compiler/Microsoft.Build.Utilities.Core.dll b/Phase/Compiler/Microsoft.Build.Utilities.Core.dll deleted file mode 100644 index f499f5587..000000000 Binary files a/Phase/Compiler/Microsoft.Build.Utilities.Core.dll and /dev/null differ diff --git a/Phase/Compiler/Microsoft.Build.dll b/Phase/Compiler/Microsoft.Build.dll deleted file mode 100644 index 8812da60c..000000000 Binary files a/Phase/Compiler/Microsoft.Build.dll and /dev/null differ diff --git a/Phase/Compiler/Microsoft.CodeAnalysis.CSharp.Workspaces.dll b/Phase/Compiler/Microsoft.CodeAnalysis.CSharp.Workspaces.dll deleted file mode 100644 index b269eecff..000000000 Binary files a/Phase/Compiler/Microsoft.CodeAnalysis.CSharp.Workspaces.dll and /dev/null differ diff --git a/Phase/Compiler/Microsoft.CodeAnalysis.CSharp.dll b/Phase/Compiler/Microsoft.CodeAnalysis.CSharp.dll deleted file mode 100644 index 2ad58e695..000000000 Binary files a/Phase/Compiler/Microsoft.CodeAnalysis.CSharp.dll and /dev/null differ diff --git a/Phase/Compiler/Microsoft.CodeAnalysis.VisualBasic.Workspaces.dll b/Phase/Compiler/Microsoft.CodeAnalysis.VisualBasic.Workspaces.dll deleted file mode 100644 index 231cd1c6c..000000000 Binary files a/Phase/Compiler/Microsoft.CodeAnalysis.VisualBasic.Workspaces.dll and /dev/null differ diff --git a/Phase/Compiler/Microsoft.CodeAnalysis.VisualBasic.dll b/Phase/Compiler/Microsoft.CodeAnalysis.VisualBasic.dll deleted file mode 100644 index a3985f275..000000000 Binary files a/Phase/Compiler/Microsoft.CodeAnalysis.VisualBasic.dll and /dev/null differ diff --git a/Phase/Compiler/Microsoft.CodeAnalysis.Workspaces.Desktop.dll b/Phase/Compiler/Microsoft.CodeAnalysis.Workspaces.Desktop.dll deleted file mode 100644 index e836804cf..000000000 Binary files a/Phase/Compiler/Microsoft.CodeAnalysis.Workspaces.Desktop.dll and /dev/null differ diff --git a/Phase/Compiler/Microsoft.CodeAnalysis.Workspaces.dll b/Phase/Compiler/Microsoft.CodeAnalysis.Workspaces.dll deleted file mode 100644 index 8a64fac3f..000000000 Binary files a/Phase/Compiler/Microsoft.CodeAnalysis.Workspaces.dll and /dev/null differ diff --git a/Phase/Compiler/Microsoft.CodeAnalysis.dll b/Phase/Compiler/Microsoft.CodeAnalysis.dll deleted file mode 100644 index 270574d39..000000000 Binary files a/Phase/Compiler/Microsoft.CodeAnalysis.dll and /dev/null differ diff --git a/Phase/Compiler/Mono.Cecil.Mdb.dll b/Phase/Compiler/Mono.Cecil.Mdb.dll deleted file mode 100644 index b44695386..000000000 Binary files a/Phase/Compiler/Mono.Cecil.Mdb.dll and /dev/null differ diff --git a/Phase/Compiler/Mono.Cecil.Pdb.dll b/Phase/Compiler/Mono.Cecil.Pdb.dll deleted file mode 100644 index bff1a30e1..000000000 Binary files a/Phase/Compiler/Mono.Cecil.Pdb.dll and /dev/null differ diff --git a/Phase/Compiler/Mono.Cecil.Rocks.dll b/Phase/Compiler/Mono.Cecil.Rocks.dll deleted file mode 100644 index 8e3e779b3..000000000 Binary files a/Phase/Compiler/Mono.Cecil.Rocks.dll and /dev/null differ diff --git a/Phase/Compiler/Mono.Cecil.dll b/Phase/Compiler/Mono.Cecil.dll deleted file mode 100644 index 8275c7bb2..000000000 Binary files a/Phase/Compiler/Mono.Cecil.dll and /dev/null differ diff --git a/Phase/Compiler/NLog.dll b/Phase/Compiler/NLog.dll deleted file mode 100644 index c045512ef..000000000 Binary files a/Phase/Compiler/NLog.dll and /dev/null differ diff --git a/Phase/Compiler/Newtonsoft.Json.dll b/Phase/Compiler/Newtonsoft.Json.dll deleted file mode 100644 index 80699020c..000000000 Binary files a/Phase/Compiler/Newtonsoft.Json.dll and /dev/null differ diff --git a/Phase/Compiler/Phase.Build.targets b/Phase/Compiler/Phase.Build.targets deleted file mode 100644 index 87346f6ad..000000000 --- a/Phase/Compiler/Phase.Build.targets +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/Phase/Compiler/Phase.Core.dll b/Phase/Compiler/Phase.Core.dll deleted file mode 100644 index 179ce612c..000000000 Binary files a/Phase/Compiler/Phase.Core.dll and /dev/null differ diff --git a/Phase/Compiler/Phase.Core.pdb b/Phase/Compiler/Phase.Core.pdb deleted file mode 100644 index dfbe7ac56..000000000 Binary files a/Phase/Compiler/Phase.Core.pdb and /dev/null differ diff --git a/Phase/Compiler/Phase.MsBuild.dll b/Phase/Compiler/Phase.MsBuild.dll deleted file mode 100644 index 6de6d07fd..000000000 Binary files a/Phase/Compiler/Phase.MsBuild.dll and /dev/null differ diff --git a/Phase/Compiler/Phase.MsBuild.dll.config b/Phase/Compiler/Phase.MsBuild.dll.config deleted file mode 100644 index cc5bae109..000000000 --- a/Phase/Compiler/Phase.MsBuild.dll.config +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Phase/Compiler/Phase.MsBuild.pdb b/Phase/Compiler/Phase.MsBuild.pdb deleted file mode 100644 index 906a5fea4..000000000 Binary files a/Phase/Compiler/Phase.MsBuild.pdb and /dev/null differ diff --git a/Phase/Compiler/Phase.Translator.dll b/Phase/Compiler/Phase.Translator.dll deleted file mode 100644 index 72535f123..000000000 Binary files a/Phase/Compiler/Phase.Translator.dll and /dev/null differ diff --git a/Phase/Compiler/Phase.Translator.pdb b/Phase/Compiler/Phase.Translator.pdb deleted file mode 100644 index 42ddfef2a..000000000 Binary files a/Phase/Compiler/Phase.Translator.pdb and /dev/null differ diff --git a/Phase/Compiler/System.Collections.Immutable.dll b/Phase/Compiler/System.Collections.Immutable.dll deleted file mode 100644 index ce6fc0e8d..000000000 Binary files a/Phase/Compiler/System.Collections.Immutable.dll and /dev/null differ diff --git a/Phase/Compiler/System.Composition.AttributedModel.dll b/Phase/Compiler/System.Composition.AttributedModel.dll deleted file mode 100644 index 4acc216e1..000000000 Binary files a/Phase/Compiler/System.Composition.AttributedModel.dll and /dev/null differ diff --git a/Phase/Compiler/System.Composition.Convention.dll b/Phase/Compiler/System.Composition.Convention.dll deleted file mode 100644 index ef3669bb2..000000000 Binary files a/Phase/Compiler/System.Composition.Convention.dll and /dev/null differ diff --git a/Phase/Compiler/System.Composition.Hosting.dll b/Phase/Compiler/System.Composition.Hosting.dll deleted file mode 100644 index a446fe6e0..000000000 Binary files a/Phase/Compiler/System.Composition.Hosting.dll and /dev/null differ diff --git a/Phase/Compiler/System.Composition.Runtime.dll b/Phase/Compiler/System.Composition.Runtime.dll deleted file mode 100644 index a05bfe9c8..000000000 Binary files a/Phase/Compiler/System.Composition.Runtime.dll and /dev/null differ diff --git a/Phase/Compiler/System.Composition.TypedParts.dll b/Phase/Compiler/System.Composition.TypedParts.dll deleted file mode 100644 index cfae95d0c..000000000 Binary files a/Phase/Compiler/System.Composition.TypedParts.dll and /dev/null differ diff --git a/Phase/Compiler/System.Data.Common.dll b/Phase/Compiler/System.Data.Common.dll deleted file mode 100644 index 198fd3e8c..000000000 Binary files a/Phase/Compiler/System.Data.Common.dll and /dev/null differ diff --git a/Phase/Compiler/System.Diagnostics.StackTrace.dll b/Phase/Compiler/System.Diagnostics.StackTrace.dll deleted file mode 100644 index 0a4be4925..000000000 Binary files a/Phase/Compiler/System.Diagnostics.StackTrace.dll and /dev/null differ diff --git a/Phase/Compiler/System.Diagnostics.Tracing.dll b/Phase/Compiler/System.Diagnostics.Tracing.dll deleted file mode 100644 index 97d8b1176..000000000 Binary files a/Phase/Compiler/System.Diagnostics.Tracing.dll and /dev/null differ diff --git a/Phase/Compiler/System.Globalization.Extensions.dll b/Phase/Compiler/System.Globalization.Extensions.dll deleted file mode 100644 index 5e6f83c7f..000000000 Binary files a/Phase/Compiler/System.Globalization.Extensions.dll and /dev/null differ diff --git a/Phase/Compiler/System.IO.Compression.dll b/Phase/Compiler/System.IO.Compression.dll deleted file mode 100644 index f8468a652..000000000 Binary files a/Phase/Compiler/System.IO.Compression.dll and /dev/null differ diff --git a/Phase/Compiler/System.Net.Http.dll b/Phase/Compiler/System.Net.Http.dll deleted file mode 100644 index e935b3b6e..000000000 Binary files a/Phase/Compiler/System.Net.Http.dll and /dev/null differ diff --git a/Phase/Compiler/System.Net.Sockets.dll b/Phase/Compiler/System.Net.Sockets.dll deleted file mode 100644 index b8d0bbb92..000000000 Binary files a/Phase/Compiler/System.Net.Sockets.dll and /dev/null differ diff --git a/Phase/Compiler/System.Reflection.Metadata.dll b/Phase/Compiler/System.Reflection.Metadata.dll deleted file mode 100644 index ee68731c0..000000000 Binary files a/Phase/Compiler/System.Reflection.Metadata.dll and /dev/null differ diff --git a/Phase/Compiler/System.Runtime.Serialization.Primitives.dll b/Phase/Compiler/System.Runtime.Serialization.Primitives.dll deleted file mode 100644 index a1b1537ee..000000000 Binary files a/Phase/Compiler/System.Runtime.Serialization.Primitives.dll and /dev/null differ diff --git a/Phase/Compiler/System.Security.Cryptography.Algorithms.dll b/Phase/Compiler/System.Security.Cryptography.Algorithms.dll deleted file mode 100644 index 71241422f..000000000 Binary files a/Phase/Compiler/System.Security.Cryptography.Algorithms.dll and /dev/null differ diff --git a/Phase/Compiler/System.Security.SecureString.dll b/Phase/Compiler/System.Security.SecureString.dll deleted file mode 100644 index 1db0169d2..000000000 Binary files a/Phase/Compiler/System.Security.SecureString.dll and /dev/null differ diff --git a/Phase/Compiler/System.Text.Encoding.CodePages.dll b/Phase/Compiler/System.Text.Encoding.CodePages.dll deleted file mode 100644 index 0f2f44744..000000000 Binary files a/Phase/Compiler/System.Text.Encoding.CodePages.dll and /dev/null differ diff --git a/Phase/Compiler/System.Threading.Overlapped.dll b/Phase/Compiler/System.Threading.Overlapped.dll deleted file mode 100644 index f43c3134b..000000000 Binary files a/Phase/Compiler/System.Threading.Overlapped.dll and /dev/null differ diff --git a/Phase/Compiler/System.Threading.Tasks.Dataflow.dll b/Phase/Compiler/System.Threading.Tasks.Dataflow.dll deleted file mode 100644 index 7567471e1..000000000 Binary files a/Phase/Compiler/System.Threading.Tasks.Dataflow.dll and /dev/null differ diff --git a/Phase/Compiler/System.Xml.XPath.XDocument.dll b/Phase/Compiler/System.Xml.XPath.XDocument.dll deleted file mode 100644 index 445d51a45..000000000 Binary files a/Phase/Compiler/System.Xml.XPath.XDocument.dll and /dev/null differ diff --git a/Phase/Mscorlib/system/Action.hx b/Phase/Mscorlib/system/Action.hx deleted file mode 100644 index d1fe4a33a..000000000 --- a/Phase/Mscorlib/system/Action.hx +++ /dev/null @@ -1,3 +0,0 @@ -package system; - -typedef Action = Void->Void; \ No newline at end of file diff --git a/Phase/Mscorlib/system/Action1.hx b/Phase/Mscorlib/system/Action1.hx deleted file mode 100644 index fab5d6a27..000000000 --- a/Phase/Mscorlib/system/Action1.hx +++ /dev/null @@ -1,3 +0,0 @@ -package system; - -typedef Action1 = T->Void; \ No newline at end of file diff --git a/Phase/Mscorlib/system/Action2.hx b/Phase/Mscorlib/system/Action2.hx deleted file mode 100644 index 56fd41bd0..000000000 --- a/Phase/Mscorlib/system/Action2.hx +++ /dev/null @@ -1,3 +0,0 @@ -package system; - -typedef Action2 = T1->T2->Void; \ No newline at end of file diff --git a/Phase/Mscorlib/system/Action3.hx b/Phase/Mscorlib/system/Action3.hx deleted file mode 100644 index 0ba881ea0..000000000 --- a/Phase/Mscorlib/system/Action3.hx +++ /dev/null @@ -1,3 +0,0 @@ -package system; - -typedef Action3 = T1->T2->T3->Void; \ No newline at end of file diff --git a/Phase/Mscorlib/system/Action9.hx b/Phase/Mscorlib/system/Action9.hx deleted file mode 100644 index 864eb98be..000000000 --- a/Phase/Mscorlib/system/Action9.hx +++ /dev/null @@ -1,3 +0,0 @@ -package system; - -typedef Action9 = T1->T2->T3->T4->T5->T6->T7->T8->T9->Void; \ No newline at end of file diff --git a/Phase/Mscorlib/system/ArgumentException.hx b/Phase/Mscorlib/system/ArgumentException.hx deleted file mode 100644 index 884ec5e8d..000000000 --- a/Phase/Mscorlib/system/ArgumentException.hx +++ /dev/null @@ -1,23 +0,0 @@ -package system; - -class ArgumentException extends Exception -{ - public var ParamName(default, null):CsString; - - public function new() - { - super(); - } - public function ArgumentException_CsString(paramName:CsString) - { - Exception_CsString("argument '" + paramName + "' has an invalid value"); - ParamName = paramName; - return this; - } - public function ArgumentException_CsString_CsString(message:CsString, paramName:CsString) - { - Exception_CsString(message); - ParamName = paramName; - return this; - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/ArgumentNullException.hx b/Phase/Mscorlib/system/ArgumentNullException.hx deleted file mode 100644 index 20cde9467..000000000 --- a/Phase/Mscorlib/system/ArgumentNullException.hx +++ /dev/null @@ -1,19 +0,0 @@ -package system; - -class ArgumentNullException extends ArgumentException -{ - public function new() - { - super(); - } - public function ArgumentNullException_CsString(paramName:CsString) - { - ArgumentNullException_CsString_CsString("argument '" + paramName + "' must not be null", paramName); - return this; - } - public function ArgumentNullException_CsString_CsString(message:CsString, paramName:CsString) - { - ArgumentException_CsString_CsString(message, paramName); - return this; - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/Boolean.hx b/Phase/Mscorlib/system/Boolean.hx deleted file mode 100644 index 515d3e9a3..000000000 --- a/Phase/Mscorlib/system/Boolean.hx +++ /dev/null @@ -1,17 +0,0 @@ -package system; - -abstract Boolean(Bool) from Bool to Bool -{ - public inline function new(v:Bool) this = v; - - public inline function ToHaxeBool() : Bool return this; - public inline function ToString() : system.CsString return Std.string(this); - - @:op(!A) public inline function not() : system.Boolean return !this; - - @:op(A == B) public static function eq(lhs : system.Boolean, rhs : system.Boolean) : system.Boolean; - @:op(A != B) public static function neq(lhs : system.Boolean, rhs : system.Boolean) : system.Boolean; - @:op(A & B) public static inline function and(lhs : system.Boolean, rhs : system.Boolean) : system.Boolean return lhs && rhs; - @:op(A | B) public static inline function or(lhs : system.Boolean, rhs : system.Boolean) : system.Boolean return lhs || rhs; - @:op(A ^ B) public static inline function xor(lhs : system.Boolean, rhs : system.Boolean) : system.Boolean return lhs != rhs; -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/Byte.hx b/Phase/Mscorlib/system/Byte.hx deleted file mode 100644 index 76a0c07d8..000000000 --- a/Phase/Mscorlib/system/Byte.hx +++ /dev/null @@ -1,315 +0,0 @@ -package system; - - -abstract Byte(Int) from Int -{ - public inline function new(i:Int) this = system.Convert.ToUInt8(i); - - public inline function ToHaxeInt(): Int return this; - public inline function ToString() : system.CsString return Std.string(this); - - public inline function ToBoolean_IFormatProvider(provider:IFormatProvider) : system.Boolean return system.Convert.ToBoolean_Byte(this); - public inline function ToChar_IFormatProvider(provider:IFormatProvider) : system.Char return system.Convert.ToChar_Byte(this); - public inline function ToSByte_IFormatProvider(provider:IFormatProvider) : system.SByte return system.Convert.ToSByte_Byte(this); - public inline function ToByte_IFormatProvider(provider:IFormatProvider) : system.Byte return system.Convert.ToByte_Byte(this); - public inline function ToInt16_IFormatProvider(provider:IFormatProvider) : system.Int16 return system.Convert.ToInt16_Byte(this); - public inline function ToUInt16_IFormatProvider(provider:IFormatProvider) : system.UInt16 return system.Convert.ToUInt16_Byte(this); - public inline function ToInt32_IFormatProvider(provider:IFormatProvider) : system.Int32 return system.Convert.ToInt32_Byte(this); - public inline function ToUInt32_IFormatProvider(provider:IFormatProvider) : system.UInt32 return system.Convert.ToUInt32_Byte(this); - public inline function ToInt64_IFormatProvider(provider:IFormatProvider) : system.Int64 return system.Convert.ToInt64_Byte(this); - public inline function ToUInt64_IFormatProvider(provider:IFormatProvider) : system.UInt64 return system.Convert.ToUInt64_Byte(this); - public inline function ToSingle_IFormatProvider(provider:IFormatProvider) : system.Single return system.Convert.ToSingle_Byte(this); - public inline function ToDouble_IFormatProvider(provider:IFormatProvider) : system.Double return system.Convert.ToDouble_Byte(this); - - public inline function GetHashCode() : system.Int32 return this; - - @:op(-A) public inline function neg() : system.Int32 return -this; - - @:op(~A)public inline function not() : system.Int32 return ~this; - - @:op(A++)public inline function postinc() : system.Byte return this++; - @:op(++A)public inline function preinc() : system.Byte return ++this; - - @:op(A--)public inline function postdec() : system.Byte return this--; - @:op(--A)public inline function predec() : system.Byte return --this; - - @:op(A * B) public static function mul1(lhs : system.Byte, rhs : Int) : system.Int32; - @:op(A * B) public static function mul2(lhs : Int, rhs : system.Byte) : system.Int32; - @:op(A * B) public static function mul3(lhs : system.Byte, rhs : Float) : system.Double; - @:op(A * B) public static function mul4(lhs : Float, rhs : system.Byte) : system.Double; - - @:op(A * B) public static function mul5(lhs : system.Byte, rhs : system.Char) : system.Int32; - @:op(A * B) public static function mul6(lhs : system.Byte, rhs : system.Byte) : system.Int32; - @:op(A * B) public static function mul7(lhs : system.Byte, rhs : system.Int16) : system.Int32; - @:op(A * B) public static function mul8(lhs : system.Byte, rhs : system.Int32) : system.Int32; - @:op(A * B) public static function mul9(lhs : system.Byte, rhs : system.Int64) : system.Int64; - @:op(A * B) public static function mul10(lhs : system.Byte, rhs : system.SByte) : system.Int32; - @:op(A * B) public static function mul11(lhs : system.Byte, rhs : system.UInt16) : system.Int32; - @:op(A * B) public static function mul12(lhs : system.Byte, rhs : system.UInt32) : system.UInt32; - @:op(A * B) public static function mul13(lhs : system.Byte, rhs : system.UInt64) : system.UInt64; - @:op(A * B) public static function mul14(lhs : system.Byte, rhs : system.Single) : system.Single; - @:op(A * B) public static function mul15(lhs : system.Byte, rhs : system.Double) : system.Double; - - - @:op(A / B) public static inline function div0(lhs : system.Byte, rhs : system.Byte) : system.Int32 return Std.int(lhs.ToHaxeInt() / rhs.ToHaxeInt()); - @:op(A / B) public static inline function div1(lhs : system.Byte, rhs : Int) : system.Int32 return Std.int(lhs.ToHaxeInt() / rhs); - @:op(A / B) public static inline function div2(lhs : Int, rhs : system.Byte) : system.Int32 return Std.int(lhs / rhs.ToHaxeInt()); - @:op(A / B) public static function div3(lhs : system.Byte, rhs : Float) : system.Double; - @:op(A / B) public static function div4(lhs : Float, rhs : system.Byte) : system.Double; - - @:op(A / B) public static function div5(lhs : system.Byte, rhs : system.Char) : system.Int32; - @:op(A / B) public static function div6(lhs : system.Byte, rhs : system.Byte) : system.Int32; - @:op(A / B) public static function div7(lhs : system.Byte, rhs : system.Int16) : system.Int32; - @:op(A / B) public static function div8(lhs : system.Byte, rhs : system.Int32) : system.Int32; - @:op(A / B) public static function div9(lhs : system.Byte, rhs : system.Int64) : system.Int64; - @:op(A / B) public static function div10(lhs : system.Byte, rhs : system.SByte) : system.Int32; - @:op(A / B) public static function div11(lhs : system.Byte, rhs : system.UInt16) : system.Int32; - @:op(A / B) public static function div12(lhs : system.Byte, rhs : system.UInt32) : system.UInt32; - @:op(A / B) public static function div13(lhs : system.Byte, rhs : system.UInt64) : system.UInt64; - @:op(A / B) public static function div14(lhs : system.Byte, rhs : system.Single) : system.Single; - @:op(A / B) public static function div15(lhs : system.Byte, rhs : system.Double) : system.Double; - - - @:op(A % B) public static function mod0(lhs : system.Byte, rhs : system.Byte) : system.Int32; - @:op(A % B) public static function mod1(lhs : system.Byte, rhs : Int) : system.Int32; - @:op(A % B) public static function mod2(lhs : Int, rhs : system.Byte) : system.Int32; - @:op(A % B) public static function mod3(lhs : system.Byte, rhs : Float) : system.Double; - @:op(A % B) public static function mod4(lhs : Float, rhs : system.Byte) : system.Double; - - @:op(A % B) public static function mod5(lhs : system.Byte, rhs : system.Char) : system.Int32; - @:op(A % B) public static function mod6(lhs : system.Byte, rhs : system.Byte) : system.Int32; - @:op(A % B) public static function mod7(lhs : system.Byte, rhs : system.Int16) : system.Int32; - @:op(A % B) public static function mod8(lhs : system.Byte, rhs : system.Int32) : system.Int32; - @:op(A % B) public static function mod9(lhs : system.Byte, rhs : system.Int64) : system.Int64; - @:op(A % B) public static function mod10(lhs : system.Byte, rhs : system.SByte) : system.Int32; - @:op(A % B) public static function mod11(lhs : system.Byte, rhs : system.UInt16) : system.Int32; - @:op(A % B) public static function mod12(lhs : system.Byte, rhs : system.UInt32) : system.UInt32; - @:op(A % B) public static function mod13(lhs : system.Byte, rhs : system.UInt64) : system.UInt64; - @:op(A % B) public static function mod14(lhs : system.Byte, rhs : system.Single) : system.Single; - @:op(A % B) public static function mod15(lhs : system.Byte, rhs : system.Double) : system.Double; - - - @:op(A + B) public static function add0(lhs : system.Byte, rhs : system.Byte) : system.Int32; - @:op(A + B) public static function add1(lhs : system.Byte, rhs : Int) : system.Int32; - @:op(A + B) public static function add2(lhs : Int, rhs : system.Byte) : system.Int32; - @:op(A + B) public static function add3(lhs : system.Byte, rhs : Float) : system.Double; - @:op(A + B) public static function add4(lhs : Float, rhs : system.Byte) : system.Double; - - @:op(A + B) public static function add5(lhs : system.Byte, rhs : system.Char) : system.Int32; - @:op(A + B) public static function add6(lhs : system.Byte, rhs : system.Byte) : system.Int32; - @:op(A + B) public static function add7(lhs : system.Byte, rhs : system.Int16) : system.Int32; - @:op(A + B) public static function add8(lhs : system.Byte, rhs : system.Int32) : system.Int32; - @:op(A + B) public static function add9(lhs : system.Byte, rhs : system.Int64) : system.Int64; - @:op(A + B) public static function add10(lhs : system.Byte, rhs : system.SByte) : system.Int32; - @:op(A + B) public static function add11(lhs : system.Byte, rhs : system.UInt16) : system.Int32; - @:op(A + B) public static function add12(lhs : system.Byte, rhs : system.UInt32) : system.UInt32; - @:op(A + B) public static function add13(lhs : system.Byte, rhs : system.UInt64) : system.UInt64; - @:op(A + B) public static function add14(lhs : system.Byte, rhs : system.Single) : system.Single; - @:op(A + B) public static function add15(lhs : system.Byte, rhs : system.Double) : system.Double; - @:op(A + B) public static inline function add16(lhs : system.Byte, rhs : system.CsString) : system.CsString return lhs.ToString() + rhs; - @:op(A + B) public static inline function add17(lhs : system.Byte, rhs : String) : system.CsString return lhs.ToString() + rhs; - - - @:op(A - B) public static function sub0(lhs : system.Byte, rhs : system.Byte) : system.Int32; - @:op(A - B) public static function sub1(lhs : system.Byte, rhs : Int) : system.Int32; - @:op(A - B) public static function sub2(lhs : Int, rhs : system.Byte) : system.Int32; - @:op(A - B) public static function sub3(lhs : system.Byte, rhs : Float) : system.Double; - @:op(A - B) public static function sub4(lhs : Float, rhs : system.Byte) : system.Double; - - @:op(A - B) public static function sub5(lhs : system.Byte, rhs : system.Char) : system.Int32; - @:op(A - B) public static function sub6(lhs : system.Byte, rhs : system.Byte) : system.Int32; - @:op(A - B) public static function sub7(lhs : system.Byte, rhs : system.Int16) : system.Int32; - @:op(A - B) public static function sub8(lhs : system.Byte, rhs : system.Int32) : system.Int32; - @:op(A - B) public static function sub9(lhs : system.Byte, rhs : system.Int64) : system.Int64; - @:op(A - B) public static function sub10(lhs : system.Byte, rhs : system.SByte) : system.Int32; - @:op(A - B) public static function sub11(lhs : system.Byte, rhs : system.UInt16) : system.Int32; - @:op(A - B) public static function sub12(lhs : system.Byte, rhs : system.UInt32) : system.UInt32; - @:op(A - B) public static function sub13(lhs : system.Byte, rhs : system.UInt64) : system.UInt64; - @:op(A - B) public static function sub14(lhs : system.Byte, rhs : system.Single) : system.Single; - @:op(A - B) public static function sub15(lhs : system.Byte, rhs : system.Double) : system.Double; - - - @:op(A << B) public static function shl0(lhs : system.Byte, rhs : system.Byte) : system.Int32; - @:op(A << B) public static function shl1(lhs : system.Byte, rhs : Int) : system.Int32; - @:op(A << B) public static function shl2(lhs : Int, rhs : system.Byte) : system.Int32; - - @:op(A << B) public static function shl5(lhs : system.Byte, rhs : system.Char) : system.Int32; - @:op(A << B) public static function shl6(lhs : system.Byte, rhs : system.Byte) : system.Int32; - @:op(A << B) public static function shl7(lhs : system.Byte, rhs : system.Int16) : system.Int32; - @:op(A << B) public static function shl8(lhs : system.Byte, rhs : system.Int32) : system.Int32; - // @:op(A << B) public static function shl9(lhs : system.Byte, rhs : system.Int64) : system.Int64; - @:op(A << B) public static function shl10(lhs : system.Byte, rhs : system.SByte) : system.Int32; - @:op(A << B) public static function shl11(lhs : system.Byte, rhs : system.UInt16) : system.Int32; - // @:op(A << B) public static function shl12(lhs : system.Byte, rhs : system.UInt32) : system.UInt32; - // @:op(A << B) public static function shl13(lhs : system.Byte, rhs : system.UInt64) : system.UInt64; - - - @:op(A >> B) public static function shr0(lhs : system.Byte, rhs : system.Byte) : system.Int32; - @:op(A >> B) public static function shr1(lhs : system.Byte, rhs : Int) : system.Int32; - @:op(A >> B) public static function shr2(lhs : Int, rhs : system.Byte) : system.Int32; - - @:op(A >> B) public static function shr5(lhs : system.Byte, rhs : system.Char) : system.Int32; - @:op(A >> B) public static function shr6(lhs : system.Byte, rhs : system.Byte) : system.Int32; - @:op(A >> B) public static function shr7(lhs : system.Byte, rhs : system.Int16) : system.Int32; - @:op(A >> B) public static function shr8(lhs : system.Byte, rhs : system.Int32) : system.Int32; - // @:op(A >> B) public static function shr9(lhs : system.Byte, rhs : system.Int64) : system.Int64; - @:op(A >> B) public static function shr10(lhs : system.Byte, rhs : system.SByte) : system.Int32; - @:op(A >> B) public static function shr11(lhs : system.Byte, rhs : system.UInt16) : system.Int32; - // @:op(A >> B) public static function shr12(lhs : system.Byte, rhs : system.UInt32) : system.UInt32; - // @:op(A >> B) public static function shr13(lhs : system.Byte, rhs : system.UInt64) : system.UInt64; - - - @:op(A > B) public static function gt0(lhs : system.Byte, rhs : system.Byte) : system.Boolean; - @:op(A > B) public static function gt1(lhs : system.Byte, rhs : Int) : system.Boolean; - @:op(A > B) public static function gt2(lhs : Int, rhs : system.Byte) : system.Boolean; - @:op(A > B) public static function gt3(lhs : system.Byte, rhs : Float) : system.Boolean; - @:op(A > B) public static function gt4(lhs : Float, rhs : system.Byte) : system.Boolean; - - @:op(A > B) public static function gt5(lhs : system.Byte, rhs : system.Char) : system.Boolean; - @:op(A > B) public static function gt6(lhs : system.Byte, rhs : system.Byte) : system.Boolean; - @:op(A > B) public static function gt7(lhs : system.Byte, rhs : system.Int16) : system.Boolean; - @:op(A > B) public static function gt8(lhs : system.Byte, rhs : system.Int32) : system.Boolean; - @:op(A > B) public static function gt9(lhs : system.Byte, rhs : system.Int64) : system.Boolean; - @:op(A > B) public static function gt10(lhs : system.Byte, rhs : system.SByte) : system.Boolean; - @:op(A > B) public static function gt11(lhs : system.Byte, rhs : system.UInt16) : system.Boolean; - @:op(A > B) public static function gt12(lhs : system.Byte, rhs : system.UInt32) : system.Boolean; - @:op(A > B) public static function gt13(lhs : system.Byte, rhs : system.UInt64) : system.Boolean; - @:op(A > B) public static function gt14(lhs : system.Byte, rhs : system.Single) : system.Boolean; - @:op(A > B) public static function gt15(lhs : system.Byte, rhs : system.Double) : system.Boolean; - - - @:op(A < B) public static function lt0(lhs : system.Byte, rhs : system.Byte) : system.Boolean; - @:op(A < B) public static function lt1(lhs : system.Byte, rhs : Int) : system.Boolean; - @:op(A < B) public static function lt2(lhs : Int, rhs : system.Byte) : system.Boolean; - @:op(A < B) public static function lt3(lhs : system.Byte, rhs : Float) : system.Boolean; - @:op(A < B) public static function lt4(lhs : Float, rhs : system.Byte) : system.Boolean; - - @:op(A < B) public static function lt5(lhs : system.Byte, rhs : system.Char) : system.Boolean; - @:op(A < B) public static function lt6(lhs : system.Byte, rhs : system.Byte) : system.Boolean; - @:op(A < B) public static function lt7(lhs : system.Byte, rhs : system.Int16) : system.Boolean; - @:op(A < B) public static function lt8(lhs : system.Byte, rhs : system.Int32) : system.Boolean; - @:op(A < B) public static function lt9(lhs : system.Byte, rhs : system.Int64) : system.Boolean; - @:op(A < B) public static function lt10(lhs : system.Byte, rhs : system.SByte) : system.Boolean; - @:op(A < B) public static function lt11(lhs : system.Byte, rhs : system.UInt16) : system.Boolean; - @:op(A < B) public static function lt12(lhs : system.Byte, rhs : system.UInt32) : system.Boolean; - @:op(A < B) public static function lt13(lhs : system.Byte, rhs : system.UInt64) : system.Boolean; - @:op(A < B) public static function lt14(lhs : system.Byte, rhs : system.Single) : system.Boolean; - @:op(A < B) public static function lt15(lhs : system.Byte, rhs : system.Double) : system.Boolean; - - @:op(A >= B) public static function gte0(lhs : system.Byte, rhs : system.Byte) : system.Boolean; - @:op(A >= B) public static function gte1(lhs : system.Byte, rhs : Int) : system.Boolean; - @:op(A >= B) public static function gte2(lhs : Int, rhs : system.Byte) : system.Boolean; - @:op(A >= B) public static function gte3(lhs : system.Byte, rhs : Float) : system.Boolean; - @:op(A >= B) public static function gte4(lhs : Float, rhs : system.Byte) : system.Boolean; - - @:op(A >= B) public static function gte5(lhs : system.Byte, rhs : system.Char) : system.Boolean; - @:op(A >= B) public static function gte6(lhs : system.Byte, rhs : system.Byte) : system.Boolean; - @:op(A >= B) public static function gte7(lhs : system.Byte, rhs : system.Int16) : system.Boolean; - @:op(A >= B) public static function gte8(lhs : system.Byte, rhs : system.Int32) : system.Boolean; - @:op(A >= B) public static function gte9(lhs : system.Byte, rhs : system.Int64) : system.Boolean; - @:op(A >= B) public static function gte10(lhs : system.Byte, rhs : system.SByte) : system.Boolean; - @:op(A >= B) public static function gte11(lhs : system.Byte, rhs : system.UInt16) : system.Boolean; - @:op(A >= B) public static function gte12(lhs : system.Byte, rhs : system.UInt32) : system.Boolean; - @:op(A >= B) public static function gte13(lhs : system.Byte, rhs : system.UInt64) : system.Boolean; - @:op(A >= B) public static function gte14(lhs : system.Byte, rhs : system.Single) : system.Boolean; - @:op(A >= B) public static function gte15(lhs : system.Byte, rhs : system.Double) : system.Boolean; - - - @:op(A <= B) public static function lte0(lhs : system.Byte, rhs : system.Byte) : system.Boolean; - @:op(A <= B) public static function lte1(lhs : system.Byte, rhs : Int) : system.Boolean; - @:op(A <= B) public static function lte2(lhs : Int, rhs : system.Byte) : system.Boolean; - @:op(A <= B) public static function lte3(lhs : system.Byte, rhs : Float) : system.Boolean; - @:op(A <= B) public static function lte4(lhs : Float, rhs : system.Byte) : system.Boolean; - - @:op(A <= B) public static function lte5(lhs : system.Byte, rhs : system.Char) : system.Boolean; - @:op(A <= B) public static function lte6(lhs : system.Byte, rhs : system.Byte) : system.Boolean; - @:op(A <= B) public static function lte7(lhs : system.Byte, rhs : system.Int16) : system.Boolean; - @:op(A <= B) public static function lte8(lhs : system.Byte, rhs : system.Int32) : system.Boolean; - @:op(A <= B) public static function lte9(lhs : system.Byte, rhs : system.Int64) : system.Boolean; - @:op(A <= B) public static function lte10(lhs : system.Byte, rhs : system.SByte) : system.Boolean; - @:op(A <= B) public static function lte11(lhs : system.Byte, rhs : system.UInt16) : system.Boolean; - @:op(A <= B) public static function lte12(lhs : system.Byte, rhs : system.UInt32) : system.Boolean; - @:op(A <= B) public static function lte13(lhs : system.Byte, rhs : system.UInt64) : system.Boolean; - @:op(A <= B) public static function lte14(lhs : system.Byte, rhs : system.Single) : system.Boolean; - @:op(A <= B) public static function lte15(lhs : system.Byte, rhs : system.Double) : system.Boolean; - - - @:op(A == B) public static function eq0(lhs : system.Byte, rhs : system.Byte) : system.Boolean; - @:op(A == B) public static function eq1(lhs : system.Byte, rhs : Int) : system.Boolean; - @:op(A == B) public static function eq2(lhs : Int, rhs : system.Byte) : system.Boolean; - @:op(A == B) public static function eq3(lhs : system.Byte, rhs : Float) : system.Boolean; - @:op(A == B) public static function eq4(lhs : Float, rhs : system.Byte) : system.Boolean; - - @:op(A == B) public static function eq5(lhs : system.Byte, rhs : system.Char) : system.Boolean; - @:op(A == B) public static function eq6(lhs : system.Byte, rhs : system.Byte) : system.Boolean; - @:op(A == B) public static function eq7(lhs : system.Byte, rhs : system.Int16) : system.Boolean; - @:op(A == B) public static function eq8(lhs : system.Byte, rhs : system.Int32) : system.Boolean; - @:op(A == B) public static function eq9(lhs : system.Byte, rhs : system.Int64) : system.Boolean; - @:op(A == B) public static function eq10(lhs : system.Byte, rhs : system.SByte) : system.Boolean; - @:op(A == B) public static function eq11(lhs : system.Byte, rhs : system.UInt16) : system.Boolean; - @:op(A == B) public static function eq12(lhs : system.Byte, rhs : system.UInt32) : system.Boolean; - @:op(A == B) public static function eq13(lhs : system.Byte, rhs : system.UInt64) : system.Boolean; - @:op(A == B) public static function eq14(lhs : system.Byte, rhs : system.Single) : system.Boolean; - @:op(A == B) public static function eq15(lhs : system.Byte, rhs : system.Double) : system.Boolean; - - - @:op(A != B) public static function neq0(lhs : system.Byte, rhs : system.Byte) : system.Boolean; - @:op(A != B) public static function neq1(lhs : system.Byte, rhs : Int) : system.Boolean; - @:op(A != B) public static function neq2(lhs : Int, rhs : system.Byte) : system.Boolean; - @:op(A != B) public static function neq3(lhs : system.Byte, rhs : Float) : system.Boolean; - @:op(A != B) public static function neq4(lhs : Float, rhs : system.Byte) : system.Boolean; - - @:op(A != B) public static function neq5(lhs : system.Byte, rhs : system.Char) : system.Boolean; - @:op(A != B) public static function neq6(lhs : system.Byte, rhs : system.Byte) : system.Boolean; - @:op(A != B) public static function neq7(lhs : system.Byte, rhs : system.Int16) : system.Boolean; - @:op(A != B) public static function neq8(lhs : system.Byte, rhs : system.Int32) : system.Boolean; - @:op(A != B) public static function neq9(lhs : system.Byte, rhs : system.Int64) : system.Boolean; - @:op(A != B) public static function neq10(lhs : system.Byte, rhs : system.SByte) : system.Boolean; - @:op(A != B) public static function neq11(lhs : system.Byte, rhs : system.UInt16) : system.Boolean; - @:op(A != B) public static function neq12(lhs : system.Byte, rhs : system.UInt32) : system.Boolean; - @:op(A != B) public static function neq13(lhs : system.Byte, rhs : system.UInt64) : system.Boolean; - @:op(A != B) public static function neq14(lhs : system.Byte, rhs : system.Single) : system.Boolean; - @:op(A != B) public static function neq15(lhs : system.Byte, rhs : system.Double) : system.Boolean; - - - @:op(A & B) public static function and1(lhs : system.Byte, rhs : Int) : system.Int32; - @:op(A & B) public static function and2(lhs : Int, rhs : system.Byte) : system.Int32; - - @:op(A & B) public static function and5(lhs : system.Byte, rhs : system.Char) : system.Int32; - @:op(A & B) public static function and6(lhs : system.Byte, rhs : system.Byte) : system.Int32; - @:op(A & B) public static function and7(lhs : system.Byte, rhs : system.Int16) : system.Int32; - @:op(A & B) public static function and8(lhs : system.Byte, rhs : system.Int32) : system.Int32; - @:op(A & B) public static function and9(lhs : system.Byte, rhs : system.Int64) : system.Int64; - @:op(A & B) public static function and10(lhs : system.Byte, rhs : system.SByte) : system.Int32; - @:op(A & B) public static function and11(lhs : system.Byte, rhs : system.UInt16) : system.Int32; - @:op(A & B) public static function and12(lhs : system.Byte, rhs : system.UInt32) : system.UInt32; - @:op(A & B) public static function and13(lhs : system.Byte, rhs : system.UInt64) : system.UInt64; - - @:op(A | B) public static function or0(lhs : system.Byte, rhs : system.Byte) : system.Int32; - @:op(A | B) public static function or1(lhs : system.Byte, rhs : Int) : system.Int32; - @:op(A | B) public static function or2(lhs : Int, rhs : system.Byte) : system.Int32; - - @:op(A | B) public static function or5(lhs : system.Byte, rhs : system.Char) : system.Int32; - @:op(A | B) public static function or6(lhs : system.Byte, rhs : system.Byte) : system.Int32; - @:op(A | B) public static function or7(lhs : system.Byte, rhs : system.Int16) : system.Int32; - @:op(A | B) public static function or8(lhs : system.Byte, rhs : system.Int32) : system.Int32; - @:op(A | B) public static function or9(lhs : system.Byte, rhs : system.Int64) : system.Int64; - @:op(A | B) public static function or10(lhs : system.Byte, rhs : system.SByte) : system.Int32; - @:op(A | B) public static function or11(lhs : system.Byte, rhs : system.UInt16) : system.Int32; - @:op(A | B) public static function or12(lhs : system.Byte, rhs : system.UInt32) : system.UInt32; - @:op(A | B) public static function or13(lhs : system.Byte, rhs : system.UInt64) : system.UInt64; - - @:op(A ^ B) public static function xor0(lhs : system.Byte, rhs : system.Byte) : system.Int32; - @:op(A ^ B) public static function xor1(lhs : system.Byte, rhs : Int) : system.Int32; - @:op(A ^ B) public static function xor2(lhs : Int, rhs : system.Byte) : system.Int32; - - @:op(A ^ B) public static function xor5(lhs : system.Byte, rhs : system.Char) : system.Int32; - @:op(A ^ B) public static function xor6(lhs : system.Byte, rhs : system.Byte) : system.Int32; - @:op(A ^ B) public static function xor7(lhs : system.Byte, rhs : system.Int16) : system.Int32; - @:op(A ^ B) public static function xor8(lhs : system.Byte, rhs : system.Int32) : system.Int32; - @:op(A ^ B) public static function xor9(lhs : system.Byte, rhs : system.Int64) : system.Int64; - @:op(A ^ B) public static function xor10(lhs : system.Byte, rhs : system.SByte) : system.Int32; - @:op(A ^ B) public static function xor11(lhs : system.Byte, rhs : system.UInt16) : system.Int32; - @:op(A ^ B) public static function xor12(lhs : system.Byte, rhs : system.UInt32) : system.UInt32; - @:op(A ^ B) public static function xor13(lhs : system.Byte, rhs : system.UInt64) : system.UInt64; -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/ByteArray.hx b/Phase/Mscorlib/system/ByteArray.hx deleted file mode 100644 index 9cdb174a0..000000000 --- a/Phase/Mscorlib/system/ByteArray.hx +++ /dev/null @@ -1,24 +0,0 @@ -package system; - -abstract ByteArray(js.html.Uint8Array) -{ - public inline function new(length:Int32) this = new js.html.Uint8Array(length.ToHaxeInt()); - - @:from public static inline function fromArray(a:Array):ByteArray return cast new js.html.Uint8Array(untyped a); - @:from public static inline function fromFixedArray(a:FixedArray):ByteArray return cast new js.html.Uint8Array(untyped a.ToHaxeArray()); - @:from public static inline function fromArrayBuffer(a:js.html.ArrayBuffer):ByteArray return cast new js.html.Uint8Array(a); - - public inline function toUint8Array():js.html.Uint8Array return this; - - public var Length(get, never):Int32; - public inline function get_Length() : Int32 return this.length; - - @:op([]) public inline function get(index:Int32):Byte return this[index.ToHaxeInt()]; - @:op([]) public inline function set(index:Int32, val:Byte):Byte return this[index.ToHaxeInt()] = val.ToHaxeInt(); - - public inline function iterator() : Iterator return new ByteArrayIterator(this); - - public inline function ToEnumerable() : system.collections.generic.IEnumerable return new ByteArrayEnumerable(this); - - public static inline function empty(size:Int32) return new ByteArray(size); -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/ByteArrayEnumerable.hx b/Phase/Mscorlib/system/ByteArrayEnumerable.hx deleted file mode 100644 index c1fe87ce8..000000000 --- a/Phase/Mscorlib/system/ByteArrayEnumerable.hx +++ /dev/null @@ -1,18 +0,0 @@ -package system; - -import system.collections.generic.IEnumerable; -import system.collections.generic.IEnumerator; - -class ByteArrayEnumerable implements IEnumerable -{ - private var _array:js.html.Uint8Array; - public function new(array:js.html.Uint8Array) - { - _array= array; - } - - public function GetEnumerator() : IEnumerator - { - return new ByteArrayEnumerator(_array); - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/ByteArrayEnumerator.hx b/Phase/Mscorlib/system/ByteArrayEnumerator.hx deleted file mode 100644 index ce3026f0b..000000000 --- a/Phase/Mscorlib/system/ByteArrayEnumerator.hx +++ /dev/null @@ -1,29 +0,0 @@ -package system; - -import system.collections.generic.IEnumerator; - -class ByteArrayEnumerator implements IEnumerator -{ - private var _array:js.html.Uint8Array; - private var _i:Int; - - public function new(array:js.html.Uint8Array) - { - _array = array; - _i = -1; - } - - public var Current(get, never):Byte; - public function get_Current() : Byte return _array[_i]; - public function MoveNext() : Bool - { - if(_i >= _array.length - 1) return false; - _i++; - return true; - - } - public function Reset():Void - { - _i = -1; - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/ByteArrayIterator.hx b/Phase/Mscorlib/system/ByteArrayIterator.hx deleted file mode 100644 index 8f463a34d..000000000 --- a/Phase/Mscorlib/system/ByteArrayIterator.hx +++ /dev/null @@ -1,19 +0,0 @@ -package system; - -class ByteArrayIterator -{ - private var _array:js.html.Uint8Array; - private var _i:Int; - - public function new(array:js.html.Uint8Array) - { - _array = array; - _i = 0; - } - - public function hasNext() return _i < _array.length; - public function next() : Byte - { - return _array[_i++]; - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/Char.hx b/Phase/Mscorlib/system/Char.hx deleted file mode 100644 index eb930b188..000000000 --- a/Phase/Mscorlib/system/Char.hx +++ /dev/null @@ -1,325 +0,0 @@ -package system; - -abstract Char(Int) from Int -{ - public inline function new(v:Int) this = system.Convert.ToUInt16(v); - - @:from public static inline function fromCode(i:Int) : Char return new Char(i); - public static inline function fromString(i:String) : Char return new Char(i.charCodeAt(0)); - - public inline function ToHaxeInt(): Int return this; - public inline function ToString() : system.CsString return String.fromCharCode(this); - - public inline function ToBoolean_IFormatProvider(provider:IFormatProvider) : system.Boolean return system.Convert.ToBoolean_Char(this); - public inline function ToChar_IFormatProvider(provider:IFormatProvider) : system.Char return system.Convert.ToChar_Char(this); - public inline function ToSByte_IFormatProvider(provider:IFormatProvider) : system.SByte return system.Convert.ToSByte_Char(this); - public inline function ToByte_IFormatProvider(provider:IFormatProvider) : system.Byte return system.Convert.ToByte_Char(this); - public inline function ToInt16_IFormatProvider(provider:IFormatProvider) : system.Int16 return system.Convert.ToInt16_Char(this); - public inline function ToUInt16_IFormatProvider(provider:IFormatProvider) : system.UInt16 return system.Convert.ToUInt16_Char(this); - public inline function ToInt32_IFormatProvider(provider:IFormatProvider) : system.Int32 return system.Convert.ToInt32_Char(this); - public inline function ToUInt32_IFormatProvider(provider:IFormatProvider) : system.UInt32 return system.Convert.ToUInt32_Char(this); - public inline function ToInt64_IFormatProvider(provider:IFormatProvider) : system.Int64 return system.Convert.ToInt64_Char(this); - public inline function ToUInt64_IFormatProvider(provider:IFormatProvider) : system.UInt64 return system.Convert.ToUInt64_Char(this); - public inline function ToSingle_IFormatProvider(provider:IFormatProvider) : system.Single return system.Convert.ToSingle_Char(this); - public inline function ToDouble_IFormatProvider(provider:IFormatProvider) : system.Double return system.Convert.ToDouble_Char(this); - - public inline function GetHashCode() : system.Int32 return this | (this << 16); - - @:op(-A) public inline function neg() : system.Int32 return -this; - - @:op(~A)public inline function not() : system.Int32 return ~this; - - @:op(A++)public inline function postinc() : system.Char return this++; - @:op(++A)public inline function preinc() : system.Char return ++this; - - @:op(A--)public inline function postdec() : system.Char return this--; - @:op(--A)public inline function predec() : system.Char return --this; - - @:op(A * B) public static function mul1(lhs : system.Char, rhs : Int) : system.Int32; - @:op(A * B) public static function mul2(lhs : Int, rhs : system.Char) : system.Int32; - @:op(A * B) public static function mul3(lhs : system.Char, rhs : Float) : system.Double; - @:op(A * B) public static function mul4(lhs : Float, rhs : system.Char) : system.Double; - - @:op(A * B) public static function mul5(lhs : system.Char, rhs : system.Char) : system.Int32; - @:op(A * B) public static function mul6(lhs : system.Char, rhs : system.Byte) : system.Int32; - @:op(A * B) public static function mul7(lhs : system.Char, rhs : system.Int16) : system.Int32; - @:op(A * B) public static function mul8(lhs : system.Char, rhs : system.Int32) : system.Int32; - @:op(A * B) public static function mul9(lhs : system.Char, rhs : system.Int64) : system.Int64; - @:op(A * B) public static function mul10(lhs : system.Char, rhs : system.SByte) : system.Int32; - @:op(A * B) public static function mul11(lhs : system.Char, rhs : system.UInt16) : system.Int32; - @:op(A * B) public static function mul12(lhs : system.Char, rhs : system.UInt32) : system.UInt32; - @:op(A * B) public static function mul13(lhs : system.Char, rhs : system.UInt64) : system.UInt64; - @:op(A * B) public static function mul14(lhs : system.Char, rhs : system.Single) : system.Single; - @:op(A * B) public static function mul15(lhs : system.Char, rhs : system.Double) : system.Double; - - - @:op(A / B) public static inline function div1(lhs : system.Char, rhs : Int) : system.Int32 return Std.int(lhs.ToHaxeInt() / rhs); - @:op(A / B) public static inline function div2(lhs : Int, rhs : system.Char) : system.Int32 return Std.int(lhs / rhs.ToHaxeInt()); - @:op(A / B) public static function div3(lhs : system.Char, rhs : Float) : system.Double; - @:op(A / B) public static function div4(lhs : Float, rhs : system.Char) : system.Double; - - @:op(A / B) public static function div5(lhs : system.Char, rhs : system.Char) : system.Int32; - @:op(A / B) public static function div6(lhs : system.Char, rhs : system.Byte) : system.Int32; - @:op(A / B) public static function div7(lhs : system.Char, rhs : system.Int16) : system.Int32; - @:op(A / B) public static function div8(lhs : system.Char, rhs : system.Int32) : system.Int32; - @:op(A / B) public static function div9(lhs : system.Char, rhs : system.Int64) : system.Int64; - @:op(A / B) public static function div10(lhs : system.Char, rhs : system.SByte) : system.Int32; - @:op(A / B) public static function div11(lhs : system.Char, rhs : system.UInt16) : system.Int32; - @:op(A / B) public static function div12(lhs : system.Char, rhs : system.UInt32) : system.UInt32; - @:op(A / B) public static function div13(lhs : system.Char, rhs : system.UInt64) : system.UInt64; - @:op(A / B) public static function div14(lhs : system.Char, rhs : system.Single) : system.Single; - @:op(A / B) public static function div15(lhs : system.Char, rhs : system.Double) : system.Double; - - - @:op(A % B) public static function mod1(lhs : system.Char, rhs : Int) : system.Int32; - @:op(A % B) public static function mod2(lhs : Int, rhs : system.Char) : system.Int32; - @:op(A % B) public static function mod3(lhs : system.Char, rhs : Float) : system.Double; - @:op(A % B) public static function mod4(lhs : Float, rhs : system.Char) : system.Double; - - @:op(A % B) public static function mod5(lhs : system.Char, rhs : system.Char) : system.Int32; - @:op(A % B) public static function mod6(lhs : system.Char, rhs : system.Byte) : system.Int32; - @:op(A % B) public static function mod7(lhs : system.Char, rhs : system.Int16) : system.Int32; - @:op(A % B) public static function mod8(lhs : system.Char, rhs : system.Int32) : system.Int32; - @:op(A % B) public static function mod9(lhs : system.Char, rhs : system.Int64) : system.Int64; - @:op(A % B) public static function mod10(lhs : system.Char, rhs : system.SByte) : system.Int32; - @:op(A % B) public static function mod11(lhs : system.Char, rhs : system.UInt16) : system.Int32; - @:op(A % B) public static function mod12(lhs : system.Char, rhs : system.UInt32) : system.UInt32; - @:op(A % B) public static function mod13(lhs : system.Char, rhs : system.UInt64) : system.UInt64; - @:op(A % B) public static function mod14(lhs : system.Char, rhs : system.Single) : system.Single; - @:op(A % B) public static function mod15(lhs : system.Char, rhs : system.Double) : system.Double; - - - @:op(A + B) public static function add1(lhs : system.Char, rhs : Int) : system.Int32; - @:op(A + B) public static function add2(lhs : Int, rhs : system.Char) : system.Int32; - @:op(A + B) public static function add3(lhs : system.Char, rhs : Float) : system.Double; - @:op(A + B) public static function add4(lhs : Float, rhs : system.Char) : system.Double; - - @:op(A + B) public static function add5(lhs : system.Char, rhs : system.Char) : system.Int32; - @:op(A + B) public static function add6(lhs : system.Char, rhs : system.Byte) : system.Int32; - @:op(A + B) public static function add7(lhs : system.Char, rhs : system.Int16) : system.Int32; - @:op(A + B) public static function add8(lhs : system.Char, rhs : system.Int32) : system.Int32; - @:op(A + B) public static function add9(lhs : system.Char, rhs : system.Int64) : system.Int64; - @:op(A + B) public static function add10(lhs : system.Char, rhs : system.SByte) : system.Int32; - @:op(A + B) public static function add11(lhs : system.Char, rhs : system.UInt16) : system.Int32; - @:op(A + B) public static function add12(lhs : system.Char, rhs : system.UInt32) : system.UInt32; - @:op(A + B) public static function add13(lhs : system.Char, rhs : system.UInt64) : system.UInt64; - @:op(A + B) public static function add14(lhs : system.Char, rhs : system.Single) : system.Single; - @:op(A + B) public static function add15(lhs : system.Char, rhs : system.Double) : system.Double; - @:op(A + B) public static inline function add16(lhs : system.Char, rhs : system.CsString) : system.CsString return lhs.ToString() + rhs; - @:op(A + B) public static inline function add17(lhs : system.Char, rhs : String) : system.CsString return lhs.ToString() + rhs; - - - @:op(A - B) public static function sub1(lhs : system.Char, rhs : Int) : system.Int32; - @:op(A - B) public static function sub2(lhs : Int, rhs : system.Char) : system.Int32; - @:op(A - B) public static function sub3(lhs : system.Char, rhs : Float) : system.Double; - @:op(A - B) public static function sub4(lhs : Float, rhs : system.Char) : system.Double; - - @:op(A - B) public static function sub5(lhs : system.Char, rhs : system.Char) : system.Int32; - @:op(A - B) public static function sub6(lhs : system.Char, rhs : system.Byte) : system.Int32; - @:op(A - B) public static function sub7(lhs : system.Char, rhs : system.Int16) : system.Int32; - @:op(A - B) public static function sub8(lhs : system.Char, rhs : system.Int32) : system.Int32; - @:op(A - B) public static function sub9(lhs : system.Char, rhs : system.Int64) : system.Int64; - @:op(A - B) public static function sub10(lhs : system.Char, rhs : system.SByte) : system.Int32; - @:op(A - B) public static function sub11(lhs : system.Char, rhs : system.UInt16) : system.Int32; - @:op(A - B) public static function sub12(lhs : system.Char, rhs : system.UInt32) : system.UInt32; - @:op(A - B) public static function sub13(lhs : system.Char, rhs : system.UInt64) : system.UInt64; - @:op(A - B) public static function sub14(lhs : system.Char, rhs : system.Single) : system.Single; - @:op(A - B) public static function sub15(lhs : system.Char, rhs : system.Double) : system.Double; - - - @:op(A << B) public static function shl1(lhs : system.Char, rhs : Int) : system.Int32; - @:op(A << B) public static function shl2(lhs : Int, rhs : system.Char) : system.Int32; - - @:op(A << B) public static function shl5(lhs : system.Char, rhs : system.Char) : system.Int32; - @:op(A << B) public static function shl6(lhs : system.Char, rhs : system.Byte) : system.Int32; - @:op(A << B) public static function shl7(lhs : system.Char, rhs : system.Int16) : system.Int32; - @:op(A << B) public static function shl8(lhs : system.Char, rhs : system.Int32) : system.Int32; - // @:op(A << B) public static function shl9(lhs : system.Char, rhs : system.Int64) : system.Int64; - @:op(A << B) public static function shl10(lhs : system.Char, rhs : system.SByte) : system.Int32; - @:op(A << B) public static function shl11(lhs : system.Char, rhs : system.UInt16) : system.Int32; - // @:op(A << B) public static function shl12(lhs : system.Char, rhs : system.UInt32) : system.UInt32; - // @:op(A << B) public static function shl13(lhs : system.Char, rhs : system.UInt64) : system.UInt64; - - - @:op(A >> B) public static function shr1(lhs : system.Char, rhs : Int) : system.Int32; - @:op(A >> B) public static function shr2(lhs : Int, rhs : system.Char) : system.Int32; - - @:op(A >> B) public static function shr5(lhs : system.Char, rhs : system.Char) : system.Int32; - @:op(A >> B) public static function shr6(lhs : system.Char, rhs : system.Byte) : system.Int32; - @:op(A >> B) public static function shr7(lhs : system.Char, rhs : system.Int16) : system.Int32; - @:op(A >> B) public static function shr8(lhs : system.Char, rhs : system.Int32) : system.Int32; - // @:op(A >> B) public static function shr9(lhs : system.Char, rhs : system.Int64) : system.Int64; - @:op(A >> B) public static function shr10(lhs : system.Char, rhs : system.SByte) : system.Int32; - @:op(A >> B) public static function shr11(lhs : system.Char, rhs : system.UInt16) : system.Int32; - // @:op(A >> B) public static function shr12(lhs : system.Char, rhs : system.UInt32) : system.UInt32; - // @:op(A >> B) public static function shr13(lhs : system.Char, rhs : system.UInt64) : system.UInt64; - - - @:op(A > B) public static function gt1(lhs : system.Char, rhs : Int) : system.Boolean; - @:op(A > B) public static function gt2(lhs : Int, rhs : system.Char) : system.Boolean; - @:op(A > B) public static function gt3(lhs : system.Char, rhs : Float) : system.Boolean; - @:op(A > B) public static function gt4(lhs : Float, rhs : system.Char) : system.Boolean; - - @:op(A > B) public static function gt5(lhs : system.Char, rhs : system.Char) : system.Boolean; - @:op(A > B) public static function gt6(lhs : system.Char, rhs : system.Byte) : system.Boolean; - @:op(A > B) public static function gt7(lhs : system.Char, rhs : system.Int16) : system.Boolean; - @:op(A > B) public static function gt8(lhs : system.Char, rhs : system.Int32) : system.Boolean; - @:op(A > B) public static function gt9(lhs : system.Char, rhs : system.Int64) : system.Boolean; - @:op(A > B) public static function gt10(lhs : system.Char, rhs : system.SByte) : system.Boolean; - @:op(A > B) public static function gt11(lhs : system.Char, rhs : system.UInt16) : system.Boolean; - @:op(A > B) public static function gt12(lhs : system.Char, rhs : system.UInt32) : system.Boolean; - @:op(A > B) public static function gt13(lhs : system.Char, rhs : system.UInt64) : system.Boolean; - @:op(A > B) public static function gt14(lhs : system.Char, rhs : system.Single) : system.Boolean; - @:op(A > B) public static function gt15(lhs : system.Char, rhs : system.Double) : system.Boolean; - - @:op(A > B) public static inline function gt16(lhs : system.Char, rhs : String) : system.Boolean return lhs.ToHaxeInt() > rhs.charCodeAt(0); - @:op(A > B) public static inline function gt17(lhs : system.Char, rhs : system.CsString) : system.Boolean return lhs.ToHaxeInt() > rhs.get_Chars(0); - - - - @:op(A < B) public static function lt1(lhs : system.Char, rhs : Int) : system.Boolean; - @:op(A < B) public static function lt2(lhs : Int, rhs : system.Char) : system.Boolean; - @:op(A < B) public static function lt3(lhs : system.Char, rhs : Float) : system.Boolean; - @:op(A < B) public static function lt4(lhs : Float, rhs : system.Char) : system.Boolean; - - @:op(A < B) public static function lt5(lhs : system.Char, rhs : system.Char) : system.Boolean; - @:op(A < B) public static function lt6(lhs : system.Char, rhs : system.Byte) : system.Boolean; - @:op(A < B) public static function lt7(lhs : system.Char, rhs : system.Int16) : system.Boolean; - @:op(A < B) public static function lt8(lhs : system.Char, rhs : system.Int32) : system.Boolean; - @:op(A < B) public static function lt9(lhs : system.Char, rhs : system.Int64) : system.Boolean; - @:op(A < B) public static function lt10(lhs : system.Char, rhs : system.SByte) : system.Boolean; - @:op(A < B) public static function lt11(lhs : system.Char, rhs : system.UInt16) : system.Boolean; - @:op(A < B) public static function lt12(lhs : system.Char, rhs : system.UInt32) : system.Boolean; - @:op(A < B) public static function lt13(lhs : system.Char, rhs : system.UInt64) : system.Boolean; - @:op(A < B) public static function lt14(lhs : system.Char, rhs : system.Single) : system.Boolean; - @:op(A < B) public static function lt15(lhs : system.Char, rhs : system.Double) : system.Boolean; - - @:op(A < B) public static inline function lt16(lhs : system.Char, rhs : String) : system.Boolean return lhs.ToHaxeInt() < rhs.charCodeAt(0); - @:op(A < B) public static inline function lt17(lhs : system.Char, rhs : system.CsString) : system.Boolean return lhs.ToHaxeInt() < rhs.get_Chars(0); - - @:op(A >= B) public static function gte1(lhs : system.Char, rhs : Int) : system.Boolean; - @:op(A >= B) public static function gte2(lhs : Int, rhs : system.Char) : system.Boolean; - @:op(A >= B) public static function gte3(lhs : system.Char, rhs : Float) : system.Boolean; - @:op(A >= B) public static function gte4(lhs : Float, rhs : system.Char) : system.Boolean; - - @:op(A >= B) public static function gte5(lhs : system.Char, rhs : system.Char) : system.Boolean; - @:op(A >= B) public static function gte6(lhs : system.Char, rhs : system.Byte) : system.Boolean; - @:op(A >= B) public static function gte7(lhs : system.Char, rhs : system.Int16) : system.Boolean; - @:op(A >= B) public static function gte8(lhs : system.Char, rhs : system.Int32) : system.Boolean; - @:op(A >= B) public static function gte9(lhs : system.Char, rhs : system.Int64) : system.Boolean; - @:op(A >= B) public static function gte10(lhs : system.Char, rhs : system.SByte) : system.Boolean; - @:op(A >= B) public static function gte11(lhs : system.Char, rhs : system.UInt16) : system.Boolean; - @:op(A >= B) public static function gte12(lhs : system.Char, rhs : system.UInt32) : system.Boolean; - @:op(A >= B) public static function gte13(lhs : system.Char, rhs : system.UInt64) : system.Boolean; - @:op(A >= B) public static function gte14(lhs : system.Char, rhs : system.Single) : system.Boolean; - @:op(A >= B) public static function gte15(lhs : system.Char, rhs : system.Double) : system.Boolean; - - @:op(A >= B) public static inline function gte16(lhs : system.Char, rhs : String) : system.Boolean return lhs.ToHaxeInt() >= rhs.charCodeAt(0); - @:op(A >= B) public static inline function gte17(lhs : system.Char, rhs : system.CsString) : system.Boolean return lhs.ToHaxeInt() >= rhs.get_Chars(0); - - - - @:op(A <= B) public static function lte1(lhs : system.Char, rhs : Int) : system.Boolean; - @:op(A <= B) public static function lte2(lhs : Int, rhs : system.Char) : system.Boolean; - @:op(A <= B) public static function lte3(lhs : system.Char, rhs : Float) : system.Boolean; - @:op(A <= B) public static function lte4(lhs : Float, rhs : system.Char) : system.Boolean; - - @:op(A <= B) public static function lte5(lhs : system.Char, rhs : system.Char) : system.Boolean; - @:op(A <= B) public static function lte6(lhs : system.Char, rhs : system.Byte) : system.Boolean; - @:op(A <= B) public static function lte7(lhs : system.Char, rhs : system.Int16) : system.Boolean; - @:op(A <= B) public static function lte8(lhs : system.Char, rhs : system.Int32) : system.Boolean; - @:op(A <= B) public static function lte9(lhs : system.Char, rhs : system.Int64) : system.Boolean; - @:op(A <= B) public static function lte10(lhs : system.Char, rhs : system.SByte) : system.Boolean; - @:op(A <= B) public static function lte11(lhs : system.Char, rhs : system.UInt16) : system.Boolean; - @:op(A <= B) public static function lte12(lhs : system.Char, rhs : system.UInt32) : system.Boolean; - @:op(A <= B) public static function lte13(lhs : system.Char, rhs : system.UInt64) : system.Boolean; - @:op(A <= B) public static function lte14(lhs : system.Char, rhs : system.Single) : system.Boolean; - @:op(A <= B) public static function lte15(lhs : system.Char, rhs : system.Double) : system.Boolean; - - @:op(A <= B) public static inline function lte16(lhs : system.Char, rhs : String) : system.Boolean return lhs.ToHaxeInt() <= rhs.charCodeAt(0); - @:op(A <= B) public static inline function lte17(lhs : system.Char, rhs : system.CsString) : system.Boolean return lhs.ToHaxeInt() <= rhs.get_Chars(0); - - - @:op(A == B) public static function eq1(lhs : system.Char, rhs : Int) : system.Boolean; - @:op(A == B) public static function eq2(lhs : Int, rhs : system.Char) : system.Boolean; - @:op(A == B) public static function eq3(lhs : system.Char, rhs : Float) : system.Boolean; - @:op(A == B) public static function eq4(lhs : Float, rhs : system.Char) : system.Boolean; - - @:op(A == B) public static function eq5(lhs : system.Char, rhs : system.Char) : system.Boolean; - @:op(A == B) public static function eq6(lhs : system.Char, rhs : system.Byte) : system.Boolean; - @:op(A == B) public static function eq7(lhs : system.Char, rhs : system.Int16) : system.Boolean; - @:op(A == B) public static function eq8(lhs : system.Char, rhs : system.Int32) : system.Boolean; - @:op(A == B) public static function eq9(lhs : system.Char, rhs : system.Int64) : system.Boolean; - @:op(A == B) public static function eq10(lhs : system.Char, rhs : system.SByte) : system.Boolean; - @:op(A == B) public static function eq11(lhs : system.Char, rhs : system.UInt16) : system.Boolean; - @:op(A == B) public static function eq12(lhs : system.Char, rhs : system.UInt32) : system.Boolean; - @:op(A == B) public static function eq13(lhs : system.Char, rhs : system.UInt64) : system.Boolean; - @:op(A == B) public static function eq14(lhs : system.Char, rhs : system.Single) : system.Boolean; - @:op(A == B) public static function eq15(lhs : system.Char, rhs : system.Double) : system.Boolean; - - @:op(A == B) public static inline function eq16(lhs : system.Char, rhs : String) : system.Boolean return lhs.ToHaxeInt() == rhs.charCodeAt(0); - @:op(A == B) public static inline function eq17(lhs : system.Char, rhs : system.CsString) : system.Boolean return lhs.ToHaxeInt() == rhs.get_Chars(0); - - - @:op(A != B) public static function neq1(lhs : system.Char, rhs : Int) : system.Boolean; - @:op(A != B) public static function neq2(lhs : Int, rhs : system.Char) : system.Boolean; - @:op(A != B) public static function neq3(lhs : system.Char, rhs : Float) : system.Boolean; - @:op(A != B) public static function neq4(lhs : Float, rhs : system.Char) : system.Boolean; - - @:op(A != B) public static function neq5(lhs : system.Char, rhs : system.Char) : system.Boolean; - @:op(A != B) public static function neq6(lhs : system.Char, rhs : system.Byte) : system.Boolean; - @:op(A != B) public static function neq7(lhs : system.Char, rhs : system.Int16) : system.Boolean; - @:op(A != B) public static function neq8(lhs : system.Char, rhs : system.Int32) : system.Boolean; - @:op(A != B) public static function neq9(lhs : system.Char, rhs : system.Int64) : system.Boolean; - @:op(A != B) public static function neq10(lhs : system.Char, rhs : system.SByte) : system.Boolean; - @:op(A != B) public static function neq11(lhs : system.Char, rhs : system.UInt16) : system.Boolean; - @:op(A != B) public static function neq12(lhs : system.Char, rhs : system.UInt32) : system.Boolean; - @:op(A != B) public static function neq13(lhs : system.Char, rhs : system.UInt64) : system.Boolean; - @:op(A != B) public static function neq14(lhs : system.Char, rhs : system.Single) : system.Boolean; - @:op(A != B) public static function neq15(lhs : system.Char, rhs : system.Double) : system.Boolean; - - @:op(A != B) public static inline function neq16(lhs : system.Char, rhs : String) : system.Boolean return lhs.ToHaxeInt() != rhs.charCodeAt(0); - @:op(A != B) public static inline function neq17(lhs : system.Char, rhs : system.CsString) : system.Boolean return lhs.ToHaxeInt() != rhs.get_Chars(0); - - - @:op(A & B) public static function and1(lhs : system.Char, rhs : Int) : system.Int32; - @:op(A & B) public static function and2(lhs : Int, rhs : system.Char) : system.Int32; - - @:op(A & B) public static function and5(lhs : system.Char, rhs : system.Char) : system.Int32; - @:op(A & B) public static function and6(lhs : system.Char, rhs : system.Byte) : system.Int32; - @:op(A & B) public static function and7(lhs : system.Char, rhs : system.Int16) : system.Int32; - @:op(A & B) public static function and8(lhs : system.Char, rhs : system.Int32) : system.Int32; - @:op(A & B) public static function and9(lhs : system.Char, rhs : system.Int64) : system.Int64; - @:op(A & B) public static function and10(lhs : system.Char, rhs : system.SByte) : system.Int32; - @:op(A & B) public static function and11(lhs : system.Char, rhs : system.UInt16) : system.Int32; - @:op(A & B) public static function and12(lhs : system.Char, rhs : system.UInt32) : system.UInt32; - @:op(A & B) public static function and13(lhs : system.Char, rhs : system.UInt64) : system.UInt64; - - - @:op(A | B) public static function or1(lhs : system.Char, rhs : Int) : system.Int32; - @:op(A | B) public static function or2(lhs : Int, rhs : system.Char) : system.Int32; - - @:op(A | B) public static function or5(lhs : system.Char, rhs : system.Char) : system.Int32; - @:op(A | B) public static function or6(lhs : system.Char, rhs : system.Byte) : system.Int32; - @:op(A | B) public static function or7(lhs : system.Char, rhs : system.Int16) : system.Int32; - @:op(A | B) public static function or8(lhs : system.Char, rhs : system.Int32) : system.Int32; - @:op(A | B) public static function or9(lhs : system.Char, rhs : system.Int64) : system.Int64; - @:op(A | B) public static function or10(lhs : system.Char, rhs : system.SByte) : system.Int32; - @:op(A | B) public static function or11(lhs : system.Char, rhs : system.UInt16) : system.Int32; - @:op(A | B) public static function or12(lhs : system.Char, rhs : system.UInt32) : system.UInt32; - @:op(A | B) public static function or13(lhs : system.Char, rhs : system.UInt64) : system.UInt64; - - - @:op(A ^ B) public static function xor1(lhs : system.Char, rhs : Int) : system.Int32; - @:op(A ^ B) public static function xor2(lhs : Int, rhs : system.Char) : system.Int32; - - @:op(A ^ B) public static function xor5(lhs : system.Char, rhs : system.Char) : system.Int32; - @:op(A ^ B) public static function xor6(lhs : system.Char, rhs : system.Byte) : system.Int32; - @:op(A ^ B) public static function xor7(lhs : system.Char, rhs : system.Int16) : system.Int32; - @:op(A ^ B) public static function xor8(lhs : system.Char, rhs : system.Int32) : system.Int32; - @:op(A ^ B) public static function xor9(lhs : system.Char, rhs : system.Int64) : system.Int64; - @:op(A ^ B) public static function xor10(lhs : system.Char, rhs : system.SByte) : system.Int32; - @:op(A ^ B) public static function xor11(lhs : system.Char, rhs : system.UInt16) : system.Int32; - @:op(A ^ B) public static function xor12(lhs : system.Char, rhs : system.UInt32) : system.UInt32; - @:op(A ^ B) public static function xor13(lhs : system.Char, rhs : system.UInt64) : system.UInt64; -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/CharArray.hx b/Phase/Mscorlib/system/CharArray.hx deleted file mode 100644 index 8c9c96637..000000000 --- a/Phase/Mscorlib/system/CharArray.hx +++ /dev/null @@ -1,21 +0,0 @@ -package system; - -abstract CharArray(js.html.Uint16Array) -{ - public inline function new(length:Int32) this = new js.html.Uint16Array(length.ToHaxeInt()); - - @:from public static inline function fromArray(a:Array):CharArray return cast new js.html.Uint16Array(untyped a); - @:from public static inline function fromFixedArray(a:FixedArray):CharArray return cast new js.html.Uint16Array(untyped a.ToHaxeArray()); - - public var Length(get, never):Int32; - public inline function get_Length() : Int32 return this.length; - - @:op([]) public inline function get(index:Int32):Char return this[index.ToHaxeInt()]; - @:op([]) public inline function set(index:Int32, val:Char):Char return this[index.ToHaxeInt()] = val.ToHaxeInt(); - - public inline function iterator() : Iterator return new CharArrayIterator(this); - - public inline function ToEnumerable() : system.collections.generic.IEnumerable return new CharArrayEnumerable(this); - - public static inline function empty(size:Int32) return new CharArray(size); -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/CharArrayEnumerable.hx b/Phase/Mscorlib/system/CharArrayEnumerable.hx deleted file mode 100644 index 29ba7547a..000000000 --- a/Phase/Mscorlib/system/CharArrayEnumerable.hx +++ /dev/null @@ -1,18 +0,0 @@ -package system; - -import system.collections.generic.IEnumerable; -import system.collections.generic.IEnumerator; - -class CharArrayEnumerable implements IEnumerable -{ - private var _array:js.html.Uint16Array; - public function new(array:js.html.Uint16Array) - { - _array= array; - } - - public function GetEnumerator() : IEnumerator - { - return new CharArrayEnumerator(_array); - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/CharArrayEnumerator.hx b/Phase/Mscorlib/system/CharArrayEnumerator.hx deleted file mode 100644 index 1d55a679a..000000000 --- a/Phase/Mscorlib/system/CharArrayEnumerator.hx +++ /dev/null @@ -1,29 +0,0 @@ -package system; - -import system.collections.generic.IEnumerator; - -class CharArrayEnumerator implements IEnumerator -{ - private var _array:js.html.Uint16Array; - private var _i:Int; - - public function new(array:js.html.Uint16Array) - { - _array = array; - _i = -1; - } - - public var Current(get, never):Char; - public function get_Current() : Char return _array[_i]; - public function MoveNext() : Bool - { - if(_i >= _array.length - 1) return false; - _i++; - return true; - - } - public function Reset():Void - { - _i = -1; - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/CharArrayIterator.hx b/Phase/Mscorlib/system/CharArrayIterator.hx deleted file mode 100644 index 86e3e0a8b..000000000 --- a/Phase/Mscorlib/system/CharArrayIterator.hx +++ /dev/null @@ -1,19 +0,0 @@ -package system; - -class CharArrayIterator -{ - private var _array:js.html.Uint16Array; - private var _i:Int; - - public function new(array:js.html.Uint16Array) - { - _array = array; - _i = 0; - } - - public function hasNext() return _i < _array.length; - public function next() : Char - { - return _array[_i++]; - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/Comparison.hx b/Phase/Mscorlib/system/Comparison.hx deleted file mode 100644 index 0765484d4..000000000 --- a/Phase/Mscorlib/system/Comparison.hx +++ /dev/null @@ -1,3 +0,0 @@ -package system; - -typedef Comparison = T->T-> system.Int32; \ No newline at end of file diff --git a/Phase/Mscorlib/system/Convert.hx b/Phase/Mscorlib/system/Convert.hx deleted file mode 100644 index 5b39f3510..000000000 --- a/Phase/Mscorlib/system/Convert.hx +++ /dev/null @@ -1,649 +0,0 @@ -package system; - -import js.html.ArrayBuffer; -import js.html.Int16Array; -import js.html.Int8Array; -import js.html.Int32Array; -import js.html.Uint16Array; -import js.html.Uint32Array; -import js.html.Uint8Array; -import js.html.Float32Array; -import js.html.Float64Array; - -class Convert -{ - private static var _conversionBuffer:ArrayBuffer = new ArrayBuffer(8); - private static var _int8Buffer = new Int8Array(_conversionBuffer); - private static var _uint8Buffer = new Uint8Array(_conversionBuffer); - private static var _int16Buffer = new Int16Array(_conversionBuffer); - private static var _uint16Buffer = new Uint16Array(_conversionBuffer); - private static var _int32Buffer = new Int32Array(_conversionBuffer); - private static var _uint32Buffer = new Uint32Array(_conversionBuffer); - private static var _float32Buffer = new Float32Array(_conversionBuffer); - private static var _float64Buffer = new Float64Array(_conversionBuffer); - - public static function ToInt8(v:Int) : Int - { - _int32Buffer[0] = v; - return _int8Buffer[0]; - } - public static function ToUInt8(v:Int) : Int - { - _int32Buffer[0] = v; - return _uint8Buffer[0]; - } - public static function ToInt16(v:Int) : Int - { - _int32Buffer[0] = v; - return _int16Buffer[0]; - } - public static function ToUInt16(v:Int) : Int - { - _int32Buffer[0] = v; - return _uint16Buffer[0]; - } - public static function ToUInt32(v:Int) : Int - { - _int32Buffer[0] = v; - return _uint32Buffer[0]; - } - public static function ToInt64(v:Int) : Int - { - return v; - } - public static function ToUInt64(v:Int) : Int - { - return ToUInt32(v); - } - public static function ToInt32(v:Int) : Int - { - _uint32Buffer[0] = v; - return _int32Buffer[0]; - } - - public static function ToHashCode_Single(v:system.Single) : system.Int32 - { - _float32Buffer[0] = v.ToHaxeFloat(); - return _int32Buffer[0]; - } - - public static function ToHashCode_Double(v:system.Double) : system.Int32 - { - _float64Buffer[0] = v.ToHaxeFloat(); - return _int32Buffer[0] ^ _int32Buffer[1]; - } - - public static inline function ToBoolean_Byte(v:system.Byte) : system.Boolean - { - return v != 0; - } - public static inline function ToChar_Byte(v:system.Byte) : system.Char - { - return v.ToHaxeInt(); - } - public static inline function ToSByte_Byte(v:system.Byte) : system.SByte - { - return ToInt8(v.ToHaxeInt()); - } - public static inline function ToByte_Byte(v:system.Byte) : system.Byte - { - return v; - } - public static inline function ToInt16_Byte(v:system.Byte) : system.Int16 - { - return v.ToHaxeInt(); - } - public static inline function ToUInt16_Byte(v:system.Byte) : system.UInt16 - { - return v.ToHaxeInt(); - } - public static inline function ToInt32_Byte(v:system.Byte) : system.Int32 - { - return v.ToHaxeInt(); - } - public static inline function ToUInt32_Byte(v:system.Byte) : system.UInt32 - { - return v.ToHaxeInt(); - } - public static inline function ToInt64_Byte(v:system.Byte) : system.Int64 - { - return v.ToHaxeInt(); - } - public static inline function ToUInt64_Byte(v:system.Byte) : system.UInt64 - { - return v.ToHaxeInt(); - } - public static inline function ToSingle_Byte(v:system.Byte) : system.Single - { - return v.ToHaxeInt(); - } - public static inline function ToDouble_Byte(v:system.Byte) : system.Double - { - return v.ToHaxeInt(); - } - - - public static inline function ToBoolean_Char(v:system.Char) : system.Boolean - { - throw new InvalidCastException("cannot cast system.Char to system.Boolean"); - } - public static inline function ToChar_Char(v:system.Char) : system.Char - { - return v; - } - public static inline function ToByte_Char(v:system.Char) : system.Byte - { - return ToUInt8(v.ToHaxeInt()); - } - public static inline function ToSByte_Char(v:system.Char) : system.SByte - { - return ToInt8(v.ToHaxeInt()); - } - public static inline function ToInt16_Char(v:system.Char) : system.Int16 - { - return ToInt16(v.ToHaxeInt()); - } - public static inline function ToUInt16_Char(v:system.Char) : system.UInt16 - { - return v.ToHaxeInt(); - } - public static inline function ToInt32_Char(v:system.Char) : system.Int32 - { - return v.ToHaxeInt(); - } - public static inline function ToUInt32_Char(v:system.Char) : system.UInt32 - { - return v.ToHaxeInt(); - } - public static inline function ToInt64_Char(v:system.Char) : system.Int64 - { - return v.ToHaxeInt(); - } - public static inline function ToUInt64_Char(v:system.Char) : system.UInt64 - { - return v.ToHaxeInt(); - } - public static function ToSingle_Char(v:system.Byte) : system.Single - { - throw new InvalidCastException("cannot cast system.Char to system.Single"); - } - public static function ToDouble_Char(v:system.Byte) : system.Double - { - throw new InvalidCastException("cannot cast system.Char to system.Double"); - } - - - public static inline function ToBoolean_Double(v:system.Double) : system.Boolean - { - return v != 0; - } - public static inline function ToChar_Double(v:system.Double) : system.Char - { - throw new InvalidCastException("cannot cast system.Double to system.Char"); - } - public static inline function ToByte_Double(v:system.Double) : system.Byte - { - return ToUInt8(ToInt32_Double(v).ToHaxeInt()); - } - public static inline function ToSByte_Double(v:system.Double) : system.SByte - { - return ToInt8(ToInt32_Double(v).ToHaxeInt()); - } - public static inline function ToInt16_Double(v:system.Double) : system.Int16 - { - return ToInt16(ToInt32_Double(v).ToHaxeInt()); - } - public static inline function ToUInt16_Double(v:system.Double) : system.UInt16 - { - return ToUInt16(ToInt32_Double(v).ToHaxeInt()); - } - public static function ToInt32_Double(v:system.Double) : system.Int32 - { - if (v >= 0) - { - if (v < 2147483647.5) - { - return Std.int(v.ToHaxeFloat()); - //var result = Std.int(v.ToHaxeFloat()); - //var dif = v - result; - //if (dif > 0.5 || dif == 0.5 && (result & 1) != 0) result++; - //return result; - } - } - else - { - if (v >= -2147483648.5) - { - return Std.int(v.ToHaxeFloat()); - // var result = Std.int(v.ToHaxeFloat()); - // var dif = v - result; - // if (dif < -0.5 || dif == -0.5 && (result & 1) != 0) result--; - // return result; - } - } - return Std.int(v.ToHaxeFloat()); - // throw new OverflowException("Value was either too large or too small for a Int32."); - } - public static inline function ToUInt32_Double(v:system.Double) : system.UInt32 - { - return ToUInt32(ToInt32_Double(v).ToHaxeInt()); - } - public static inline function ToInt64_Double(v:system.Double) : system.Int64 - { - // TODO: ensure that upper bits are not lost - return Std.int(v.ToHaxeFloat()); - } - public static inline function ToUInt64_Double(v:system.Double) : system.UInt64 - { - return ToInt64(ToInt64_Double(v).ToHaxeInt()); - } - public static inline function ToSingle_Double(v:system.Double) : system.Single - { - // TODO: double to float truncation - return v.ToHaxeFloat(); - } - public static inline function ToDouble_Double(v:system.Double) : system.Double - { - return v; - } - - - public static inline function ToBoolean_Int16(v:system.Int16) : system.Boolean - { - return v != 0; - } - public static inline function ToChar_Int16(v:system.Int16) : system.Char - { - return ToUInt16(v.ToHaxeInt()); - } - public static inline function ToSByte_Int16(v:system.Int16) : system.SByte - { - return ToInt8(v.ToHaxeInt()); - } - public static inline function ToByte_Int16(v:system.Int16) : system.Byte - { - return ToUInt8(v.ToHaxeInt()); - } - public static inline function ToInt16_Int16(v:system.Int16) : system.Int16 - { - return v; - } - public static inline function ToUInt16_Int16(v:system.Int16) : system.UInt16 - { - return ToUInt16(v.ToHaxeInt()); - } - public static inline function ToInt32_Int16(v:system.Int16) : system.Int32 - { - return v.ToHaxeInt(); - } - public static inline function ToUInt32_Int16(v:system.Int16) : system.UInt32 - { - return ToUInt32(v.ToHaxeInt()); - } - public static inline function ToInt64_Int16(v:system.Int16) : system.Int64 - { - return v.ToHaxeInt(); - } - public static inline function ToUInt64_Int16(v:system.Int16) : system.UInt64 - { - return ToUInt32(v.ToHaxeInt()); - } - public static inline function ToSingle_Int16(v:system.Int16) : system.Single - { - return v.ToHaxeInt(); - } - public static inline function ToDouble_Int16(v:system.Int16) : system.Double - { - return v.ToHaxeInt(); - } - - - public static inline function ToBoolean_Int32(v:system.Int32) : system.Boolean - { - return v != 0; - } - public static inline function ToChar_Int32(v:system.Int32) : system.Char - { - return ToUInt16(v.ToHaxeInt()); - } - public static inline function ToSByte_Int32(v:system.Int32) : system.SByte - { - return ToInt8(v.ToHaxeInt()); - } - public static inline function ToByte_Int32(v:system.Int32) : system.Byte - { - return ToUInt8(v.ToHaxeInt()); - } - public static inline function ToInt16_Int32(v:system.Int32) : system.Int16 - { - return ToInt16(v.ToHaxeInt()); - } - public static inline function ToUInt16_Int32(v:system.Int32) : system.UInt16 - { - return ToUInt16(v.ToHaxeInt()); - } - public static inline function ToInt32_Int32(v:system.Int32) : system.Int32 - { - return v; - } - public static inline function ToUInt32_Int32(v:system.Int32) : system.UInt32 - { - return ToUInt32(v.ToHaxeInt()); - } - public static inline function ToInt64_Int32(v:system.Int32) : system.Int64 - { - return v.ToHaxeInt(); - } - public static inline function ToUInt64_Int32(v:system.Int32) : system.UInt64 - { - return ToUInt32(v.ToHaxeInt()); - } - public static inline function ToSingle_Int32(v:system.Int32) : system.Single - { - return v.ToHaxeInt(); - } - public static inline function ToDouble_Int32(v:system.Int32) : system.Double - { - return v.ToHaxeInt(); - } - - - public static inline function ToBoolean_Int64(v:system.Int64) : system.Boolean - { - return v != 0; - } - public static inline function ToChar_Int64(v:system.Int64) : system.Char - { - return ToUInt16(v.ToHaxeInt()); - } - public static inline function ToSByte_Int64(v:system.Int64) : system.SByte - { - return ToInt8(v.ToHaxeInt()); - } - public static inline function ToByte_Int64(v:system.Int64) : system.Byte - { - return ToUInt8(v.ToHaxeInt()); - } - public static inline function ToInt16_Int64(v:system.Int64) : system.Int16 - { - return ToInt16(v.ToHaxeInt()); - } - public static inline function ToUInt16_Int64(v:system.Int64) : system.UInt16 - { - return ToUInt16(v.ToHaxeInt()); - } - public static inline function ToInt32_Int64(v:system.Int64) : system.Int32 - { - return ToInt32(v.ToHaxeInt()); - } - public static inline function ToUInt32_Int64(v:system.Int64) : system.UInt32 - { - return ToUInt32(v.ToHaxeInt()); - } - public static inline function ToInt64_Int64(v:system.Int64) : system.Int64 - { - return v; - } - public static inline function ToUInt64_Int64(v:system.Int64) : system.UInt64 - { - return ToUInt32(v.ToHaxeInt()); - } - public static inline function ToSingle_Int64(v:system.Int64) : system.Single - { - return v.ToHaxeInt(); - } - public static inline function ToDouble_Int64(v:system.Int64) : system.Double - { - return v.ToHaxeInt(); - } - - - public static inline function ToBoolean_SByte(v:system.SByte) : system.Boolean - { - return v != 0; - } - public static inline function ToChar_SByte(v:system.SByte) : system.Char - { - return ToUInt16(v.ToHaxeInt()); - } - public static inline function ToSByte_SByte(v:system.SByte) : system.SByte - { - return v; - } - public static inline function ToByte_SByte(v:system.SByte) : system.Byte - { - return ToUInt8(v.ToHaxeInt()); - } - public static inline function ToInt16_SByte(v:system.SByte) : system.Int16 - { - return v.ToHaxeInt(); - } - public static inline function ToUInt16_SByte(v:system.SByte) : system.UInt16 - { - return ToUInt16(v.ToHaxeInt()); - } - public static inline function ToInt32_SByte(v:system.SByte) : system.Int32 - { - return v.ToHaxeInt(); - } - public static inline function ToUInt32_SByte(v:system.SByte) : system.UInt32 - { - return ToUInt32(v.ToHaxeInt()); - } - public static inline function ToInt64_SByte(v:system.SByte) : system.Int64 - { - return v.ToHaxeInt(); - } - public static inline function ToUInt64_SByte(v:system.SByte) : system.UInt64 - { - return ToUInt32(v.ToHaxeInt()); - } - public static inline function ToSingle_SByte(v:system.SByte) : system.Single - { - return v.ToHaxeInt(); - } - public static inline function ToDouble_SByte(v:system.SByte) : system.Double - { - return v.ToHaxeInt(); - } - - public static inline function ToBoolean_Single(v:system.Single) : system.Boolean - { - return v != 0; - } - public static inline function ToChar_Single(v:system.Single) : system.Char - { - throw new InvalidCastException("cannot cast system.Single to system.Char"); - } - public static inline function ToByte_Single(v:system.Single) : system.Byte - { - return ToUInt8(ToInt32_Single(v).ToHaxeInt()); - } - public static inline function ToSByte_Single(v:system.Single) : system.SByte - { - return ToInt8(ToInt32_Single(v).ToHaxeInt()); - } - public static inline function ToInt16_Single(v:system.Single) : system.Int16 - { - return ToInt16(ToInt32_Single(v).ToHaxeInt()); - } - public static inline function ToUInt16_Single(v:system.Single) : system.UInt16 - { - return ToUInt16(ToInt32_Single(v).ToHaxeInt()); - } - public static function ToInt32_Single(v:system.Single) : system.Int32 - { - return ToInt32_Double(v); - } - public static inline function ToUInt32_Single(v:system.Single) : system.UInt32 - { - return ToUInt32(ToInt32_Single(v).ToHaxeInt()); - } - public static inline function ToInt64_Single(v:system.Single) : system.Int64 - { - // TODO: ensure that upper bits are not lost - return Std.int(v.ToHaxeFloat()); - } - public static inline function ToUInt64_Single(v:system.Single) : system.UInt64 - { - return ToInt64(ToInt64_Single(v).ToHaxeInt()); - } - public static inline function ToSingle_Single(v:system.Single) : system.Single - { - return v; - } - public static inline function ToDouble_Single(v:system.Single) : system.Double - { - return v.ToHaxeFloat(); - } - - - public static inline function ToBoolean_UInt16(v:system.UInt16) : system.Boolean - { - return v != 0; - } - public static inline function ToChar_UInt16(v:system.UInt16) : system.Char - { - return v.ToHaxeInt(); - } - public static inline function ToSByte_UInt16(v:system.UInt16) : system.SByte - { - return ToInt8(v.ToHaxeInt()); - } - public static inline function ToByte_UInt16(v:system.UInt16) : system.Byte - { - return ToUInt8(v.ToHaxeInt()); - } - public static inline function ToInt16_UInt16(v:system.UInt16) : system.Int16 - { - return ToInt16(v.ToHaxeInt()); - } - public static inline function ToUInt16_UInt16(v:system.UInt16) : system.UInt16 - { - return v; - } - public static inline function ToInt32_UInt16(v:system.UInt16) : system.Int32 - { - return v.ToHaxeInt(); - } - public static inline function ToUInt32_UInt16(v:system.UInt16) : system.UInt32 - { - return v.ToHaxeInt(); - } - public static inline function ToInt64_UInt16(v:system.UInt16) : system.Int64 - { - return v.ToHaxeInt(); - } - public static inline function ToUInt64_UInt16(v:system.UInt16) : system.UInt64 - { - return v.ToHaxeInt(); - } - public static inline function ToSingle_UInt16(v:system.UInt16) : system.Single - { - return v.ToHaxeInt(); - } - public static inline function ToDouble_UInt16(v:system.UInt16) : system.Double - { - return v.ToHaxeInt(); - } - - - public static inline function ToBoolean_UInt32(v:system.UInt32) : system.Boolean - { - return v != 0; - } - public static inline function ToChar_UInt32(v:system.UInt32) : system.Char - { - return ToUInt16(v.ToHaxeInt()); - } - public static inline function ToSByte_UInt32(v:system.UInt32) : system.SByte - { - return ToInt8(v.ToHaxeInt()); - } - public static inline function ToByte_UInt32(v:system.UInt32) : system.Byte - { - return ToUInt8(v.ToHaxeInt()); - } - public static inline function ToInt16_UInt32(v:system.UInt32) : system.Int16 - { - return ToInt16(v.ToHaxeInt()); - } - public static inline function ToUInt16_UInt32(v:system.UInt32) : system.UInt16 - { - return ToUInt16(v.ToHaxeInt()); - } - public static inline function ToInt32_UInt32(v:system.UInt32) : system.Int32 - { - return ToInt32(v.ToHaxeInt()); - } - public static inline function ToUInt32_UInt32(v:system.UInt32) : system.UInt32 - { - return v; - } - public static inline function ToInt64_UInt32(v:system.UInt32) : system.Int64 - { - return v.ToHaxeInt(); - } - public static inline function ToUInt64_UInt32(v:system.UInt32) : system.UInt64 - { - return v.ToHaxeInt(); - } - public static inline function ToSingle_UInt32(v:system.UInt32) : system.Single - { - return v.ToHaxeInt(); - } - public static inline function ToDouble_UInt32(v:system.UInt32) : system.Double - { - return v.ToHaxeInt(); - } - - - public static inline function ToBoolean_UInt64(v:system.UInt64) : system.Boolean - { - return v != 0; - } - public static inline function ToChar_UInt64(v:system.UInt64) : system.Char - { - return ToUInt16(v.ToHaxeInt()); - } - public static inline function ToSByte_UInt64(v:system.UInt64) : system.SByte - { - return ToInt8(v.ToHaxeInt()); - } - public static inline function ToByte_UInt64(v:system.UInt64) : system.Byte - { - return ToUInt8(v.ToHaxeInt()); - } - public static inline function ToInt16_UInt64(v:system.UInt64) : system.Int16 - { - return ToInt16(v.ToHaxeInt()); - } - public static inline function ToUInt16_UInt64(v:system.UInt64) : system.UInt16 - { - return ToUInt16(v.ToHaxeInt()); - } - public static inline function ToInt32_UInt64(v:system.UInt64) : system.Int32 - { - return ToInt32(v.ToHaxeInt()); - } - public static inline function ToUInt32_UInt64(v:system.UInt64) : system.UInt32 - { - return ToUInt32(v.ToHaxeInt()); - } - public static inline function ToInt64_UInt64(v:system.UInt64) : system.Int64 - { - return ToInt32(v.ToHaxeInt()); - } - public static inline function ToUInt64_UInt64(v:system.UInt64) : system.UInt64 - { - return v; - } - public static inline function ToSingle_UInt64(v:system.UInt64) : system.Single - { - return v.ToHaxeInt(); - } - public static inline function ToDouble_UInt64(v:system.UInt64) : system.Double - { - return v.ToHaxeInt(); - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/CsMath.hx b/Phase/Mscorlib/system/CsMath.hx deleted file mode 100644 index 3c428ded8..000000000 --- a/Phase/Mscorlib/system/CsMath.hx +++ /dev/null @@ -1,34 +0,0 @@ -package system; - -class CsMath -{ - public static inline function Min_Byte_Byte(a:Byte, b:Byte) : Byte return untyped Math.min(a.ToHaxeInt(), b.ToHaxeInt()); - public static inline function Min_Int32_Int32(a:Int32, b:Int32) : Int32 return untyped Math.min(a.ToHaxeInt(), b.ToHaxeInt()); - public static inline function Min_Double_Double(a:Double, b:Double) : Double return untyped Math.min(a.ToHaxeFloat(), b.ToHaxeFloat()); - public static inline function Max_Int32_Int32(a:Int32, b:Int32) : Int32 return untyped Math.max(a.ToHaxeInt(), b.ToHaxeInt()); - public static inline function Max_Byte_Byte(a:Byte, b:Byte) : Byte return untyped Math.max(a.ToHaxeInt(), b.ToHaxeInt()); - public static inline function Min_Int64_Int64(a:system.Int64, b:system.Int64) : system.Int64 return a.ToHaxeInt() < b.ToHaxeInt() ? a : b; - public static inline function Max_Single_Single(a:Single, b:Single) : Single return Math.max(a.ToHaxeFloat(), b.ToHaxeFloat()); - public static inline function Max_Double_Double(a:Double, b:Double) : Double return Math.max(a.ToHaxeFloat(), b.ToHaxeFloat()); - public static inline function Min_Single_Single(a:Single, b:Single) : Single return Math.min(a.ToHaxeFloat(), b.ToHaxeFloat()); - public static inline function Abs_Single(a:Single) : Single return Math.abs(a.ToHaxeFloat()); - public static inline function Abs_Int32(a:Int32) : Int32 return untyped Math.abs(a.ToHaxeInt()); - public static inline function Round_Double(a:Double) : Double return Math.round(a.ToHaxeFloat()); - public static inline function Round_Single(a:Single) : Single return Math.round(a.ToHaxeFloat()); - public static inline function Sin(a:Double) : Double return Math.sin(a.ToHaxeFloat()); - public static inline function Cos(a:Double) : Double return Math.cos(a.ToHaxeFloat()); - public static inline function Pow(v:Double, exp:Double) : Double return Math.pow(v.ToHaxeFloat(), exp.ToHaxeFloat()); - public static inline function Ceiling_Single(v:Single) : Single return Math.ceil(v.ToHaxeFloat()); - public static inline function Ceiling_Double(v:Double) : Double return Math.ceil(v.ToHaxeFloat()); - public static inline function Floor_Single(v:Single) : Single return Math.floor(v.ToHaxeFloat()); - public static inline function Floor_Double(v:Double) : Double return Math.floor(v.ToHaxeFloat()); - public static inline function Sign_Single(v:Single) : Single return Math.floor(v.ToHaxeFloat()); - public static inline function Sign_Double(v:Double) : system.Int32 return if( v < 0 ) -1 else if (v > 0) 1 else 0; - public static inline function Log_Double_Double(a:Double, newBase:Double) : system.Double return Math.log(a.ToHaxeFloat()) / Math.log(newBase.ToHaxeFloat()); - public static inline function Log10(a:Double) : system.Double return Log_Double_Double(a, 10); - public static inline function Abs_Double(v:Double) : Double return Math.abs(v.ToHaxeFloat()); - public static inline function Sqrt(v:Double) : Double return Math.sqrt(v.ToHaxeFloat()); - public static inline function Exp(v:Double) : Double return Math.exp(v.ToHaxeFloat()); - public static inline function Asin(v:Double) : Double return Math.asin(v.ToHaxeFloat()); - -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/CsString.hx b/Phase/Mscorlib/system/CsString.hx deleted file mode 100644 index 7c6fc3e69..000000000 --- a/Phase/Mscorlib/system/CsString.hx +++ /dev/null @@ -1,359 +0,0 @@ -package system; - -abstract CsString(String) from String to String -{ - public inline function new(s:String) this = s; - - public inline function ToHaxeString(): String return this; - - public var Length(get, never):Int32; - public inline function get_Length() :Int32 return this.length; - public inline function get_Chars(i:Int32) : Char return Char.fromCode(this.charCodeAt(i.ToHaxeInt())); - - public inline function Substring_Int32(start:Int32) : CsString return this.substr(start.ToHaxeInt()); - public inline function Substring_Int32_Int32(start:Int32, length:Int32) : CsString return this.substr(start.ToHaxeInt(), length.ToHaxeInt()); - public inline function Replace_CsString_CsString(from:CsString, with :CsString) : CsString return StringTools.replace(this, from.ToHaxeString(), with.ToHaxeString() ); - public inline function IndexOf_Char(ch:Char) : Int32 return this.indexOf(ch.ToString()); - public inline function LastIndexOf_Char(ch:Char) : Int32 return this.lastIndexOf(ch.ToString()); - public inline function LastIndexOf_CsString(ch:CsString) : Int32 return this.lastIndexOf(ch.ToHaxeString()); - public inline function EndsWith_CsString(end:CsString) : Boolean return StringTools.endsWith(this, end.ToHaxeString()); - public inline function Contains(s:CsString) : Boolean return this.indexOf(s.ToHaxeString()) != -1; - public inline function Trim() : CsString return StringTools.trim(this); - - public function Split_CharArray(chars:Array) : Array - { - var strings = new Array(); - var startPos = 0; - for (i in 0 ... this.length) - { - var cc = this.charCodeAt(i); - if (chars.indexOf(cc) >= 0) - { - var endPos = i; - if (endPos < startPos) - { - strings.push(""); - } - else - { - strings.push(this.substring(startPos, endPos)); - } - startPos = i + 1; - } - } - if(startPos < this.length) - { - strings.push(this.substring(startPos, this.length)); - } - return strings; - } - - public static inline function IsNullOrEmpty(s:CsString) : Boolean return (s == null || s.Length == 0); - public static inline function IsNullOrWhiteSpace(s:CsString) : Boolean return (s == null || s.Trim().Length == 0); - public static inline function FromCharCode(s:Int32) : CsString return String.fromCharCode(s.ToHaxeInt()); - public static function Format(format:CsString, args:FixedArray) : CsString - { - var sb = new StringBuf(); - - // based on https://github.com/dotnet/coreclr/blob/master/src/mscorlib/shared/System/Text/StringBuilder.cs#L1362 - var pos : Int32 = 0; - var len : Int32 = format.Length; - var ch : Char = 0; - var unescapedItemFormat:StringBuf = null; - - while(true) - { - while(pos < len) - { - ch = format.get_Chars(pos); - pos++; - - // Is it a closing brace? - if(ch == "}") - { - // Check next character (if there is one) to see if it is escaped. eg }} - if(pos < len && format.get_Chars(pos) == "}") - { - pos++; - } - else - { - // Otherwise treat it as an error (Mismatched closing brace) - throw new FormatException().FormatException_CsString("invalid format"); - } - } - - // Is it a opening brace? - if(ch == "{") - { - // Check next character (if there is one) to see if it is escaped. eg {{ - if(pos < len && format.get_Chars(pos) == "{") - { - pos++; - } - else - { - // Otherwise treat it as the opening brace of an Argument Hole. - pos--; - break; - } - } - // If it neither then treat the character as just text. - sb.addChar(ch.ToHaxeInt()); - } - - // - // Start of parsing of Argument Hole. - // Argument Hole ::= { Index (, WS* Alignment WS*)? (: Formatting)? } - // - if (pos == len) break; - - // - // Start of parsing required Index parameter. - // Index ::= ('0'-'9')+ WS* - // - pos++; - // If reached end of text then error (Unexpected end of text) - // or character is not a digit then error (Unexpected Character) - if (pos == len || (ch = format.get_Chars(pos)) < '0' || ch > '9') - { - throw new FormatException().FormatException_CsString("invalid format"); - } - - var index:Int32 = 0; - do - { - index = index * 10 + ch - Char.fromString("0"); - pos++; - // If reached end of text then error (Unexpected end of text) - if (pos == len) - { - throw new FormatException().FormatException_CsString("invalid format"); - } - ch = format.get_Chars(pos); - // so long as character is digit and value of the index is less than 1000000 ( index limit ) - } while (ch >= '0' && ch <= '9' && index < 1000000); - - // If value of index is not within the range of the arguments passed in then error (Index out of range) - if (index >= args.Length) - { - throw new FormatException().FormatException_CsString("format specifiers out of range"); - } - - // Consume optional whitespace. - while (pos < len && (ch = format.get_Chars(pos)) == ' ') pos++; - // End of parsing index parameter. - - // - // Start of parsing of optional Alignment - // Alignment ::= comma WS* minus? ('0'-'9')+ WS* - // - var leftJustify = false; - var width:Int32 = 0; - // Is the character a comma, which indicates the start of alignment parameter. - if (ch == ',') - { - pos++; - - // Consume Optional whitespace - while (pos < len && format.get_Chars(pos) == ' ') pos++; - - // If reached the end of the text then error (Unexpected end of text) - if (pos == len) throw new FormatException().FormatException_CsString("invalid format"); - - // Is there a minus sign? - ch = format.get_Chars(pos); - if (ch == '-') - { - // Yes, then alignment is left justified. - leftJustify = true; - pos++; - // If reached end of text then error (Unexpected end of text) - if (pos == len) throw new FormatException().FormatException_CsString("invalid format"); - ch = format.get_Chars(pos); - } - - // If current character is not a digit then error (Unexpected character) - if (ch < '0' || ch > '9') throw new FormatException().FormatException_CsString("invalid format"); - // Parse alignment digits. - do - { - width = width * 10 + ch - Char.fromString('0'); - pos++; - // If reached end of text then error. (Unexpected end of text) - if (pos == len) throw new FormatException().FormatException_CsString("invalid format"); - ch = format.get_Chars(pos); - // So long a current character is a digit and the value of width is less than 100000 ( width limit ) - } while (ch >= '0' && ch <= '9' && width < 100000); - // end of parsing Argument Alignment - } - - // Consume optional whitespace - while (pos < len && (ch = format.get_Chars(pos)) == ' ') pos++; - - // - // Start of parsing of optional formatting parameter. - // - var arg = args[index]; - var itemFormat:CsString = null; - - // Is current character a colon? which indicates start of formatting parameter. - if (ch == ':') - { - pos++; - var startPos :Int32 = pos; - - while (true) - { - // If reached end of text then error. (Unexpected end of text) - if (pos == len) throw new FormatException().FormatException_CsString("invalid format"); - ch = format.get_Chars(pos); - pos++; - - // Is character a opening or closing brace? - if (ch == '}' || ch == '{') - { - if (ch == '{') - { - // Yes, is next character also a opening brace, then treat as escaped. eg {{ - if (pos < len && format.get_Chars(pos) == '{') - pos++; - else - // Error Argument Holes can not be nested. - throw new FormatException().FormatException_CsString("invalid format"); - } - else - { - // Yes, is next character also a closing brace, then treat as escaped. eg }} - if (pos < len && format.get_Chars(pos) == '}') - pos++; - else - { - // No, then treat it as the closing brace of an Arg Hole. - pos--; - break; - } - } - - // Reaching here means the brace has been escaped - // so we need to build up the format string in segments - if (unescapedItemFormat == null) - { - unescapedItemFormat = new StringBuf(); - } - unescapedItemFormat.addSub(format, startPos.ToHaxeInt(), (pos - startPos - 1).ToHaxeInt()); - startPos = pos; - } - } - - if (unescapedItemFormat == null || unescapedItemFormat.length == 0) - { - if (startPos != pos) - { - itemFormat = format.Substring_Int32_Int32(startPos, pos - startPos); - } - } - else - { - unescapedItemFormat.addSub(format, startPos.ToHaxeInt(), (pos - startPos).ToHaxeInt()); - itemFormat = unescapedItemFormat.toString(); - unescapedItemFormat = new StringBuf(); - } - } - - // If current character is not a closing brace then error. (Unexpected Character) - if (ch != '}') throw new FormatException().FormatException_CsString("invalid format"); - - // Construct the output for this arg hole. - pos++; - - // TODO: support actual formatting - var s:CsString = Std.string(arg); - - sb.add(s); - } - - return sb.toString(); - } - public static function Format_CsString_Object(format:CsString, arg:system.Object) : CsString - { - return Format(format, [arg]); - } - public static function Format_CsString_ObjectArray(format:CsString, args:system.FixedArray) : CsString - { - return Format(format, args); - } - public static function Join_CsString_IEnumerable_T1(separator:CsString, v:system.collections.generic.IEnumerable) : CsString - { - var s = ""; - - var enumerator = v.GetEnumerator(); - var first = true; - while(enumerator.MoveNext()) - { - if(!first) s += separator; - s += Std.string(enumerator.Current); - first = false; - } - - return s; - } - - public static function Join_CsString_IEnumerable_CsString(separator:CsString, v:system.collections.generic.IEnumerable) : CsString - { - var s = ""; - - var enumerator = v.GetEnumerator(); - var first = true; - while(enumerator.MoveNext()) - { - if(!first) s += separator; - s += enumerator.Current; - first = false; - } - - return s; - } - - public static function Join_CsString_CsStringArray(separator:CsString, args: system.FixedArray ) : CsString - { - var s = ""; - - var first = true; - for(x in args) - { - if(!first) s += separator; - s += Std.string(x); - first = false; - } - - return s; - } - - public inline function StartsWith_CsString(s:CsString) : Boolean return StringTools.startsWith(this, s); - - public inline function ToLower() :CsString return this.toLowerCase(); - public inline function ToUpper() :CsString return this.toUpperCase(); - - @:op(A + B) public static inline function add1(lhs : system.CsString, rhs : String) : system.CsString return lhs.ToHaxeString() + rhs; - @:op(A + B) public static inline function add2(lhs : String, rhs : system.CsString) : system.CsString return lhs + rhs.ToHaxeString(); - - @:op(A + B) public static inline function add3(lhs : system.CsString, rhs : Int) : system.CsString return lhs.ToHaxeString() + Std.string(rhs); - @:op(A + B) public static inline function add4(lhs : Int, rhs : system.CsString) : system.CsString return Std.string(lhs) + rhs.ToHaxeString(); - - @:op(A + B) public static inline function add5(lhs : system.CsString, rhs : Float) : system.CsString return lhs + Std.string(rhs); - @:op(A + B) public static inline function add6(lhs : Float, rhs : system.CsString) : system.CsString return Std.string(lhs) + rhs; - - @:op(A + B) public static inline function add7(lhs : system.CsString, rhs : system.Char) : system.CsString return lhs + rhs.ToString(); - @:op(A + B) public static inline function add8(lhs : system.CsString, rhs : system.Byte) : system.CsString return lhs + rhs.ToString(); - @:op(A + B) public static inline function add9(lhs : system.CsString, rhs : system.Int16) : system.CsString return lhs + rhs.ToString(); - @:op(A + B) public static inline function add10(lhs : system.CsString, rhs : system.Int32) : system.CsString return lhs + rhs.ToString(); - @:op(A + B) public static inline function add11(lhs : system.CsString, rhs : system.Int64) : system.CsString return lhs + rhs.ToString(); - @:op(A + B) public static inline function add12(lhs : system.CsString, rhs : system.SByte) : system.CsString return lhs + rhs.ToString(); - @:op(A + B) public static inline function add13(lhs : system.CsString, rhs : system.UInt16) : system.CsString return lhs + rhs.ToString(); - @:op(A + B) public static inline function add14(lhs : system.CsString, rhs : system.UInt32) : system.CsString return lhs + rhs.ToString(); - @:op(A + B) public static inline function add15(lhs : system.CsString, rhs : system.UInt64) : system.CsString return lhs + rhs.ToString(); - @:op(A + B) public static inline function add16(lhs : system.CsString, rhs : system.Single) : system.CsString return lhs + rhs.ToString(); - @:op(A + B) public static inline function add17(lhs : system.CsString, rhs : system.Double) : system.CsString return lhs + rhs.ToString(); - @:op(A + B) public static inline function add18(lhs : system.CsString, rhs : system.CsString) : system.CsString return lhs.ToHaxeString() + rhs.ToHaxeString(); -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/CsType.hx b/Phase/Mscorlib/system/CsType.hx deleted file mode 100644 index c2f4c9f1d..000000000 --- a/Phase/Mscorlib/system/CsType.hx +++ /dev/null @@ -1,19 +0,0 @@ -package system; - -abstract CsType(Class) from Class to Class -{ - public function new(cls : Class) this = cls; - - public var Name(get,never):CsString; - public function get_Name() : CsString - { - var fullName = Type.getClassName(this); - var i = fullName.lastIndexOf("."); - return i >= 0 ? fullName.substring(i + 1) : fullName; - } - - @:op(A == B) public inline static function eq(lhs : CsType, rhs : CsType) : system.Boolean return Type.getClassName(lhs) == Type.getClassName(rhs); - - @:op(A != B) public inline static function neq(lhs : CsType, rhs : CsType) : system.Boolean return Type.getClassName(lhs) != Type.getClassName(rhs); - -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/Delegate.hx b/Phase/Mscorlib/system/Delegate.hx deleted file mode 100644 index d9980ecc4..000000000 --- a/Phase/Mscorlib/system/Delegate.hx +++ /dev/null @@ -1,3 +0,0 @@ -package system; - -typedef Delegate = Dynamic; \ No newline at end of file diff --git a/Phase/Mscorlib/system/Double.hx b/Phase/Mscorlib/system/Double.hx deleted file mode 100644 index 874ad5758..000000000 --- a/Phase/Mscorlib/system/Double.hx +++ /dev/null @@ -1,246 +0,0 @@ -package system; - -abstract Double(Float) from Float from Int -{ - public inline function new(i:Float) this = i; - - @:from public static inline function FromInt(i:Int) : Double return new Double(i); - @:from public static inline function FromByte(i:system.Byte) : Double return new Double(i.ToHaxeInt()); - @:from public static inline function FromInt16(i:system.Int16) : Double return new Double(i.ToHaxeInt()); - @:from public static inline function FromInt32(i:system.Int32) : Double return new Double(i.ToHaxeInt()); - @:from public static inline function FromInt64(i:system.Int64) : Double return new Double(i.ToHaxeInt()); - @:from public static inline function FromSByte(i:system.Byte) : Double return new Double(i.ToHaxeInt()); - @:from public static inline function FromUInt16(i:system.UInt16) : Double return new Double(i.ToHaxeInt()); - @:from public static inline function FromUInt32(i:system.UInt32) : Double return new Double(i.ToHaxeInt()); - @:from public static inline function FromUInt64(i:system.UInt64) : Double return new Double(i.ToHaxeInt()); - @:from public static inline function FromSingle(i:Single) : Double return new Double(i.ToHaxeFloat()); - - public inline function ToHaxeFloat(): Float return this; - public inline function ToString() : system.CsString return Std.string(this); - - public inline function ToBoolean_IFormatProvider(provider:IFormatProvider) : system.Boolean return system.Convert.ToBoolean_Double(this); - public inline function ToChar_IFormatProvider(provider:IFormatProvider) : system.Char return system.Convert.ToChar_Double(this); - public inline function ToSByte_IFormatProvider(provider:IFormatProvider) : system.SByte return system.Convert.ToSByte_Double(this); - public inline function ToByte_IFormatProvider(provider:IFormatProvider) : system.Byte return system.Convert.ToByte_Double(this); - public inline function ToInt16_IFormatProvider(provider:IFormatProvider) : system.Int16 return system.Convert.ToInt16_Double(this); - public inline function ToUInt16_IFormatProvider(provider:IFormatProvider) : system.UInt16 return system.Convert.ToUInt16_Double(this); - public inline function ToInt32_IFormatProvider(provider:IFormatProvider) : system.Int32 return system.Convert.ToInt32_Double(this); - public inline function ToUInt32_IFormatProvider(provider:IFormatProvider) : system.UInt32 return system.Convert.ToUInt32_Double(this); - public inline function ToInt64_IFormatProvider(provider:IFormatProvider) : system.Int64 return system.Convert.ToInt64_Double(this); - public inline function ToUInt64_IFormatProvider(provider:IFormatProvider) : system.UInt64 return system.Convert.ToUInt64_Double(this); - public inline function ToSingle_IFormatProvider(provider:IFormatProvider) : system.Single return system.Convert.ToSingle_Double(this); - public inline function ToDouble_IFormatProvider(provider:IFormatProvider) : system.Double return system.Convert.ToDouble_Double(this); - - public function GetHashCode() : system.Int32 return system.Convert.ToHashCode_Double(this); - - public static var NaN(get, never) : system.Double; - public static inline function get_NaN() : system.Double return Math.NaN; - public static inline function IsNaN(v:system.Double) : system.Boolean return Math.isNaN(v.ToHaxeFloat()); - - @:op(-A) public inline function neg() : system.Double return -this; - - @:op(A++) public inline function postinc() : system.Double return this++; - @:op(++A) public inline function preinc() : system.Double return ++this; - - @:op(A--) public inline function postdec() : system.Double return this--; - @:op(--A) public inline function predec() : system.Double return --this; - - @:op(A * B) public static function mul1(lhs : system.Double, rhs : Int) : system.Double; - @:op(A * B) public static function mul2(lhs : Int, rhs : system.Double) : system.Double; - @:op(A * B) public static function mul3(lhs : system.Double, rhs : Float) : system.Double; - @:op(A * B) public static function mul4(lhs : Float, rhs : system.Double) : system.Double; - - @:op(A * B) public static function mul5(lhs : system.Double, rhs : system.Char) : system.Double; - @:op(A * B) public static function mul6(lhs : system.Double, rhs : system.Byte) : system.Double; - @:op(A * B) public static function mul7(lhs : system.Double, rhs : system.Int16) : system.Double; - @:op(A * B) public static function mul8(lhs : system.Double, rhs : system.Int32) : system.Double; - @:op(A * B) public static function mul9(lhs : system.Double, rhs : system.Int64) : system.Double; - @:op(A * B) public static function mul10(lhs : system.Double, rhs : system.SByte) : system.Double; - @:op(A * B) public static function mul11(lhs : system.Double, rhs : system.UInt16) : system.Double; - @:op(A * B) public static function mul12(lhs : system.Double, rhs : system.UInt32) : system.Double; - @:op(A * B) public static function mul13(lhs : system.Double, rhs : system.UInt64) : system.Double; - @:op(A * B) public static function mul14(lhs : system.Double, rhs : system.Single) : system.Double; - @:op(A * B) public static function mul15(lhs : system.Double, rhs : system.Double) : system.Double; - - - @:op(A / B) public static function div1(lhs : system.Double, rhs : Int) : system.Double; - @:op(A / B) public static function div2(lhs : Int, rhs : system.Double) : system.Double; - @:op(A / B) public static function div3(lhs : system.Double, rhs : Float) : system.Double; - @:op(A / B) public static function div4(lhs : Float, rhs : system.Double) : system.Double; - - @:op(A / B) public static function div5(lhs : system.Double, rhs : system.Char) : system.Double; - @:op(A / B) public static function div6(lhs : system.Double, rhs : system.Byte) : system.Double; - @:op(A / B) public static function div7(lhs : system.Double, rhs : system.Int16) : system.Double; - @:op(A / B) public static function div8(lhs : system.Double, rhs : system.Int32) : system.Double; - @:op(A / B) public static function div9(lhs : system.Double, rhs : system.Int64) : system.Double; - @:op(A / B) public static function div10(lhs : system.Double, rhs : system.SByte) : system.Double; - @:op(A / B) public static function div11(lhs : system.Double, rhs : system.UInt16) : system.Double; - @:op(A / B) public static function div12(lhs : system.Double, rhs : system.UInt32) : system.Double; - @:op(A / B) public static function div13(lhs : system.Double, rhs : system.UInt64) : system.Double; - @:op(A / B) public static function div14(lhs : system.Double, rhs : system.Single) : system.Double; - @:op(A / B) public static function div15(lhs : system.Double, rhs : system.Double) : system.Double; - - - @:op(A % B) public static function mod1(lhs : system.Double, rhs : Int) : system.Double; - @:op(A % B) public static function mod2(lhs : Int, rhs : system.Double) : system.Double; - @:op(A % B) public static function mod3(lhs : system.Double, rhs : Float) : system.Double; - @:op(A % B) public static function mod4(lhs : Float, rhs : system.Double) : system.Double; - - @:op(A % B) public static function mod5(lhs : system.Double, rhs : system.Char) : system.Double; - @:op(A % B) public static function mod6(lhs : system.Double, rhs : system.Byte) : system.Double; - @:op(A % B) public static function mod7(lhs : system.Double, rhs : system.Int16) : system.Double; - @:op(A % B) public static function mod8(lhs : system.Double, rhs : system.Int32) : system.Double; - @:op(A % B) public static function mod9(lhs : system.Double, rhs : system.Int64) : system.Double; - @:op(A % B) public static function mod10(lhs : system.Double, rhs : system.SByte) : system.Double; - @:op(A % B) public static function mod11(lhs : system.Double, rhs : system.UInt16) : system.Double; - @:op(A % B) public static function mod12(lhs : system.Double, rhs : system.UInt32) : system.Double; - @:op(A % B) public static function mod13(lhs : system.Double, rhs : system.UInt64) : system.Double; - @:op(A % B) public static function mod14(lhs : system.Double, rhs : system.Single) : system.Double; - @:op(A % B) public static function mod15(lhs : system.Double, rhs : system.Double) : system.Double; - - - @:op(A + B) public static function add1(lhs : system.Double, rhs : Int) : system.Double; - @:op(A + B) public static function add2(lhs : Int, rhs : system.Double) : system.Double; - @:op(A + B) public static function add3(lhs : system.Double, rhs : Float) : system.Double; - @:op(A + B) public static function add4(lhs : Float, rhs : system.Double) : system.Double; - - @:op(A + B) public static function add5(lhs : system.Double, rhs : system.Char) : system.Double; - @:op(A + B) public static function add6(lhs : system.Double, rhs : system.Byte) : system.Double; - @:op(A + B) public static function add7(lhs : system.Double, rhs : system.Int16) : system.Double; - @:op(A + B) public static function add8(lhs : system.Double, rhs : system.Int32) : system.Double; - @:op(A + B) public static function add9(lhs : system.Double, rhs : system.Int64) : system.Double; - @:op(A + B) public static function add10(lhs : system.Double, rhs : system.SByte) : system.Double; - @:op(A + B) public static function add11(lhs : system.Double, rhs : system.UInt16) : system.Double; - @:op(A + B) public static function add12(lhs : system.Double, rhs : system.UInt32) : system.Double; - @:op(A + B) public static function add13(lhs : system.Double, rhs : system.UInt64) : system.Double; - @:op(A + B) public static function add14(lhs : system.Double, rhs : system.Single) : system.Double; - @:op(A + B) public static function add15(lhs : system.Double, rhs : system.Double) : system.Double; - - - @:op(A - B) public static function sub1(lhs : system.Double, rhs : Int) : system.Double; - @:op(A - B) public static function sub2(lhs : Int, rhs : system.Double) : system.Double; - @:op(A - B) public static function sub3(lhs : system.Double, rhs : Float) : system.Double; - @:op(A - B) public static function sub4(lhs : Float, rhs : system.Double) : system.Double; - - @:op(A - B) public static function sub5(lhs : system.Double, rhs : system.Char) : system.Double; - @:op(A - B) public static function sub6(lhs : system.Double, rhs : system.Byte) : system.Double; - @:op(A - B) public static function sub7(lhs : system.Double, rhs : system.Int16) : system.Double; - @:op(A - B) public static function sub8(lhs : system.Double, rhs : system.Int32) : system.Double; - @:op(A - B) public static function sub9(lhs : system.Double, rhs : system.Int64) : system.Double; - @:op(A - B) public static function sub10(lhs : system.Double, rhs : system.SByte) : system.Double; - @:op(A - B) public static function sub11(lhs : system.Double, rhs : system.UInt16) : system.Double; - @:op(A - B) public static function sub12(lhs : system.Double, rhs : system.UInt32) : system.Double; - @:op(A - B) public static function sub13(lhs : system.Double, rhs : system.UInt64) : system.Double; - @:op(A - B) public static function sub14(lhs : system.Double, rhs : system.Single) : system.Double; - @:op(A - B) public static function sub15(lhs : system.Double, rhs : system.Double) : system.Double; - - - @:op(A > B) public static function gt1(lhs : system.Double, rhs : Int) : system.Boolean; - @:op(A > B) public static function gt2(lhs : Int, rhs : system.Double) : system.Boolean; - @:op(A > B) public static function gt3(lhs : system.Double, rhs : Float) : system.Boolean; - @:op(A > B) public static function gt4(lhs : Float, rhs : system.Double) : system.Boolean; - - @:op(A > B) public static function gt5(lhs : system.Double, rhs : system.Char) : system.Boolean; - @:op(A > B) public static function gt6(lhs : system.Double, rhs : system.Byte) : system.Boolean; - @:op(A > B) public static function gt7(lhs : system.Double, rhs : system.Int16) : system.Boolean; - @:op(A > B) public static function gt8(lhs : system.Double, rhs : system.Int32) : system.Boolean; - @:op(A > B) public static function gt9(lhs : system.Double, rhs : system.Int64) : system.Boolean; - @:op(A > B) public static function gt10(lhs : system.Double, rhs : system.SByte) : system.Boolean; - @:op(A > B) public static function gt11(lhs : system.Double, rhs : system.UInt16) : system.Boolean; - @:op(A > B) public static function gt12(lhs : system.Double, rhs : system.UInt32) : system.Boolean; - @:op(A > B) public static function gt13(lhs : system.Double, rhs : system.UInt64) : system.Boolean; - @:op(A > B) public static function gt14(lhs : system.Double, rhs : system.Single) : system.Boolean; - @:op(A > B) public static function gt15(lhs : system.Double, rhs : system.Double) : system.Boolean; - - - @:op(A < B) public static function lt1(lhs : system.Double, rhs : Int) : system.Boolean; - @:op(A < B) public static function lt2(lhs : Int, rhs : system.Double) : system.Boolean; - @:op(A < B) public static function lt3(lhs : system.Double, rhs : Float) : system.Boolean; - @:op(A < B) public static function lt4(lhs : Float, rhs : system.Double) : system.Boolean; - - @:op(A < B) public static function lt5(lhs : system.Double, rhs : system.Char) : system.Boolean; - @:op(A < B) public static function lt6(lhs : system.Double, rhs : system.Byte) : system.Boolean; - @:op(A < B) public static function lt7(lhs : system.Double, rhs : system.Int16) : system.Boolean; - @:op(A < B) public static function lt8(lhs : system.Double, rhs : system.Int32) : system.Boolean; - @:op(A < B) public static function lt9(lhs : system.Double, rhs : system.Int64) : system.Boolean; - @:op(A < B) public static function lt10(lhs : system.Double, rhs : system.SByte) : system.Boolean; - @:op(A < B) public static function lt11(lhs : system.Double, rhs : system.UInt16) : system.Boolean; - @:op(A < B) public static function lt12(lhs : system.Double, rhs : system.UInt32) : system.Boolean; - @:op(A < B) public static function lt13(lhs : system.Double, rhs : system.UInt64) : system.Boolean; - @:op(A < B) public static function lt14(lhs : system.Double, rhs : system.Single) : system.Boolean; - @:op(A < B) public static function lt15(lhs : system.Double, rhs : system.Double) : system.Boolean; - - - @:op(A >= B) public static function gte1(lhs : system.Double, rhs : Int) : system.Boolean; - @:op(A >= B) public static function gte2(lhs : Int, rhs : system.Double) : system.Boolean; - @:op(A >= B) public static function gte3(lhs : system.Double, rhs : Float) : system.Boolean; - @:op(A >= B) public static function gte4(lhs : Float, rhs : system.Double) : system.Boolean; - - @:op(A >= B) public static function gte5(lhs : system.Double, rhs : system.Char) : system.Boolean; - @:op(A >= B) public static function gte6(lhs : system.Double, rhs : system.Byte) : system.Boolean; - @:op(A >= B) public static function gte7(lhs : system.Double, rhs : system.Int16) : system.Boolean; - @:op(A >= B) public static function gte8(lhs : system.Double, rhs : system.Int32) : system.Boolean; - @:op(A >= B) public static function gte9(lhs : system.Double, rhs : system.Int64) : system.Boolean; - @:op(A >= B) public static function gte10(lhs : system.Double, rhs : system.SByte) : system.Boolean; - @:op(A >= B) public static function gte11(lhs : system.Double, rhs : system.UInt16) : system.Boolean; - @:op(A >= B) public static function gte12(lhs : system.Double, rhs : system.UInt32) : system.Boolean; - @:op(A >= B) public static function gte13(lhs : system.Double, rhs : system.UInt64) : system.Boolean; - @:op(A >= B) public static function gte14(lhs : system.Double, rhs : system.Single) : system.Boolean; - @:op(A >= B) public static function gte15(lhs : system.Double, rhs : system.Double) : system.Boolean; - - - @:op(A <= B) public static function lte1(lhs : system.Double, rhs : Int) : system.Boolean; - @:op(A <= B) public static function lte2(lhs : Int, rhs : system.Double) : system.Boolean; - @:op(A <= B) public static function lte3(lhs : system.Double, rhs : Float) : system.Boolean; - @:op(A <= B) public static function lte4(lhs : Float, rhs : system.Double) : system.Boolean; - - - @:op(A <= B) public static function lte5(lhs : system.Double, rhs : system.Char) : system.Boolean; - @:op(A <= B) public static function lte6(lhs : system.Double, rhs : system.Byte) : system.Boolean; - @:op(A <= B) public static function lte7(lhs : system.Double, rhs : system.Int16) : system.Boolean; - @:op(A <= B) public static function lte8(lhs : system.Double, rhs : system.Int32) : system.Boolean; - @:op(A <= B) public static function lte9(lhs : system.Double, rhs : system.Int64) : system.Boolean; - @:op(A <= B) public static function lte10(lhs : system.Double, rhs : system.SByte) : system.Boolean; - @:op(A <= B) public static function lte11(lhs : system.Double, rhs : system.UInt16) : system.Boolean; - @:op(A <= B) public static function lte12(lhs : system.Double, rhs : system.UInt32) : system.Boolean; - @:op(A <= B) public static function lte13(lhs : system.Double, rhs : system.UInt64) : system.Boolean; - @:op(A <= B) public static function lte14(lhs : system.Double, rhs : system.Single) : system.Boolean; - @:op(A <= B) public static function lte15(lhs : system.Double, rhs : system.Double) : system.Boolean; - - - @:op(A == B) public static function eq1(lhs : system.Double, rhs : Int) : system.Boolean; - @:op(A == B) public static function eq2(lhs : Int, rhs : system.Double) : system.Boolean; - @:op(A == B) public static function eq3(lhs : system.Double, rhs : Float) : system.Boolean; - @:op(A == B) public static function eq4(lhs : Float, rhs : system.Double) : system.Boolean; - - @:op(A == B) public static function eq5(lhs : system.Double, rhs : system.Char) : system.Boolean; - @:op(A == B) public static function eq6(lhs : system.Double, rhs : system.Byte) : system.Boolean; - @:op(A == B) public static function eq7(lhs : system.Double, rhs : system.Int16) : system.Boolean; - @:op(A == B) public static function eq8(lhs : system.Double, rhs : system.Int32) : system.Boolean; - @:op(A == B) public static function eq9(lhs : system.Double, rhs : system.Int64) : system.Boolean; - @:op(A == B) public static function eq10(lhs : system.Double, rhs : system.SByte) : system.Boolean; - @:op(A == B) public static function eq11(lhs : system.Double, rhs : system.UInt16) : system.Boolean; - @:op(A == B) public static function eq12(lhs : system.Double, rhs : system.UInt32) : system.Boolean; - @:op(A == B) public static function eq13(lhs : system.Double, rhs : system.UInt64) : system.Boolean; - @:op(A == B) public static function eq14(lhs : system.Double, rhs : system.Single) : system.Boolean; - @:op(A == B) public static function eq15(lhs : system.Double, rhs : system.Double) : system.Boolean; - - - @:op(A != B) public static function neq1(lhs : system.Double, rhs : Int) : system.Boolean; - @:op(A != B) public static function neq2(lhs : Int, rhs : system.Double) : system.Boolean; - @:op(A != B) public static function neq3(lhs : system.Double, rhs : Float) : system.Boolean; - @:op(A != B) public static function neq4(lhs : Float, rhs : system.Double) : system.Boolean; - - @:op(A != B) public static function neq5(lhs : system.Double, rhs : system.Char) : system.Boolean; - @:op(A != B) public static function neq6(lhs : system.Double, rhs : system.Byte) : system.Boolean; - @:op(A != B) public static function neq7(lhs : system.Double, rhs : system.Int16) : system.Boolean; - @:op(A != B) public static function neq8(lhs : system.Double, rhs : system.Int32) : system.Boolean; - @:op(A != B) public static function neq9(lhs : system.Double, rhs : system.Int64) : system.Boolean; - @:op(A != B) public static function neq10(lhs : system.Double, rhs : system.SByte) : system.Boolean; - @:op(A != B) public static function neq11(lhs : system.Double, rhs : system.UInt16) : system.Boolean; - @:op(A != B) public static function neq12(lhs : system.Double, rhs : system.UInt32) : system.Boolean; - @:op(A != B) public static function neq13(lhs : system.Double, rhs : system.UInt64) : system.Boolean; - @:op(A != B) public static function neq14(lhs : system.Double, rhs : system.Single) : system.Boolean; - @:op(A != B) public static function neq15(lhs : system.Double, rhs : system.Double) : system.Boolean; - -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/DoubleArray.hx b/Phase/Mscorlib/system/DoubleArray.hx deleted file mode 100644 index c62ebea12..000000000 --- a/Phase/Mscorlib/system/DoubleArray.hx +++ /dev/null @@ -1,21 +0,0 @@ -package system; - -abstract DoubleArray(js.html.Float64Array) -{ - public inline function new(length:Int32) this = new js.html.Float64Array(length.ToHaxeInt()); - - @:from public static inline function fromArray(a:Array):DoubleArray return cast new js.html.Float64Array(untyped a); - @:from public static inline function fromFixedArray(a:FixedArray):DoubleArray return cast new js.html.Float64Array(untyped a.ToHaxeArray()); - - public var Length(get, never):Int32; - public inline function get_Length() : Int32 return this.length; - - @:op([]) public inline function get(index:Int32):Double return this[index.ToHaxeInt()]; - @:op([]) public inline function set(index:Int32, val:Double):Double return this[index.ToHaxeInt()] = val.ToHaxeFloat(); - - public inline function iterator() : Iterator return new DoubleArrayIterator(this); - - public inline function ToEnumerable() : system.collections.generic.IEnumerable return new DoubleArrayEnumerable(this); - - public static inline function empty(size:Int32) return new DoubleArray(size); -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/DoubleArrayEnumerable.hx b/Phase/Mscorlib/system/DoubleArrayEnumerable.hx deleted file mode 100644 index 7dd81b61a..000000000 --- a/Phase/Mscorlib/system/DoubleArrayEnumerable.hx +++ /dev/null @@ -1,18 +0,0 @@ -package system; - -import system.collections.generic.IEnumerable; -import system.collections.generic.IEnumerator; - -class DoubleArrayEnumerable implements IEnumerable -{ - private var _array:js.html.Float64Array; - public function new(array:js.html.Float64Array) - { - _array= array; - } - - public function GetEnumerator() : IEnumerator - { - return new DoubleArrayEnumerator(_array); - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/DoubleArrayEnumerator.hx b/Phase/Mscorlib/system/DoubleArrayEnumerator.hx deleted file mode 100644 index d12225c69..000000000 --- a/Phase/Mscorlib/system/DoubleArrayEnumerator.hx +++ /dev/null @@ -1,29 +0,0 @@ -package system; - -import system.collections.generic.IEnumerator; - -class DoubleArrayEnumerator implements IEnumerator -{ - private var _array:js.html.Float64Array; - private var _i:Int; - - public function new(array:js.html.Float64Array) - { - _array = array; - _i = -1; - } - - public var Current(get, never):Double; - public function get_Current() : Double return _array[_i]; - public function MoveNext() : Bool - { - if(_i >= _array.length - 1) return false; - _i++; - return true; - - } - public function Reset():Void - { - _i = -1; - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/DoubleArrayIterator.hx b/Phase/Mscorlib/system/DoubleArrayIterator.hx deleted file mode 100644 index e0a7833bd..000000000 --- a/Phase/Mscorlib/system/DoubleArrayIterator.hx +++ /dev/null @@ -1,19 +0,0 @@ -package system; - -class DoubleArrayIterator -{ - private var _array:js.html.Float64Array; - private var _i:Int; - - public function new(array:js.html.Float64Array) - { - _array = array; - _i = 0; - } - - public function hasNext() return _i < _array.length; - public function next() : Double - { - return _array[_i++]; - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/EventAction.hx b/Phase/Mscorlib/system/EventAction.hx deleted file mode 100644 index 42cd36ba2..000000000 --- a/Phase/Mscorlib/system/EventAction.hx +++ /dev/null @@ -1,46 +0,0 @@ -package system; - -abstract EventAction(ArrayVoid>) -{ - public inline function new(v:Void->Void) this = v == null ? null : [v]; - - @:to public inline function ToLambda() : Void->Void return Invoke; - - public inline function ToArray() return this; - @:op(A + B) public static function add(lhs : EventAction, rhs : Void->Void) : EventAction - { - if(lhs == null) - { - lhs = new EventAction(rhs); - } - else - { - lhs.ToArray().push(rhs); - } - return lhs; - } - - @:op(A - B) public static function sub(lhs : EventAction, rhs : Void->Void) : EventAction - { - var raw = lhs.ToArray(); - var index = raw.indexOf(rhs); - if(index != -1) - { - raw.splice(index, 1); - if(raw.length == 0) - { - return null; - } - } - return lhs; - } - - public function Invoke() : Void - { - if(this == null) return; - for (x in this) - { - x(); - } - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/EventAction1.hx b/Phase/Mscorlib/system/EventAction1.hx deleted file mode 100644 index 86a0dc4ec..000000000 --- a/Phase/Mscorlib/system/EventAction1.hx +++ /dev/null @@ -1,48 +0,0 @@ -package system; - -abstract EventAction1(ArrayVoid>) -{ - public inline function new(v:T1->Void) this = v == null ? null : [v]; - - - @:to public inline function ToLambda() : T1->Void return Invoke; - - public inline function ToArray() return this; - @:op(A + B) public static function add(lhs : EventAction1, rhs : T1->Void) : EventAction1 - { - if(lhs == null) - { - lhs = new EventAction1(rhs); - } - else - { - lhs.ToArray().push(rhs); - } - return lhs; - } - - @:op(A - B) public static function sub(lhs : EventAction1, rhs : T1->Void) : EventAction1 - { - var raw = lhs.ToArray(); - var index = raw.indexOf(rhs); - if(index != -1) - { - raw.splice(index, 1); - if(raw.length == 0) - { - return null; - } - } - return lhs; - } - - - public function Invoke(p:T1) : Void - { - if(this == null) return; - for (x in this) - { - x(p); - } - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/EventAction2.hx b/Phase/Mscorlib/system/EventAction2.hx deleted file mode 100644 index dfa5dde32..000000000 --- a/Phase/Mscorlib/system/EventAction2.hx +++ /dev/null @@ -1,46 +0,0 @@ -package system; - -abstract EventAction2(ArrayT2->Void>) -{ - public inline function new(v:T1->T2->Void) this = v == null ? null : [v]; - - @:to public inline function ToLambda() : T1->T2->Void return Invoke; - - public inline function ToArray() return this; - @:op(A + B) public static function add(lhs : EventAction2, rhs : T1->T2->Void) : EventAction2 - { - if(lhs == null) - { - lhs = new EventAction2(rhs); - } - else - { - lhs.ToArray().push(rhs); - } - return lhs; - } - - @:op(A - B) public static function sub(lhs : EventAction2, rhs : T1->T2->Void) : EventAction2 - { - var raw = lhs.ToArray(); - var index = raw.indexOf(rhs); - if(index != -1) - { - raw.splice(index, 1); - if(raw.length == 0) - { - return null; - } - } - return lhs; - } - - public function Invoke(p1:T1, p2:T2) : Void - { - if(this == null) return; - for (x in this) - { - x(p1, p2); - } - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/Exception.hx b/Phase/Mscorlib/system/Exception.hx deleted file mode 100644 index 62008cd6c..000000000 --- a/Phase/Mscorlib/system/Exception.hx +++ /dev/null @@ -1,16 +0,0 @@ -package system; - -class Exception -{ - public var Message(default, null):String; - - public function Exception_CsString(message:String) - { - Message = message; - return this; - } - - public function new() - { - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/FixedArray.hx b/Phase/Mscorlib/system/FixedArray.hx deleted file mode 100644 index f880b3d45..000000000 --- a/Phase/Mscorlib/system/FixedArray.hx +++ /dev/null @@ -1,27 +0,0 @@ -package system; - -/** - * ... - * @author Danielku15 - */ -abstract FixedArray(Array) to Array -{ - public inline function new(length:Int32) this = untyped __new__(Array, length.ToHaxeInt()); - - public inline function ToHaxeArray() : Array return this; - - @:from public static inline function fromArray(a:Array):FixedArray return cast a; - - public var Length(get, never):Int32; - public inline function get_Length() : Int32 return this.length; - - @:op([]) public inline function get(index:Int32):T return this[index.ToHaxeInt()]; - @:op([]) public inline function set(index:Int32, val:T):T return this[index.ToHaxeInt()] = val; - - public inline function iterator() : Iterator return this.iterator(); - - public inline function ToEnumerable() : system.collections.generic.IEnumerable return new system.collections.generic.IterableEnumerable(this); - - public static inline function empty(size:Int32) return new FixedArray(size); - public static inline function empty2(size:Int32) return new FixedArray>(size); -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/FormatException.hx b/Phase/Mscorlib/system/FormatException.hx deleted file mode 100644 index 655924c0b..000000000 --- a/Phase/Mscorlib/system/FormatException.hx +++ /dev/null @@ -1,14 +0,0 @@ -package system; - -class FormatException extends Exception -{ - public function new() - { - super(); - } - public function FormatException_CsString(message:CsString) - { - Exception_CsString(message); - return this; - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/Func1.hx b/Phase/Mscorlib/system/Func1.hx deleted file mode 100644 index bee869e24..000000000 --- a/Phase/Mscorlib/system/Func1.hx +++ /dev/null @@ -1,3 +0,0 @@ -package system; - -typedef Func1 = Void -> T; \ No newline at end of file diff --git a/Phase/Mscorlib/system/Func2.hx b/Phase/Mscorlib/system/Func2.hx deleted file mode 100644 index 9f3d2777c..000000000 --- a/Phase/Mscorlib/system/Func2.hx +++ /dev/null @@ -1,3 +0,0 @@ -package system; - -typedef Func2 = T1-> TReturn; \ No newline at end of file diff --git a/Phase/Mscorlib/system/Func3.hx b/Phase/Mscorlib/system/Func3.hx deleted file mode 100644 index 5dc618391..000000000 --- a/Phase/Mscorlib/system/Func3.hx +++ /dev/null @@ -1,3 +0,0 @@ -package system; - -typedef Func3 = T1->T2-> TReturn; \ No newline at end of file diff --git a/Phase/Mscorlib/system/HaxeExtensions.hx b/Phase/Mscorlib/system/HaxeExtensions.hx deleted file mode 100644 index ab2089e8f..000000000 --- a/Phase/Mscorlib/system/HaxeExtensions.hx +++ /dev/null @@ -1,38 +0,0 @@ -package system; - -class StringExtensions -{ - public static inline function ToHaxeString(s:String) return s; -} - -class IntExtensions -{ - public static inline function ToHaxeInt(i:Int) return i; - public static inline function ToChar_IFormatProvider(i:Int, provider:IFormatProvider) : system.Char return i; - public static inline function ToSByte_IFormatProvider(i:Int, provider:IFormatProvider) : system.SByte return i; - public static inline function ToByte_IFormatProvider(i:Int, provider:IFormatProvider) : system.Byte return i; - public static inline function ToInt16_IFormatProvider(i:Int, provider:IFormatProvider) : system.Int16 return i; - public static inline function ToUInt16_IFormatProvider(i:Int, provider:IFormatProvider) : system.UInt16 return i; - public static inline function ToInt32_IFormatProvider(i:Int, provider:IFormatProvider) : system.Int32 return i; - public static inline function ToUInt32_IFormatProvider(i:Int, provider:IFormatProvider) : system.UInt32 return i; - public static inline function ToInt64_IFormatProvider(i:Int, provider:IFormatProvider) : system.Int64 return i; - public static inline function ToUInt64_IFormatProvider(i:Int, provider:IFormatProvider) : system.UInt64 return i; - public static inline function ToSingle_IFormatProvider(i:Int, provider:IFormatProvider) : system.Single return i; - public static inline function ToDouble_IFormatProvider(i:Int, provider:IFormatProvider) : system.Double return i; -} -class FloatExtensions -{ - public static inline function ToHaxeFloat(f:Float) return f; - - public static inline function ToChar_IFormatProvider(i:Float, provider:IFormatProvider) : system.Char return Std.int(i); - public static inline function ToSByte_IFormatProvider(i:Float, provider:IFormatProvider) : system.SByte return Std.int(i); - public static inline function ToByte_IFormatProvider(i:Float, provider:IFormatProvider) : system.Byte return Std.int(i); - public static inline function ToInt16_IFormatProvider(i:Float, provider:IFormatProvider) : system.Int16 return Std.int(i); - public static inline function ToUInt16_IFormatProvider(i:Float, provider:IFormatProvider) : system.UInt16 return Std.int(i); - public static inline function ToInt32_IFormatProvider(i:Float, provider:IFormatProvider) : system.Int32 return Std.int(i); - public static inline function ToUInt32_IFormatProvider(i:Float, provider:IFormatProvider) : system.UInt32 return Std.int(i); - public static inline function ToInt64_IFormatProvider(i:Float, provider:IFormatProvider) : system.Int64 return Std.int(i); - public static inline function ToUInt64_IFormatProvider(i:Float, provider:IFormatProvider) : system.UInt64 return Std.int(i); - public static inline function ToSingle_IFormatProvider(i:Float, provider:IFormatProvider) : system.Single return Std.int(i); - public static inline function ToDouble_IFormatProvider(i:Float, provider:IFormatProvider) : system.Double return Std.int(i); -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/IFormatProvider.hx b/Phase/Mscorlib/system/IFormatProvider.hx deleted file mode 100644 index 2437ba7cc..000000000 --- a/Phase/Mscorlib/system/IFormatProvider.hx +++ /dev/null @@ -1,5 +0,0 @@ -package system; - -interface IFormatProvider -{ -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/Int16.hx b/Phase/Mscorlib/system/Int16.hx deleted file mode 100644 index fc4216ac8..000000000 --- a/Phase/Mscorlib/system/Int16.hx +++ /dev/null @@ -1,319 +0,0 @@ -package system; - - -abstract Int16(Int) from Int -{ - public inline function new(i:Int) this = system.Convert.ToInt16(i); - - public inline function ToHaxeInt(): Int return this; - public inline function ToString() : system.CsString return Std.string(this); - - @:from public static inline function FromByte(c:Byte) : Int16 return new Int16(c.ToHaxeInt()); - - public inline function ToBoolean_IFormatProvider(provider:IFormatProvider) : system.Boolean return system.Convert.ToBoolean_Int16(this); - public inline function ToChar_IFormatProvider(provider:IFormatProvider) : system.Char return system.Convert.ToChar_Int16(this); - public inline function ToSByte_IFormatProvider(provider:IFormatProvider) : system.SByte return system.Convert.ToSByte_Int16(this); - public inline function ToByte_IFormatProvider(provider:IFormatProvider) : system.Byte return system.Convert.ToByte_Int16(this); - public inline function ToInt16_IFormatProvider(provider:IFormatProvider) : system.Int16 return system.Convert.ToInt16_Int16(this); - public inline function ToUInt16_IFormatProvider(provider:IFormatProvider) : system.UInt16 return system.Convert.ToUInt16_Int16(this); - public inline function ToInt32_IFormatProvider(provider:IFormatProvider) : system.Int32 return system.Convert.ToInt32_Int16(this); - public inline function ToUInt32_IFormatProvider(provider:IFormatProvider) : system.UInt32 return system.Convert.ToUInt32_Int16(this); - public inline function ToInt64_IFormatProvider(provider:IFormatProvider) : system.Int64 return system.Convert.ToInt64_Int16(this); - public inline function ToUInt64_IFormatProvider(provider:IFormatProvider) : system.UInt64 return system.Convert.ToUInt64_Int16(this); - public inline function ToSingle_IFormatProvider(provider:IFormatProvider) : system.Single return system.Convert.ToSingle_Int16(this); - public inline function ToDouble_IFormatProvider(provider:IFormatProvider) : system.Double return system.Convert.ToDouble_Int16(this); - - public inline function GetHashCode() : system.Int32 return this | (this << 16); - - @:op(-A) public inline function neg() : system.Int32 return -this; - - @:op(~A)public inline function not() : system.Int32 return ~this; - - @:op(A++)public inline function postinc() : system.Int16 return this++; - @:op(++A)public inline function preinc() : system.Int16 return ++this; - - @:op(A--)public inline function postdec() : system.Int16 return this--; - @:op(--A)public inline function predec() : system.Int16 return --this; - - @:op(A * B) public static function mul1(lhs : system.Int16, rhs : Int) : system.Int32; - @:op(A * B) public static function mul2(lhs : Int, rhs : system.Int16) : system.Int32; - @:op(A * B) public static function mul3(lhs : system.Int16, rhs : Float) : Float; - @:op(A * B) public static function mul4(lhs : Float, rhs : system.Int16) : Float; - - @:op(A * B) public static function mul5(lhs : system.Int16, rhs : system.Char) : system.Int32; - @:op(A * B) public static function mul6(lhs : system.Int16, rhs : system.Byte) : system.Int32; - @:op(A * B) public static function mul7(lhs : system.Int16, rhs : system.Int16) : system.Int32; - @:op(A * B) public static function mul8(lhs : system.Int16, rhs : system.Int32) : system.Int32; - @:op(A * B) public static function mul9(lhs : system.Int16, rhs : system.Int64) : system.Int64; - @:op(A * B) public static function mul10(lhs : system.Int16, rhs : system.SByte) : system.Int32; - @:op(A * B) public static function mul11(lhs : system.Int16, rhs : system.UInt16) : system.Int32; - @:op(A * B) public static function mul12(lhs : system.Int16, rhs : system.UInt32) : system.Int64; - // @:op(A * B) public static function mul13(lhs : system.Int16, rhs : system.UInt64) : system.UInt64; - @:op(A * B) public static function mul14(lhs : system.Int16, rhs : system.Single) : system.Single; - @:op(A * B) public static function mul15(lhs : system.Int16, rhs : system.Double) : system.Double; - - - @:op(A / B) public static inline function div0(lhs : system.Int16, rhs : system.Int16) : system.Int32 return Std.int(lhs.ToHaxeInt() / rhs.ToHaxeInt()); - @:op(A / B) public static inline function div1(lhs : system.Int16, rhs : Int) : system.Int32 return Std.int(lhs.ToHaxeInt() / rhs); - @:op(A / B) public static inline function div2(lhs : Int, rhs : system.Int16) : system.Int32 return Std.int(lhs / rhs.ToHaxeInt()); - @:op(A / B) public static function div3(lhs : system.Int16, rhs : Float) : Float; - @:op(A / B) public static function div4(lhs : Float, rhs : system.Int16) : Float; - - @:op(A / B) public static function div5(lhs : system.Int16, rhs : system.Char) : system.Int32; - @:op(A / B) public static function div6(lhs : system.Int16, rhs : system.Byte) : system.Int32; - @:op(A / B) public static function div7(lhs : system.Int16, rhs : system.Int16) : system.Int32; - @:op(A / B) public static function div8(lhs : system.Int16, rhs : system.Int32) : system.Int32; - @:op(A / B) public static function div9(lhs : system.Int16, rhs : system.Int64) : system.Int64; - @:op(A / B) public static function div10(lhs : system.Int16, rhs : system.SByte) : system.Int32; - @:op(A / B) public static function div11(lhs : system.Int16, rhs : system.UInt16) : system.Int32; - @:op(A / B) public static function div12(lhs : system.Int16, rhs : system.UInt32) : system.Int64; - // @:op(A / B) public static function div13(lhs : system.Int16, rhs : system.UInt64) : system.UInt64; - @:op(A / B) public static function div14(lhs : system.Int16, rhs : system.Single) : system.Single; - @:op(A / B) public static function div15(lhs : system.Int16, rhs : system.Double) : system.Double; - - - @:op(A % B) public static function mod0(lhs : system.Int16, rhs : system.Int16) : system.Int32; - @:op(A % B) public static function mod1(lhs : system.Int16, rhs : Int) : system.Int32; - @:op(A % B) public static function mod2(lhs : Int, rhs : system.Int16) : system.Int32; - @:op(A % B) public static function mod3(lhs : system.Int16, rhs : Float) : Float; - @:op(A % B) public static function mod4(lhs : Float, rhs : system.Int16) : Float; - - @:op(A % B) public static function mod5(lhs : system.Int16, rhs : system.Char) : system.Int32; - @:op(A % B) public static function mod6(lhs : system.Int16, rhs : system.Byte) : system.Int32; - @:op(A % B) public static function mod7(lhs : system.Int16, rhs : system.Int16) : system.Int32; - @:op(A % B) public static function mod8(lhs : system.Int16, rhs : system.Int32) : system.Int32; - @:op(A % B) public static function mod9(lhs : system.Int16, rhs : system.Int64) : system.Int64; - @:op(A % B) public static function mod10(lhs : system.Int16, rhs : system.SByte) : system.Int32; - @:op(A % B) public static function mod11(lhs : system.Int16, rhs : system.UInt16) : system.Int32; - @:op(A % B) public static function mod12(lhs : system.Int16, rhs : system.UInt32) : system.Int64; - // @:op(A % B) public static function mod13(lhs : system.Int16, rhs : system.UInt64) : system.UInt64; - @:op(A % B) public static function mod14(lhs : system.Int16, rhs : system.Single) : system.Single; - @:op(A % B) public static function mod15(lhs : system.Int16, rhs : system.Double) : system.Double; - - @:op(A + B) public static function add0(lhs : system.Int16, rhs : system.Int16) : system.Int32; - @:op(A + B) public static function add1(lhs : system.Int16, rhs : Int) : system.Int32; - @:op(A + B) public static function add2(lhs : Int, rhs : system.Int16) : system.Int32; - @:op(A + B) public static function add3(lhs : system.Int16, rhs : Float) : Float; - @:op(A + B) public static function add4(lhs : Float, rhs : system.Int16) : Float; - - @:op(A + B) public static function add5(lhs : system.Int16, rhs : system.Char) : system.Int32; - @:op(A + B) public static function add6(lhs : system.Int16, rhs : system.Byte) : system.Int32; - @:op(A + B) public static function add7(lhs : system.Int16, rhs : system.Int16) : system.Int32; - @:op(A + B) public static function add8(lhs : system.Int16, rhs : system.Int32) : system.Int32; - @:op(A + B) public static function add9(lhs : system.Int16, rhs : system.Int64) : system.Int64; - @:op(A + B) public static function add10(lhs : system.Int16, rhs : system.SByte) : system.Int32; - @:op(A + B) public static function add11(lhs : system.Int16, rhs : system.UInt16) : system.Int32; - @:op(A + B) public static function add12(lhs : system.Int16, rhs : system.UInt32) : system.Int64; - // @:op(A + B) public static function add13(lhs : system.Int16, rhs : system.UInt64) : system.UInt64; - @:op(A + B) public static function add14(lhs : system.Int16, rhs : system.Single) : system.Single; - @:op(A + B) public static function add15(lhs : system.Int16, rhs : system.Double) : system.Double; - @:op(A + B) public static inline function add16(lhs : system.Int16, rhs : system.CsString) : system.CsString return lhs.ToString() + rhs; - @:op(A + B) public static inline function add17(lhs : system.Int16, rhs : String) : system.CsString return lhs.ToString() + rhs; - - - @:op(A - B) public static function sub0(lhs : system.Int16, rhs : system.Int16) : system.Int32; - @:op(A - B) public static function sub1(lhs : system.Int16, rhs : Int) : system.Int32; - @:op(A - B) public static function sub2(lhs : Int, rhs : system.Int16) : system.Int32; - @:op(A - B) public static function sub3(lhs : system.Int16, rhs : Float) : Float; - @:op(A - B) public static function sub4(lhs : Float, rhs : system.Int16) : Float; - - @:op(A - B) public static function sub5(lhs : system.Int16, rhs : system.Char) : system.Int32; - @:op(A - B) public static function sub6(lhs : system.Int16, rhs : system.Byte) : system.Int32; - @:op(A - B) public static function sub7(lhs : system.Int16, rhs : system.Int16) : system.Int32; - @:op(A - B) public static function sub8(lhs : system.Int16, rhs : system.Int32) : system.Int32; - @:op(A - B) public static function sub9(lhs : system.Int16, rhs : system.Int64) : system.Int64; - @:op(A - B) public static function sub10(lhs : system.Int16, rhs : system.SByte) : system.Int32; - @:op(A - B) public static function sub11(lhs : system.Int16, rhs : system.UInt16) : system.Int32; - @:op(A - B) public static function sub12(lhs : system.Int16, rhs : system.UInt32) : system.Int64; - // @:op-A - B) public static function sub13(lhs : system.Int16, rhs : system.UInt64) : system.UInt64; - @:op(A - B) public static function sub14(lhs : system.Int16, rhs : system.Single) : system.Single; - @:op(A - B) public static function sub15(lhs : system.Int16, rhs : system.Double) : system.Double; - - - @:op(A << B) public static function shl0(lhs : system.Int16, rhs : system.Int16) : system.Int32; - @:op(A << B) public static function shl1(lhs : system.Int16, rhs : Int) : system.Int32; - @:op(A << B) public static function shl2(lhs : Int, rhs : system.Int16) : system.Int32; - - @:op(A << B) public static function shl5(lhs : system.Int16, rhs : system.Char) : system.Int32; - @:op(A << B) public static function shl6(lhs : system.Int16, rhs : system.Byte) : system.Int32; - @:op(A << B) public static function shl7(lhs : system.Int16, rhs : system.Int16) : system.Int32; - @:op(A << B) public static function shl8(lhs : system.Int16, rhs : system.Int32) : system.Int32; - // @:op(A << B) public static function shl9(lhs : system.Int16, rhs : system.Int64) : system.Int64; - @:op(A << B) public static function shl10(lhs : system.Int16, rhs : system.SByte) : system.Int32; - @:op(A << B) public static function shl11(lhs : system.Int16, rhs : system.UInt16) : system.Int32; - // @:op(A << B) public static function shl12(lhs : system.Int16, rhs : system.UInt32) : system.Int64; - // @:op-A << B) public static function shl13(lhs : system.Int16, rhs : system.UInt64) : system.UInt64; - - - @:op(A >> B) public static function shr0(lhs : system.Int16, rhs : system.Int16) : system.Int32; - @:op(A >> B) public static function shr1(lhs : system.Int16, rhs : Int) : system.Int32; - @:op(A >> B) public static function shr2(lhs : Int, rhs : system.Int16) : system.Int32; - - @:op(A >> B) public static function shr5(lhs : system.Int16, rhs : system.Char) : system.Int32; - @:op(A >> B) public static function shr6(lhs : system.Int16, rhs : system.Byte) : system.Int32; - @:op(A >> B) public static function shr7(lhs : system.Int16, rhs : system.Int16) : system.Int32; - @:op(A >> B) public static function shr8(lhs : system.Int16, rhs : system.Int32) : system.Int32; - // @:op(A >> B) public static function shr9(lhs : system.Int16, rhs : system.Int64) : system.Int64; - @:op(A >> B) public static function shr10(lhs : system.Int16, rhs : system.SByte) : system.Int32; - @:op(A >> B) public static function shr11(lhs : system.Int16, rhs : system.UInt16) : system.Int32; - // @:op(A >> B) public static function shr12(lhs : system.Int16, rhs : system.UInt32) : system.Int64; - // @:op-A >> B) public static function shr13(lhs : system.Int16, rhs : system.UInt64) : system.UInt64; - - @:op(A > B) public static function gt0(lhs : system.Int16, rhs : system.Int16) : system.Boolean; - @:op(A > B) public static function gt1(lhs : system.Int16, rhs : Int) : system.Boolean; - @:op(A > B) public static function gt2(lhs : Int, rhs : system.Int16) : system.Boolean; - @:op(A > B) public static function gt3(lhs : system.Int16, rhs : Float) : system.Boolean; - @:op(A > B) public static function gt4(lhs : Float, rhs : system.Int16) : system.Boolean; - - @:op(A > B) public static function gt5(lhs : system.Int16, rhs : system.Char) : system.Boolean; - @:op(A > B) public static function gt6(lhs : system.Int16, rhs : system.Byte) : system.Boolean; - @:op(A > B) public static function gt7(lhs : system.Int16, rhs : system.Int16) : system.Boolean; - @:op(A > B) public static function gt8(lhs : system.Int16, rhs : system.Int32) : system.Boolean; - @:op(A > B) public static function gt9(lhs : system.Int16, rhs : system.Int64) : system.Boolean; - @:op(A > B) public static function gt10(lhs : system.Int16, rhs : system.SByte) : system.Boolean; - @:op(A > B) public static function gt11(lhs : system.Int16, rhs : system.UInt16) : system.Boolean; - @:op(A > B) public static function gt12(lhs : system.Int16, rhs : system.UInt32) : system.Boolean; - @:op(A > B) public static function gt13(lhs : system.Int16, rhs : system.UInt64) : system.Boolean; - @:op(A > B) public static function gt14(lhs : system.Int16, rhs : system.Single) : system.Boolean; - @:op(A > B) public static function gt15(lhs : system.Int16, rhs : system.Double) : system.Boolean; - - - @:op(A < B) public static function lt0(lhs : system.Int16, rhs : system.Int16) : system.Boolean; - @:op(A < B) public static function lt1(lhs : system.Int16, rhs : Int) : system.Boolean; - @:op(A < B) public static function lt2(lhs : Int, rhs : system.Int16) : system.Boolean; - @:op(A < B) public static function lt3(lhs : system.Int16, rhs : Float) : system.Boolean; - @:op(A < B) public static function lt4(lhs : Float, rhs : system.Int16) : system.Boolean; - - @:op(A < B) public static function lt5(lhs : system.Int16, rhs : system.Char) : system.Boolean; - @:op(A < B) public static function lt6(lhs : system.Int16, rhs : system.Byte) : system.Boolean; - @:op(A < B) public static function lt7(lhs : system.Int16, rhs : system.Int16) : system.Boolean; - @:op(A < B) public static function lt8(lhs : system.Int16, rhs : system.Int32) : system.Boolean; - @:op(A < B) public static function lt9(lhs : system.Int16, rhs : system.Int64) : system.Boolean; - @:op(A < B) public static function lt10(lhs : system.Int16, rhs : system.SByte) : system.Boolean; - @:op(A < B) public static function lt11(lhs : system.Int16, rhs : system.UInt16) : system.Boolean; - @:op(A < B) public static function lt12(lhs : system.Int16, rhs : system.UInt32) : system.Boolean; - @:op(A < B) public static function lt13(lhs : system.Int16, rhs : system.UInt64) : system.Boolean; - @:op(A < B) public static function lt14(lhs : system.Int16, rhs : system.Single) : system.Boolean; - @:op(A < B) public static function lt15(lhs : system.Int16, rhs : system.Double) : system.Boolean; - - - @:op(A >= B) public static function gte0(lhs : system.Int16, rhs : system.Int16) : system.Boolean; - @:op(A >= B) public static function gte1(lhs : system.Int16, rhs : Int) : system.Boolean; - @:op(A >= B) public static function gte2(lhs : Int, rhs : system.Int16) : system.Boolean; - @:op(A >= B) public static function gte3(lhs : system.Int16, rhs : Float) : system.Boolean; - @:op(A >= B) public static function gte4(lhs : Float, rhs : system.Int16) : system.Boolean; - - @:op(A >= B) public static function gte5(lhs : system.Int16, rhs : system.Char) : system.Boolean; - @:op(A >= B) public static function gte6(lhs : system.Int16, rhs : system.Byte) : system.Boolean; - @:op(A >= B) public static function gte7(lhs : system.Int16, rhs : system.Int16) : system.Boolean; - @:op(A >= B) public static function gte8(lhs : system.Int16, rhs : system.Int32) : system.Boolean; - @:op(A >= B) public static function gte9(lhs : system.Int16, rhs : system.Int64) : system.Boolean; - @:op(A >= B) public static function gte10(lhs : system.Int16, rhs : system.SByte) : system.Boolean; - @:op(A >= B) public static function gte11(lhs : system.Int16, rhs : system.UInt16) : system.Boolean; - @:op(A >= B) public static function gte12(lhs : system.Int16, rhs : system.UInt32) : system.Boolean; - @:op(A >= B) public static function gte13(lhs : system.Int16, rhs : system.UInt64) : system.Boolean; - @:op(A >= B) public static function gte14(lhs : system.Int16, rhs : system.Single) : system.Boolean; - @:op(A >= B) public static function gte15(lhs : system.Int16, rhs : system.Double) : system.Boolean; - - - @:op(A <= B) public static function lte0(lhs : system.Int16, rhs : system.Int16) : system.Boolean; - @:op(A <= B) public static function lte1(lhs : system.Int16, rhs : Int) : system.Boolean; - @:op(A <= B) public static function lte2(lhs : Int, rhs : system.Int16) : system.Boolean; - @:op(A <= B) public static function lte3(lhs : system.Int16, rhs : Float) : system.Boolean; - @:op(A <= B) public static function lte4(lhs : Float, rhs : system.Int16) : system.Boolean; - - @:op(A <= B) public static function lte5(lhs : system.Int16, rhs : system.Char) : system.Boolean; - @:op(A <= B) public static function lte6(lhs : system.Int16, rhs : system.Byte) : system.Boolean; - @:op(A <= B) public static function lte7(lhs : system.Int16, rhs : system.Int16) : system.Boolean; - @:op(A <= B) public static function lte8(lhs : system.Int16, rhs : system.Int32) : system.Boolean; - @:op(A <= B) public static function lte9(lhs : system.Int16, rhs : system.Int64) : system.Boolean; - @:op(A <= B) public static function lte10(lhs : system.Int16, rhs : system.SByte) : system.Boolean; - @:op(A <= B) public static function lte11(lhs : system.Int16, rhs : system.UInt16) : system.Boolean; - @:op(A <= B) public static function lte12(lhs : system.Int16, rhs : system.UInt32) : system.Boolean; - @:op(A <= B) public static function lte13(lhs : system.Int16, rhs : system.UInt64) : system.Boolean; - @:op(A <= B) public static function lte14(lhs : system.Int16, rhs : system.Single) : system.Boolean; - @:op(A <= B) public static function lte15(lhs : system.Int16, rhs : system.Double) : system.Boolean; - - - @:op(A == B) public static function eq0(lhs : system.Int16, rhs : system.Int16) : system.Boolean; - @:op(A == B) public static function eq1(lhs : system.Int16, rhs : Int) : system.Boolean; - @:op(A == B) public static function eq2(lhs : Int, rhs : system.Int16) : system.Boolean; - @:op(A == B) public static function eq3(lhs : system.Int16, rhs : Float) : system.Boolean; - @:op(A == B) public static function eq4(lhs : Float, rhs : system.Int16) : system.Boolean; - - @:op(A == B) public static function eq5(lhs : system.Int16, rhs : system.Char) : system.Boolean; - @:op(A == B) public static function eq6(lhs : system.Int16, rhs : system.Byte) : system.Boolean; - @:op(A == B) public static function eq7(lhs : system.Int16, rhs : system.Int16) : system.Boolean; - @:op(A == B) public static function eq8(lhs : system.Int16, rhs : system.Int32) : system.Boolean; - @:op(A == B) public static function eq9(lhs : system.Int16, rhs : system.Int64) : system.Boolean; - @:op(A == B) public static function eq10(lhs : system.Int16, rhs : system.SByte) : system.Boolean; - @:op(A == B) public static function eq11(lhs : system.Int16, rhs : system.UInt16) : system.Boolean; - @:op(A == B) public static function eq12(lhs : system.Int16, rhs : system.UInt32) : system.Boolean; - @:op(A == B) public static function eq13(lhs : system.Int16, rhs : system.UInt64) : system.Boolean; - @:op(A == B) public static function eq14(lhs : system.Int16, rhs : system.Single) : system.Boolean; - @:op(A == B) public static function eq15(lhs : system.Int16, rhs : system.Double) : system.Boolean; - - - @:op(A != B) public static function neq0(lhs : system.Int16, rhs : system.Int16) : system.Boolean; - @:op(A != B) public static function neq1(lhs : system.Int16, rhs : Int) : system.Boolean; - @:op(A != B) public static function neq2(lhs : Int, rhs : system.Int16) : system.Boolean; - @:op(A != B) public static function neq3(lhs : system.Int16, rhs : Float) : system.Boolean; - @:op(A != B) public static function neq4(lhs : Float, rhs : system.Int16) : system.Boolean; - - @:op(A != B) public static function neq5(lhs : system.Int16, rhs : system.Char) : system.Boolean; - @:op(A != B) public static function neq6(lhs : system.Int16, rhs : system.Byte) : system.Boolean; - @:op(A != B) public static function neq7(lhs : system.Int16, rhs : system.Int16) : system.Boolean; - @:op(A != B) public static function neq8(lhs : system.Int16, rhs : system.Int32) : system.Boolean; - @:op(A != B) public static function neq9(lhs : system.Int16, rhs : system.Int64) : system.Boolean; - @:op(A != B) public static function neq10(lhs : system.Int16, rhs : system.SByte) : system.Boolean; - @:op(A != B) public static function neq11(lhs : system.Int16, rhs : system.UInt16) : system.Boolean; - @:op(A != B) public static function neq12(lhs : system.Int16, rhs : system.UInt32) : system.Boolean; - @:op(A != B) public static function neq13(lhs : system.Int16, rhs : system.UInt64) : system.Boolean; - @:op(A != B) public static function neq14(lhs : system.Int16, rhs : system.Single) : system.Boolean; - @:op(A != B) public static function neq15(lhs : system.Int16, rhs : system.Double) : system.Boolean; - - - @:op(A & B) public static function and0(lhs : system.Int16, rhs : system.Int16) : system.Int32; - @:op(A & B) public static function and1(lhs : system.Int16, rhs : Int) : system.Int32; - @:op(A & B) public static function and2(lhs : Int, rhs : system.Int16) : system.Int32; - - @:op(A & B) public static function and5(lhs : system.Int16, rhs : system.Char) : system.Int32; - @:op(A & B) public static function and6(lhs : system.Int16, rhs : system.Byte) : system.Int32; - @:op(A & B) public static function and7(lhs : system.Int16, rhs : system.Int16) : system.Int32; - @:op(A & B) public static function and8(lhs : system.Int16, rhs : system.Int32) : system.Int32; - @:op(A & B) public static function and9(lhs : system.Int16, rhs : system.Int64) : system.Int64; - @:op(A & B) public static function and10(lhs : system.Int16, rhs : system.SByte) : system.Int32; - @:op(A & B) public static function and11(lhs : system.Int16, rhs : system.UInt16) : system.Int32; - @:op(A & B) public static function and12(lhs : system.Int16, rhs : system.UInt32) : system.Int64; - //@:op(A & B) public static function and13(lhs : system.Int16, rhs : system.UInt64) : system.UInt64; - - - @:op(A | B) public static function or0(lhs : system.Int16, rhs : system.Int16) : system.Int32; - @:op(A | B) public static function or1(lhs : system.Int16, rhs : Int) : system.Int32; - @:op(A | B) public static function or2(lhs : Int, rhs : system.Int16) : system.Int32; - - @:op(A | B) public static function or5(lhs : system.Int16, rhs : system.Char) : system.Int32; - @:op(A | B) public static function or6(lhs : system.Int16, rhs : system.Byte) : system.Int32; - @:op(A | B) public static function or7(lhs : system.Int16, rhs : system.Int16) : system.Int32; - @:op(A | B) public static function or8(lhs : system.Int16, rhs : system.Int32) : system.Int32; - @:op(A | B) public static function or9(lhs : system.Int16, rhs : system.Int64) : system.Int64; - @:op(A | B) public static function or10(lhs : system.Int16, rhs : system.SByte) : system.Int32; - @:op(A | B) public static function or11(lhs : system.Int16, rhs : system.UInt16) : system.Int32; - @:op(A | B) public static function or12(lhs : system.Int16, rhs : system.UInt32) : system.Int64; - //@:op(A | B) public static function or13(lhs : system.Int16, rhs : system.UInt64) : system.UInt64; - - @:op(A ^ B) public static function xor0(lhs : system.Int16, rhs : system.Int16) : system.Int32; - @:op(A ^ B) public static function xor1(lhs : system.Int16, rhs : Int) : system.Int32; - @:op(A ^ B) public static function xor2(lhs : Int, rhs : system.Int16) : system.Int32; - - @:op(A ^ B) public static function xor5(lhs : system.Int16, rhs : system.Char) : system.Int32; - @:op(A ^ B) public static function xor6(lhs : system.Int16, rhs : system.Byte) : system.Int32; - @:op(A ^ B) public static function xor7(lhs : system.Int16, rhs : system.Int16) : system.Int32; - @:op(A ^ B) public static function xor8(lhs : system.Int16, rhs : system.Int32) : system.Int32; - @:op(A ^ B) public static function xor9(lhs : system.Int16, rhs : system.Int64) : system.Int64; - @:op(A ^ B) public static function xor10(lhs : system.Int16, rhs : system.SByte) : system.Int32; - @:op(A ^ B) public static function xor11(lhs : system.Int16, rhs : system.UInt16) : system.Int32; - @:op(A ^ B) public static function xor12(lhs : system.Int16, rhs : system.UInt32) : system.Int64; - //@:op(A ^ B) public static function xor13(lhs : system.Int16, rhs : system.UInt64) : system.UInt64; - -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/Int16Array.hx b/Phase/Mscorlib/system/Int16Array.hx deleted file mode 100644 index 682504aec..000000000 --- a/Phase/Mscorlib/system/Int16Array.hx +++ /dev/null @@ -1,21 +0,0 @@ -package system; - -abstract Int16Array(js.html.Int16Array) -{ - public inline function new(length:Int32) this = new js.html.Int16Array(length.ToHaxeInt()); - - @:from public static inline function fromArray(a:Array):Int16Array return cast new js.html.Int16Array(untyped a); - @:from public static inline function fromFixedArray(a:FixedArray):Int16Array return cast new js.html.Int16Array(untyped a.ToHaxeArray()); - - public var Length(get, never):Int32; - public inline function get_Length() : Int32 return this.length; - - @:op([]) public inline function get(index:Int32):Int16 return this[index.ToHaxeInt()]; - @:op([]) public inline function set(index:Int32, val:Int16):Int16 return this[index.ToHaxeInt()] = val.ToHaxeInt(); - - public inline function iterator() : Iterator return new Int16ArrayIterator(this); - - public inline function ToEnumerable() : system.collections.generic.IEnumerable return new Int16ArrayEnumerable(this); - - public static inline function empty(size:Int32) return new Int16Array(size); -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/Int16ArrayEnumerable.hx b/Phase/Mscorlib/system/Int16ArrayEnumerable.hx deleted file mode 100644 index b499c4fd4..000000000 --- a/Phase/Mscorlib/system/Int16ArrayEnumerable.hx +++ /dev/null @@ -1,18 +0,0 @@ -package system; - -import system.collections.generic.IEnumerable; -import system.collections.generic.IEnumerator; - -class Int16ArrayEnumerable implements IEnumerable -{ - private var _array:js.html.Int16Array; - public function new(array:js.html.Int16Array) - { - _array= array; - } - - public function GetEnumerator() : IEnumerator - { - return new Int16ArrayEnumerator(_array); - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/Int16ArrayEnumerator.hx b/Phase/Mscorlib/system/Int16ArrayEnumerator.hx deleted file mode 100644 index 99688cf22..000000000 --- a/Phase/Mscorlib/system/Int16ArrayEnumerator.hx +++ /dev/null @@ -1,29 +0,0 @@ -package system; - -import system.collections.generic.IEnumerator; - -class Int16ArrayEnumerator implements IEnumerator -{ - private var _array:js.html.Int16Array; - private var _i:Int; - - public function new(array:js.html.Int16Array) - { - _array = array; - _i = -1; - } - - public var Current(get, never):Int16; - public function get_Current() : Int16 return _array[_i]; - public function MoveNext() : Bool - { - if(_i >= _array.length - 1) return false; - _i++; - return true; - - } - public function Reset():Void - { - _i = -1; - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/Int16ArrayIterator.hx b/Phase/Mscorlib/system/Int16ArrayIterator.hx deleted file mode 100644 index b4713fe40..000000000 --- a/Phase/Mscorlib/system/Int16ArrayIterator.hx +++ /dev/null @@ -1,19 +0,0 @@ -package system; - -class Int16ArrayIterator -{ - private var _array:js.html.Int16Array; - private var _i:Int; - - public function new(array:js.html.Int16Array) - { - _array = array; - _i = 0; - } - - public function hasNext() return _i < _array.length; - public function next() : Int16 - { - return _array[_i++]; - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/Int32.hx b/Phase/Mscorlib/system/Int32.hx deleted file mode 100644 index 783a310fc..000000000 --- a/Phase/Mscorlib/system/Int32.hx +++ /dev/null @@ -1,329 +0,0 @@ -package system; - -abstract Int32(Int) from Int -{ - public inline function new(i:Int) this = i; - - public inline function ToHaxeInt(): Int return this; - public inline function ToString() : system.CsString return Std.string(this); - - @:from public static inline function FromChar(c:Char) : Int32 return new Int32(c.ToHaxeInt()); - @:from public static inline function FromByte(c:Byte) : Int32 return new Int32(c.ToHaxeInt()); - @:from public static inline function FromSByte(c:SByte) : Int32 return new Int32(c.ToHaxeInt()); - @:from public static inline function FromInt16(c:Int16) : Int32 return new Int32(c.ToHaxeInt()); - - public inline function ToBoolean_IFormatProvider(provider:IFormatProvider) : system.Boolean return system.Convert.ToBoolean_Int32(this); - public inline function ToChar_IFormatProvider(provider:IFormatProvider) : system.Char return system.Convert.ToChar_Int32(this); - public inline function ToSByte_IFormatProvider(provider:IFormatProvider) : system.SByte return system.Convert.ToSByte_Int32(this); - public inline function ToByte_IFormatProvider(provider:IFormatProvider) : system.Byte return system.Convert.ToByte_Int32(this); - public inline function ToInt16_IFormatProvider(provider:IFormatProvider) : system.Int16 return system.Convert.ToInt16_Int32(this); - public inline function ToUInt16_IFormatProvider(provider:IFormatProvider) : system.UInt16 return system.Convert.ToUInt16_Int32(this); - public inline function ToInt32_IFormatProvider(provider:IFormatProvider) : system.Int32 return system.Convert.ToInt32_Int32(this); - public inline function ToUInt32_IFormatProvider(provider:IFormatProvider) : system.UInt32 return system.Convert.ToUInt32_Int32(this); - public inline function ToInt64_IFormatProvider(provider:IFormatProvider) : system.Int64 return system.Convert.ToInt64_Int32(this); - public inline function ToUInt64_IFormatProvider(provider:IFormatProvider) : system.UInt64 return system.Convert.ToUInt64_Int32(this); - public inline function ToSingle_IFormatProvider(provider:IFormatProvider) : system.Single return system.Convert.ToSingle_Int32(this); - public inline function ToDouble_IFormatProvider(provider:IFormatProvider) : system.Double return system.Convert.ToDouble_Int32(this); - - public inline function GetHashCode() : system.Int32 return this; - - public function CompareTo_Int32(other:Int32) : Int32 - { - if (this < other) return -1; - if (this > other) return 1; - return 0; - } - - - @:op(-A) public inline function neg() : system.Int32 return -this; - - @:op(~A) public inline function not() : system.Int32 return ~this; - - @:op(A++) public inline function postinc() : system.Int32 return this++; - @:op(++A) public inline function preinc() : system.Int32 return ++this; - - @:op(A--) public inline function postdec() : system.Int32 return this--; - @:op(--A) public inline function predec() : system.Int32 return --this; - - @:op(A * B) public static function mul0(lhs : system.Int32, rhs : system.Int32) : system.Int32; - @:op(A * B) public static function mul1(lhs : system.Int32, rhs : Int) : system.Int32; - @:op(A * B) public static function mul2(lhs : Int, rhs : system.Int32) : system.Int32; - @:op(A * B) public static function mul3(lhs : system.Int32, rhs : Float) : system.Double; - @:op(A * B) public static function mul4(lhs : Float, rhs : system.Int32) : system.Double; - - @:op(A * B) public static function mul5(lhs : system.Int32, rhs : system.Char) : system.Int32; - @:op(A * B) public static function mul6(lhs : system.Int32, rhs : system.Byte) : system.Int32; - @:op(A * B) public static function mul7(lhs : system.Int32, rhs : system.Int16) : system.Int32; - @:op(A * B) public static function mul8(lhs : system.Int32, rhs : system.Int32) : system.Int32; - @:op(A * B) public static function mul9(lhs : system.Int32, rhs : system.Int64) : system.Int64; - @:op(A * B) public static function mul10(lhs : system.Int32, rhs : system.SByte) : system.Int32; - @:op(A * B) public static function mul11(lhs : system.Int32, rhs : system.UInt16) : system.Int32; - @:op(A * B) public static function mul12(lhs : system.Int32, rhs : system.UInt32) : system.Int64; - // @:op(A * B) public static function mul13(lhs : system.Int32, rhs : system.UInt64) : system.UInt64; - @:op(A * B) public static function mul14(lhs : system.Int32, rhs : system.Single) : system.Single; - @:op(A * B) public static function mul15(lhs : system.Int32, rhs : system.Double) : system.Double; - - - @:op(A / B) public static inline function div0(lhs : system.Int32, rhs : system.Int32) : system.Int32 return Std.int(lhs.ToHaxeInt() / rhs.ToHaxeInt()); - @:op(A / B) public static inline function div1(lhs : system.Int32, rhs : Int) : system.Int32 return Std.int(lhs.ToHaxeInt() / rhs); - @:op(A / B) public static inline function div2(lhs : Int, rhs : system.Int32) : system.Int32 return Std.int(lhs / rhs.ToHaxeInt()); - @:op(A / B) public static function div3(lhs : system.Int32, rhs : Float) : system.Double; - @:op(A / B) public static function div4(lhs : Float, rhs : system.Int32) : system.Double; - - @:op(A / B) public static function div5(lhs : system.Int32, rhs : system.Char) : system.Int32; - @:op(A / B) public static function div6(lhs : system.Int32, rhs : system.Byte) : system.Int32; - @:op(A / B) public static function div7(lhs : system.Int32, rhs : system.Int16) : system.Int32; - @:op(A / B) public static function div8(lhs : system.Int32, rhs : system.Int32) : system.Int32; - @:op(A / B) public static function div9(lhs : system.Int32, rhs : system.Int64) : system.Int64; - @:op(A / B) public static function div10(lhs : system.Int32, rhs : system.SByte) : system.Int32; - @:op(A / B) public static function div11(lhs : system.Int32, rhs : system.UInt16) : system.Int32; - @:op(A / B) public static function div12(lhs : system.Int32, rhs : system.UInt32) : system.Int64; - // @:op(A / B) public static function div13(lhs : system.Int32, rhs : system.UInt64) : system.UInt64; - @:op(A / B) public static function div14(lhs : system.Int32, rhs : system.Single) : system.Single; - @:op(A / B) public static function div15(lhs : system.Int32, rhs : system.Double) : system.Double; - - - @:op(A % B) public static function mod0(lhs : system.Int32, rhs : system.Int32) : system.Int32; - @:op(A % B) public static function mod1(lhs : system.Int32, rhs : Int) : system.Int32; - @:op(A % B) public static function mod2(lhs : Int, rhs : system.Int32) : system.Int32; - @:op(A % B) public static function mod3(lhs : system.Int32, rhs : Float) : system.Double; - @:op(A % B) public static function mod4(lhs : Float, rhs : system.Int32) : system.Double; - - @:op(A % B) public static function mod5(lhs : system.Int32, rhs : system.Char) : system.Int32; - @:op(A % B) public static function mod6(lhs : system.Int32, rhs : system.Byte) : system.Int32; - @:op(A % B) public static function mod7(lhs : system.Int32, rhs : system.Int16) : system.Int32; - @:op(A % B) public static function mod8(lhs : system.Int32, rhs : system.Int32) : system.Int32; - @:op(A % B) public static function mod9(lhs : system.Int32, rhs : system.Int64) : system.Int64; - @:op(A % B) public static function mod10(lhs : system.Int32, rhs : system.SByte) : system.Int32; - @:op(A % B) public static function mod11(lhs : system.Int32, rhs : system.UInt16) : system.Int32; - @:op(A % B) public static function mod12(lhs : system.Int32, rhs : system.UInt32) : system.Int64; - // @:op(A % B) public static function mod13(lhs : system.Int32, rhs : system.UInt64) : system.UInt64; - @:op(A % B) public static function mod14(lhs : system.Int32, rhs : system.Single) : system.Single; - @:op(A % B) public static function mod15(lhs : system.Int32, rhs : system.Double) : system.Double; - - - @:op(A + B) public static function add0(lhs : system.Int32, rhs : system.Int32) : system.Int32; - @:op(A + B) public static function add1(lhs : system.Int32, rhs : Int) : system.Int32; - @:op(A + B) public static function add2(lhs : Int, rhs : system.Int32) : system.Int32; - @:op(A + B) public static function add3(lhs : system.Int32, rhs : Float) : system.Double; - @:op(A + B) public static function add4(lhs : Float, rhs : system.Int32) : system.Double; - - @:op(A + B) public static function add5(lhs : system.Int32, rhs : system.Char) : system.Int32; - @:op(A + B) public static function add6(lhs : system.Int32, rhs : system.Byte) : system.Int32; - @:op(A + B) public static function add7(lhs : system.Int32, rhs : system.Int16) : system.Int32; - @:op(A + B) public static function add8(lhs : system.Int32, rhs : system.Int32) : system.Int32; - @:op(A + B) public static function add9(lhs : system.Int32, rhs : system.Int64) : system.Int64; - @:op(A + B) public static function add10(lhs : system.Int32, rhs : system.SByte) : system.Int32; - @:op(A + B) public static function add11(lhs : system.Int32, rhs : system.UInt16) : system.Int32; - @:op(A + B) public static function add12(lhs : system.Int32, rhs : system.UInt32) : system.Int64; - // @:op(A + B) public static function add13(lhs : system.Int32, rhs : system.UInt64) : system.UInt64; - @:op(A + B) public static function add14(lhs : system.Int32, rhs : system.Single) : system.Single; - @:op(A + B) public static function add15(lhs : system.Int32, rhs : system.Double) : system.Double; - @:op(A + B) public static inline function add16(lhs : system.Int32, rhs : system.CsString) : system.CsString return lhs.ToString() + rhs; - @:op(A + B) public static inline function add17(lhs : system.Int32, rhs : String) : system.CsString return lhs.ToString() + rhs; - - - @:op(A - B) public static function sub0(lhs : system.Int32, rhs : system.Int32) : system.Int32; - @:op(A - B) public static function sub1(lhs : system.Int32, rhs : Int) : system.Int32; - @:op(A - B) public static function sub2(lhs : Int, rhs : system.Int32) : system.Int32; - @:op(A - B) public static function sub3(lhs : system.Int32, rhs : Float) : system.Double; - @:op(A - B) public static function sub4(lhs : Float, rhs : system.Int32) : system.Double; - - @:op(A - B) public static function sub5(lhs : system.Int32, rhs : system.Char) : system.Int32; - @:op(A - B) public static function sub6(lhs : system.Int32, rhs : system.Byte) : system.Int32; - @:op(A - B) public static function sub7(lhs : system.Int32, rhs : system.Int16) : system.Int32; - @:op(A - B) public static function sub8(lhs : system.Int32, rhs : system.Int32) : system.Int32; - @:op(A - B) public static function sub9(lhs : system.Int32, rhs : system.Int64) : system.Int64; - @:op(A - B) public static function sub10(lhs : system.Int32, rhs : system.SByte) : system.Int32; - @:op(A - B) public static function sub11(lhs : system.Int32, rhs : system.UInt16) : system.Int32; - @:op(A - B) public static function sub12(lhs : system.Int32, rhs : system.UInt32) : system.Int64; - // @:op-A - B) public static function sub13(lhs : system.Int32, rhs : system.UInt64) : system.UInt64; - @:op(A - B) public static function sub14(lhs : system.Int32, rhs : system.Single) : system.Single; - @:op(A - B) public static function sub15(lhs : system.Int32, rhs : system.Double) : system.Double; - - - @:op(A << B) public static function shl0(lhs : system.Int32, rhs : system.Int32) : system.Int32; - @:op(A << B) public static function shl1(lhs : system.Int32, rhs : Int) : system.Int32; - @:op(A << B) public static function shl2(lhs : Int, rhs : system.Int32) : system.Int32; - - @:op(A << B) public static function shl5(lhs : system.Int32, rhs : system.Char) : system.Int32; - @:op(A << B) public static function shl6(lhs : system.Int32, rhs : system.Byte) : system.Int32; - @:op(A << B) public static function shl7(lhs : system.Int32, rhs : system.Int16) : system.Int32; - @:op(A << B) public static function shl8(lhs : system.Int32, rhs : system.Int32) : system.Int32; - // @:op(A << B) public static function shl9(lhs : system.Int32, rhs : system.Int64) : system.Int64; - @:op(A << B) public static function shl10(lhs : system.Int32, rhs : system.SByte) : system.Int32; - @:op(A << B) public static function shl11(lhs : system.Int32, rhs : system.UInt16) : system.Int32; - // @:op(A << B) public static function shl12(lhs : system.Int32, rhs : system.UInt32) : system.Int64; - // @:op-A << B) public static function shl13(lhs : system.Int32, rhs : system.UInt64) : system.UInt64; - - - @:op(A >> B) public static function shr0(lhs : system.Int32, rhs : system.Int32) : system.Int32; - @:op(A >> B) public static function shr1(lhs : system.Int32, rhs : Int) : system.Int32; - @:op(A >> B) public static function shr2(lhs : Int, rhs : system.Int32) : system.Int32; - - @:op(A >> B) public static function shr5(lhs : system.Int32, rhs : system.Char) : system.Int32; - @:op(A >> B) public static function shr6(lhs : system.Int32, rhs : system.Byte) : system.Int32; - @:op(A >> B) public static function shr7(lhs : system.Int32, rhs : system.Int16) : system.Int32; - @:op(A >> B) public static function shr8(lhs : system.Int32, rhs : system.Int32) : system.Int32; - // @:op(A >> B) public static function shr9(lhs : system.Int32, rhs : system.Int64) : system.Int64; - @:op(A >> B) public static function shr10(lhs : system.Int32, rhs : system.SByte) : system.Int32; - @:op(A >> B) public static function shr11(lhs : system.Int32, rhs : system.UInt16) : system.Int32; - // @:op(A >> B) public static function shr12(lhs : system.Int32, rhs : system.UInt32) : system.Int64; - // @:op-A >> B) public static function shr13(lhs : system.Int32, rhs : system.UInt64) : system.UInt64; - - - @:op(A > B) public static function gt0(lhs : system.Int32, rhs : system.Int32) : system.Boolean; - @:op(A > B) public static function gt1(lhs : system.Int32, rhs : Int) : system.Boolean; - @:op(A > B) public static function gt2(lhs : Int, rhs : system.Int32) : system.Boolean; - @:op(A > B) public static function gt3(lhs : system.Int32, rhs : Float) : system.Boolean; - @:op(A > B) public static function gt4(lhs : Float, rhs : system.Int32) : system.Boolean; - - @:op(A > B) public static function gt5(lhs : system.Int32, rhs : system.Char) : system.Boolean; - @:op(A > B) public static function gt6(lhs : system.Int32, rhs : system.Byte) : system.Boolean; - @:op(A > B) public static function gt7(lhs : system.Int32, rhs : system.Int16) : system.Boolean; - @:op(A > B) public static function gt8(lhs : system.Int32, rhs : system.Int32) : system.Boolean; - @:op(A > B) public static function gt9(lhs : system.Int32, rhs : system.Int64) : system.Boolean; - @:op(A > B) public static function gt10(lhs : system.Int32, rhs : system.SByte) : system.Boolean; - @:op(A > B) public static function gt11(lhs : system.Int32, rhs : system.UInt16) : system.Boolean; - @:op(A > B) public static function gt12(lhs : system.Int32, rhs : system.UInt32) : system.Boolean; - @:op(A > B) public static function gt13(lhs : system.Int32, rhs : system.UInt64) : system.Boolean; - @:op(A > B) public static function gt14(lhs : system.Int32, rhs : system.Single) : system.Boolean; - @:op(A > B) public static function gt15(lhs : system.Int32, rhs : system.Double) : system.Boolean; - - - @:op(A < B) public static function lt0(lhs : system.Int32, rhs : system.Int32) : system.Boolean; - @:op(A < B) public static function lt1(lhs : system.Int32, rhs : Int) : system.Boolean; - @:op(A < B) public static function lt2(lhs : Int, rhs : system.Int32) : system.Boolean; - @:op(A < B) public static function lt3(lhs : system.Int32, rhs : Float) : system.Boolean; - @:op(A < B) public static function lt4(lhs : Float, rhs : system.Int32) : system.Boolean; - - @:op(A < B) public static function lt5(lhs : system.Int32, rhs : system.Char) : system.Boolean; - @:op(A < B) public static function lt6(lhs : system.Int32, rhs : system.Byte) : system.Boolean; - @:op(A < B) public static function lt7(lhs : system.Int32, rhs : system.Int16) : system.Boolean; - @:op(A < B) public static function lt8(lhs : system.Int32, rhs : system.Int32) : system.Boolean; - @:op(A < B) public static function lt9(lhs : system.Int32, rhs : system.Int64) : system.Boolean; - @:op(A < B) public static function lt10(lhs : system.Int32, rhs : system.SByte) : system.Boolean; - @:op(A < B) public static function lt11(lhs : system.Int32, rhs : system.UInt16) : system.Boolean; - @:op(A < B) public static function lt12(lhs : system.Int32, rhs : system.UInt32) : system.Boolean; - @:op(A < B) public static function lt13(lhs : system.Int32, rhs : system.UInt64) : system.Boolean; - @:op(A < B) public static function lt14(lhs : system.Int32, rhs : system.Single) : system.Boolean; - @:op(A < B) public static function lt15(lhs : system.Int32, rhs : system.Double) : system.Boolean; - - - @:op(A >= B) public static function gte0(lhs : system.Int32, rhs : system.Int32) : system.Boolean; - @:op(A >= B) public static function gte1(lhs : system.Int32, rhs : Int) : system.Boolean; - @:op(A >= B) public static function gte2(lhs : Int, rhs : system.Int32) : system.Boolean; - @:op(A >= B) public static function gte3(lhs : system.Int32, rhs : Float) : system.Boolean; - @:op(A >= B) public static function gte4(lhs : Float, rhs : system.Int32) : system.Boolean; - - @:op(A >= B) public static function gte5(lhs : system.Int32, rhs : system.Char) : system.Boolean; - @:op(A >= B) public static function gte6(lhs : system.Int32, rhs : system.Byte) : system.Boolean; - @:op(A >= B) public static function gte7(lhs : system.Int32, rhs : system.Int16) : system.Boolean; - @:op(A >= B) public static function gte8(lhs : system.Int32, rhs : system.Int32) : system.Boolean; - @:op(A >= B) public static function gte9(lhs : system.Int32, rhs : system.Int64) : system.Boolean; - @:op(A >= B) public static function gte10(lhs : system.Int32, rhs : system.SByte) : system.Boolean; - @:op(A >= B) public static function gte11(lhs : system.Int32, rhs : system.UInt16) : system.Boolean; - @:op(A >= B) public static function gte12(lhs : system.Int32, rhs : system.UInt32) : system.Boolean; - @:op(A >= B) public static function gte13(lhs : system.Int32, rhs : system.UInt64) : system.Boolean; - @:op(A >= B) public static function gte14(lhs : system.Int32, rhs : system.Single) : system.Boolean; - @:op(A >= B) public static function gte15(lhs : system.Int32, rhs : system.Double) : system.Boolean; - - - @:op(A <= B) public static function lte0(lhs : system.Int32, rhs : system.Int32) : system.Boolean; - @:op(A <= B) public static function lte1(lhs : system.Int32, rhs : Int) : system.Boolean; - @:op(A <= B) public static function lte2(lhs : Int, rhs : system.Int32) : system.Boolean; - @:op(A <= B) public static function lte3(lhs : system.Int32, rhs : Float) : system.Boolean; - @:op(A <= B) public static function lte4(lhs : Float, rhs : system.Int32) : system.Boolean; - - @:op(A <= B) public static function lte5(lhs : system.Int32, rhs : system.Char) : system.Boolean; - @:op(A <= B) public static function lte6(lhs : system.Int32, rhs : system.Byte) : system.Boolean; - @:op(A <= B) public static function lte7(lhs : system.Int32, rhs : system.Int16) : system.Boolean; - @:op(A <= B) public static function lte8(lhs : system.Int32, rhs : system.Int32) : system.Boolean; - @:op(A <= B) public static function lte9(lhs : system.Int32, rhs : system.Int64) : system.Boolean; - @:op(A <= B) public static function lte10(lhs : system.Int32, rhs : system.SByte) : system.Boolean; - @:op(A <= B) public static function lte11(lhs : system.Int32, rhs : system.UInt16) : system.Boolean; - @:op(A <= B) public static function lte12(lhs : system.Int32, rhs : system.UInt32) : system.Boolean; - @:op(A <= B) public static function lte13(lhs : system.Int32, rhs : system.UInt64) : system.Boolean; - @:op(A <= B) public static function lte14(lhs : system.Int32, rhs : system.Single) : system.Boolean; - @:op(A <= B) public static function lte15(lhs : system.Int32, rhs : system.Double) : system.Boolean; - - - @:op(A == B) public static function eq0(lhs : system.Int32, rhs : system.Int32) : system.Boolean; - @:op(A == B) public static function eq1(lhs : system.Int32, rhs : Int) : system.Boolean; - @:op(A == B) public static function eq2(lhs : Int, rhs : system.Int32) : system.Boolean; - @:op(A == B) public static function eq3(lhs : system.Int32, rhs : Float) : system.Boolean; - @:op(A == B) public static function eq4(lhs : Float, rhs : system.Int32) : system.Boolean; - - @:op(A == B) public static function eq5(lhs : system.Int32, rhs : system.Char) : system.Boolean; - @:op(A == B) public static function eq6(lhs : system.Int32, rhs : system.Byte) : system.Boolean; - @:op(A == B) public static function eq7(lhs : system.Int32, rhs : system.Int16) : system.Boolean; - @:op(A == B) public static function eq8(lhs : system.Int32, rhs : system.Int32) : system.Boolean; - @:op(A == B) public static function eq9(lhs : system.Int32, rhs : system.Int64) : system.Boolean; - @:op(A == B) public static function eq10(lhs : system.Int32, rhs : system.SByte) : system.Boolean; - @:op(A == B) public static function eq11(lhs : system.Int32, rhs : system.UInt16) : system.Boolean; - @:op(A == B) public static function eq12(lhs : system.Int32, rhs : system.UInt32) : system.Boolean; - @:op(A == B) public static function eq13(lhs : system.Int32, rhs : system.UInt64) : system.Boolean; - @:op(A == B) public static function eq14(lhs : system.Int32, rhs : system.Single) : system.Boolean; - @:op(A == B) public static function eq15(lhs : system.Int32, rhs : system.Double) : system.Boolean; - - - @:op(A != B) public static function neq0(lhs : system.Int32, rhs : system.Int32) : system.Boolean; - @:op(A != B) public static function neq1(lhs : system.Int32, rhs : Int) : system.Boolean; - @:op(A != B) public static function neq2(lhs : Int, rhs : system.Int32) : system.Boolean; - @:op(A != B) public static function neq3(lhs : system.Int32, rhs : Float) : system.Boolean; - @:op(A != B) public static function neq4(lhs : Float, rhs : system.Int32) : system.Boolean; - - @:op(A != B) public static function neq5(lhs : system.Int32, rhs : system.Char) : system.Boolean; - @:op(A != B) public static function neq6(lhs : system.Int32, rhs : system.Byte) : system.Boolean; - @:op(A != B) public static function neq7(lhs : system.Int32, rhs : system.Int16) : system.Boolean; - @:op(A != B) public static function neq8(lhs : system.Int32, rhs : system.Int32) : system.Boolean; - @:op(A != B) public static function neq9(lhs : system.Int32, rhs : system.Int64) : system.Boolean; - @:op(A != B) public static function neq10(lhs : system.Int32, rhs : system.SByte) : system.Boolean; - @:op(A != B) public static function neq11(lhs : system.Int32, rhs : system.UInt16) : system.Boolean; - @:op(A != B) public static function neq12(lhs : system.Int32, rhs : system.UInt32) : system.Boolean; - @:op(A != B) public static function neq13(lhs : system.Int32, rhs : system.UInt64) : system.Boolean; - @:op(A != B) public static function neq14(lhs : system.Int32, rhs : system.Single) : system.Boolean; - @:op(A != B) public static function neq15(lhs : system.Int32, rhs : system.Double) : system.Boolean; - - @:op(A & B) public static function and0(lhs : system.Int32, rhs : system.Int32) : system.Int32; - @:op(A & B) public static function and1(lhs : system.Int32, rhs : Int) : system.Int32; - @:op(A & B) public static function and2(lhs : Int, rhs : system.Int32) : system.Int32; - - @:op(A & B) public static function and5(lhs : system.Int32, rhs : system.Char) : system.Int32; - @:op(A & B) public static function and6(lhs : system.Int32, rhs : system.Byte) : system.Int32; - @:op(A & B) public static function and7(lhs : system.Int32, rhs : system.Int16) : system.Int32; - @:op(A & B) public static function and8(lhs : system.Int32, rhs : system.Int32) : system.Int32; - @:op(A & B) public static function and9(lhs : system.Int32, rhs : system.Int64) : system.Int64; - @:op(A & B) public static function and10(lhs : system.Int32, rhs : system.SByte) : system.Int32; - @:op(A & B) public static function and11(lhs : system.Int32, rhs : system.UInt16) : system.Int32; - @:op(A & B) public static function and12(lhs : system.Int32, rhs : system.UInt32) : system.Int64; - //@:op(A & B) public static function and13(lhs : system.Int32, rhs : system.UInt64) : system.UInt64; - - @:op(A | B) public static function or0(lhs : system.Int32, rhs : system.Int32) : system.Int32; - @:op(A | B) public static function or1(lhs : system.Int32, rhs : Int) : system.Int32; - @:op(A | B) public static function or2(lhs : Int, rhs : system.Int32) : system.Int32; - - @:op(A | B) public static function or5(lhs : system.Int32, rhs : system.Char) : system.Int32; - @:op(A | B) public static function or6(lhs : system.Int32, rhs : system.Byte) : system.Int32; - @:op(A | B) public static function or7(lhs : system.Int32, rhs : system.Int16) : system.Int32; - @:op(A | B) public static function or8(lhs : system.Int32, rhs : system.Int32) : system.Int32; - @:op(A | B) public static function or9(lhs : system.Int32, rhs : system.Int64) : system.Int64; - @:op(A | B) public static function or10(lhs : system.Int32, rhs : system.SByte) : system.Int32; - @:op(A | B) public static function or11(lhs : system.Int32, rhs : system.UInt16) : system.Int32; - @:op(A | B) public static function or12(lhs : system.Int32, rhs : system.UInt32) : system.Int64; - //@:op(A | B) public static function or13(lhs : system.Int32, rhs : system.UInt64) : system.UInt64; - - @:op(A ^ B) public static function xor0(lhs : system.Int32, rhs : system.Int32) : system.Int32; - @:op(A ^ B) public static function xor1(lhs : system.Int32, rhs : Int) : system.Int32; - @:op(A ^ B) public static function xor2(lhs : Int, rhs : system.Int32) : system.Int32; - - @:op(A ^ B) public static function xor5(lhs : system.Int32, rhs : system.Char) : system.Int32; - @:op(A ^ B) public static function xor6(lhs : system.Int32, rhs : system.Byte) : system.Int32; - @:op(A ^ B) public static function xor7(lhs : system.Int32, rhs : system.Int16) : system.Int32; - @:op(A ^ B) public static function xor8(lhs : system.Int32, rhs : system.Int32) : system.Int32; - @:op(A ^ B) public static function xor9(lhs : system.Int32, rhs : system.Int64) : system.Int64; - @:op(A ^ B) public static function xor10(lhs : system.Int32, rhs : system.SByte) : system.Int32; - @:op(A ^ B) public static function xor11(lhs : system.Int32, rhs : system.UInt16) : system.Int32; - @:op(A ^ B) public static function xor12(lhs : system.Int32, rhs : system.UInt32) : system.Int64; - //@:op(A ^ B) public static function xor13(lhs : system.Int32, rhs : system.UInt64) : system.UInt64; -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/Int32Array.hx b/Phase/Mscorlib/system/Int32Array.hx deleted file mode 100644 index d453caa46..000000000 --- a/Phase/Mscorlib/system/Int32Array.hx +++ /dev/null @@ -1,21 +0,0 @@ -package system; - -abstract Int32Array(js.html.Int32Array) -{ - public inline function new(length:Int32) this = new js.html.Int32Array(length.ToHaxeInt()); - - @:from public static inline function fromArray(a:Array):Int32Array return cast new js.html.Int32Array(untyped a); - @:from public static inline function fromFixedArray(a:FixedArray):Int32Array return cast new js.html.Int32Array(untyped a.ToHaxeArray()); - - public var Length(get, never):Int32; - public inline function get_Length() : Int32 return this.length; - - @:op([]) public inline function get(index:Int32):Int32 return this[index.ToHaxeInt()]; - @:op([]) public inline function set(index:Int32, val:Int32):Int32 return this[index.ToHaxeInt()] = val.ToHaxeInt(); - - public inline function iterator() : Iterator return new Int32ArrayIterator(this); - - @:to public inline function ToEnumerable() : system.collections.generic.IEnumerable return new Int32ArrayEnumerable(this); - - public static inline function empty(size:Int32) return new Int32Array(size); -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/Int32ArrayEnumerable.hx b/Phase/Mscorlib/system/Int32ArrayEnumerable.hx deleted file mode 100644 index d3eb3af92..000000000 --- a/Phase/Mscorlib/system/Int32ArrayEnumerable.hx +++ /dev/null @@ -1,18 +0,0 @@ -package system; - -import system.collections.generic.IEnumerable; -import system.collections.generic.IEnumerator; - -class Int32ArrayEnumerable implements IEnumerable -{ - private var _array:js.html.Int32Array; - public function new(array:js.html.Int32Array) - { - _array= array; - } - - public function GetEnumerator() : IEnumerator - { - return new Int32ArrayEnumerator(_array); - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/Int32ArrayEnumerator.hx b/Phase/Mscorlib/system/Int32ArrayEnumerator.hx deleted file mode 100644 index ece70c33a..000000000 --- a/Phase/Mscorlib/system/Int32ArrayEnumerator.hx +++ /dev/null @@ -1,29 +0,0 @@ -package system; - -import system.collections.generic.IEnumerator; - -class Int32ArrayEnumerator implements IEnumerator -{ - private var _array:js.html.Int32Array; - private var _i:Int; - - public function new(array:js.html.Int32Array) - { - _array = array; - _i = -1; - } - - public var Current(get, never):Int32; - public function get_Current() : Int32 return _array[_i]; - public function MoveNext() : Bool - { - if(_i >= _array.length - 1) return false; - _i++; - return true; - - } - public function Reset():Void - { - _i = -1; - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/Int32ArrayIterator.hx b/Phase/Mscorlib/system/Int32ArrayIterator.hx deleted file mode 100644 index 881f85c6c..000000000 --- a/Phase/Mscorlib/system/Int32ArrayIterator.hx +++ /dev/null @@ -1,19 +0,0 @@ -package system; - -class Int32ArrayIterator -{ - private var _array:js.html.Int32Array; - private var _i:Int; - - public function new(array:js.html.Int32Array) - { - _array = array; - _i = 0; - } - - public function hasNext() return _i < _array.length; - public function next() - { - return _array[_i++]; - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/Int64.hx b/Phase/Mscorlib/system/Int64.hx deleted file mode 100644 index c80228c6f..000000000 --- a/Phase/Mscorlib/system/Int64.hx +++ /dev/null @@ -1,322 +0,0 @@ -package system; - -// TODO: actual 64bit support -abstract Int64(Int) from Int -{ - public inline function new(i:Int) this = i; - - public inline function ToHaxeInt(): Int return this; - public inline function ToString() : system.CsString return Std.string(this); - - @:from public static inline function FromByte(c:Byte) : Int64 return new Int64(c.ToHaxeInt()); - @:from public static inline function FromInt16(c:Int16) : Int64 return new Int64(c.ToHaxeInt()); - @:from public static inline function FromInt32(c:Int32) : Int64 return new Int64(c.ToHaxeInt()); - - public inline function ToBoolean_IFormatProvider(provider:IFormatProvider) : system.Boolean return system.Convert.ToBoolean_Int64(this); - public inline function ToChar_IFormatProvider(provider:IFormatProvider) : system.Char return system.Convert.ToChar_Int64(this); - public inline function ToSByte_IFormatProvider(provider:IFormatProvider) : system.SByte return system.Convert.ToSByte_Int64(this); - public inline function ToByte_IFormatProvider(provider:IFormatProvider) : system.Byte return system.Convert.ToByte_Int64(this); - public inline function ToInt16_IFormatProvider(provider:IFormatProvider) : system.Int16 return system.Convert.ToInt16_Int64(this); - public inline function ToUInt16_IFormatProvider(provider:IFormatProvider) : system.UInt16 return system.Convert.ToUInt16_Int64(this); - public inline function ToInt32_IFormatProvider(provider:IFormatProvider) : system.Int32 return system.Convert.ToInt32_Int64(this); - public inline function ToUInt32_IFormatProvider(provider:IFormatProvider) : system.UInt32 return system.Convert.ToUInt32_Int64(this); - public inline function ToInt64_IFormatProvider(provider:IFormatProvider) : system.Int64 return system.Convert.ToInt64_Int64(this); - public inline function ToUInt64_IFormatProvider(provider:IFormatProvider) : system.UInt64 return system.Convert.ToUInt64_Int64(this); - public inline function ToSingle_IFormatProvider(provider:IFormatProvider) : system.Single return system.Convert.ToSingle_Int64(this); - public inline function ToDouble_IFormatProvider(provider:IFormatProvider) : system.Double return system.Convert.ToDouble_Int64(this); - - public inline function GetHashCode() : system.Int32 return this; - - @:op(-A) public inline function neg() : system.Int64 return -this; - - @:op(~A) public inline function not() : system.Int64 return ~this; - - @:op(A++) public inline function postinc() : system.Int64 return this++; - @:op(++A) public inline function preinc() : system.Int64 return ++this; - - @:op(A--) public inline function postdec() : system.Int64 return this--; - @:op(--A) public inline function predec() : system.Int64 return --this; - - @:op(A * B) public static function mul1(lhs : system.Int64, rhs : Int) : system.Int64; - @:op(A * B) public static function mul2(lhs : Int, rhs : system.Int64) : system.Int64; - @:op(A * B) public static function mul3(lhs : system.Int64, rhs : Float) : system.Double; - @:op(A * B) public static function mul4(lhs : Float, rhs : system.Int64) : system.Double; - - @:op(A * B) public static function mul5(lhs : system.Int64, rhs : system.Char) : system.Int64; - @:op(A * B) public static function mul6(lhs : system.Int64, rhs : system.Byte) : system.Int64; - @:op(A * B) public static function mul7(lhs : system.Int64, rhs : system.Int16) : system.Int64; - @:op(A * B) public static function mul8(lhs : system.Int64, rhs : system.Int32) : system.Int64; - @:op(A * B) public static function mul9(lhs : system.Int64, rhs : system.Int64) : system.Int64; - @:op(A * B) public static function mul10(lhs : system.Int64, rhs : system.SByte) : system.Int64; - @:op(A * B) public static function mul11(lhs : system.Int64, rhs : system.UInt16) : system.Int64; - @:op(A * B) public static function mul12(lhs : system.Int64, rhs : system.UInt32) : system.Int64; - // @:op(A * B) public static function mul13(lhs : system.Int64, rhs : system.UInt64) : system.UInt64; - @:op(A * B) public static function mul14(lhs : system.Int64, rhs : system.Single) : system.Single; - @:op(A * B) public static function mul15(lhs : system.Int64, rhs : system.Double) : system.Double; - - - @:op(A / B) public static inline function div0(lhs : system.Int64, rhs : system.Int64) : system.Int64 return Std.int(lhs.ToHaxeInt() / rhs.ToHaxeInt()); - @:op(A / B) public static inline function div1(lhs : system.Int64, rhs : Int) : system.Int64 return Std.int(lhs.ToHaxeInt() / rhs); - @:op(A / B) public static inline function div2(lhs : Int, rhs : system.Int64) : system.Int64 return Std.int(lhs / rhs.ToHaxeInt()); - @:op(A / B) public static function div3(lhs : system.Int64, rhs : Float) : system.Double; - @:op(A / B) public static function div4(lhs : Float, rhs : system.Int64) : system.Double; - - @:op(A / B) public static function div5(lhs : system.Int64, rhs : system.Char) : system.Int64; - @:op(A / B) public static function div6(lhs : system.Int64, rhs : system.Byte) : system.Int64; - @:op(A / B) public static function div7(lhs : system.Int64, rhs : system.Int16) : system.Int64; - @:op(A / B) public static function div8(lhs : system.Int64, rhs : system.Int32) : system.Int64; - @:op(A / B) public static function div9(lhs : system.Int64, rhs : system.Int64) : system.Int64; - @:op(A / B) public static function div10(lhs : system.Int64, rhs : system.SByte) : system.Int64; - @:op(A / B) public static function div11(lhs : system.Int64, rhs : system.UInt16) : system.Int64; - @:op(A / B) public static function div12(lhs : system.Int64, rhs : system.UInt32) : system.Int64; - // @:op(A / B) public static function div13(lhs : system.Int64, rhs : system.UInt64) : system.UInt64; - @:op(A / B) public static function div14(lhs : system.Int64, rhs : system.Single) : system.Single; - @:op(A / B) public static function div15(lhs : system.Int64, rhs : system.Double) : system.Double; - - - @:op(A % B) public static function mod0(lhs : system.Int64, rhs : system.Int64) : system.Int64; - @:op(A % B) public static function mod1(lhs : system.Int64, rhs : Int) : system.Int64; - @:op(A % B) public static function mod2(lhs : Int, rhs : system.Int64) : system.Int64; - @:op(A % B) public static function mod3(lhs : system.Int64, rhs : Float) : system.Double; - @:op(A % B) public static function mod4(lhs : Float, rhs : system.Int64) : system.Double; - - @:op(A % B) public static function mod5(lhs : system.Int64, rhs : system.Char) : system.Int64; - @:op(A % B) public static function mod6(lhs : system.Int64, rhs : system.Byte) : system.Int64; - @:op(A % B) public static function mod7(lhs : system.Int64, rhs : system.Int16) : system.Int64; - @:op(A % B) public static function mod8(lhs : system.Int64, rhs : system.Int32) : system.Int64; - @:op(A % B) public static function mod9(lhs : system.Int64, rhs : system.Int64) : system.Int64; - @:op(A % B) public static function mod10(lhs : system.Int64, rhs : system.SByte) : system.Int64; - @:op(A % B) public static function mod11(lhs : system.Int64, rhs : system.UInt16) : system.Int64; - @:op(A % B) public static function mod12(lhs : system.Int64, rhs : system.UInt32) : system.Int64; - // @:op(A % B) public static function mod13(lhs : system.Int64, rhs : system.UInt64) : system.Int64; - @:op(A % B) public static function mod14(lhs : system.Int64, rhs : system.Single) : system.Single; - @:op(A % B) public static function mod15(lhs : system.Int64, rhs : system.Double) : system.Double; - - - @:op(A + B) public static function add0(lhs : system.Int64, rhs : system.Int64) : system.Int64; - @:op(A + B) public static function add1(lhs : system.Int64, rhs : Int) : system.Int64; - @:op(A + B) public static function add2(lhs : Int, rhs : system.Int64) : system.Int64; - @:op(A + B) public static function add3(lhs : system.Int64, rhs : Float) : system.Double; - @:op(A + B) public static function add4(lhs : Float, rhs : system.Int64) : system.Double; - - @:op(A + B) public static function add5(lhs : system.Int64, rhs : system.Char) : system.Int64; - @:op(A + B) public static function add6(lhs : system.Int64, rhs : system.Byte) : system.Int64; - @:op(A + B) public static function add7(lhs : system.Int64, rhs : system.Int16) : system.Int64; - @:op(A + B) public static function add8(lhs : system.Int64, rhs : system.Int32) : system.Int64; - @:op(A + B) public static function add9(lhs : system.Int64, rhs : system.Int64) : system.Int64; - @:op(A + B) public static function add10(lhs : system.Int64, rhs : system.SByte) : system.Int64; - @:op(A + B) public static function add11(lhs : system.Int64, rhs : system.UInt16) : system.Int64; - @:op(A + B) public static function add12(lhs : system.Int64, rhs : system.UInt32) : system.Int64; - // @:op(A + B) public static function add13(lhs : system.Int64, rhs : system.UInt64) : system.Int64; - @:op(A + B) public static function add14(lhs : system.Int64, rhs : system.Single) : system.Single; - @:op(A + B) public static function add15(lhs : system.Int64, rhs : system.Double) : system.Double; - @:op(A + B) public static inline function add16(lhs : system.Int64, rhs : system.CsString) : system.CsString return lhs.ToString() + rhs; - @:op(A + B) public static inline function add17(lhs : system.Int64, rhs : String) : system.CsString return lhs.ToString() + rhs; - - - @:op(A - B) public static function sub0(lhs : system.Int64, rhs : system.Int64) : system.Int64; - @:op(A - B) public static function sub1(lhs : system.Int64, rhs : Int) : system.Int64; - @:op(A - B) public static function sub2(lhs : Int, rhs : system.Int64) : system.Int64; - @:op(A - B) public static function sub3(lhs : system.Int64, rhs : Float) : system.Double; - @:op(A - B) public static function sub4(lhs : Float, rhs : system.Int64) : system.Double; - - @:op(A - B) public static function sub5(lhs : system.Int64, rhs : system.Char) : system.Int64; - @:op(A - B) public static function sub6(lhs : system.Int64, rhs : system.Byte) : system.Int64; - @:op(A - B) public static function sub7(lhs : system.Int64, rhs : system.Int16) : system.Int64; - @:op(A - B) public static function sub8(lhs : system.Int64, rhs : system.Int32) : system.Int64; - @:op(A - B) public static function sub9(lhs : system.Int64, rhs : system.Int64) : system.Int64; - @:op(A - B) public static function sub10(lhs : system.Int64, rhs : system.SByte) : system.Int64; - @:op(A - B) public static function sub11(lhs : system.Int64, rhs : system.UInt16) : system.Int64; - @:op(A - B) public static function sub12(lhs : system.Int64, rhs : system.UInt32) : system.Int64; - // @:op-A - B) public static function sub13(lhs : system.Int64, rhs : system.UInt64) : system.Int64; - @:op(A - B) public static function sub14(lhs : system.Int64, rhs : system.Single) : system.Single; - @:op(A - B) public static function sub15(lhs : system.Int64, rhs : system.Double) : system.Double; - - - @:op(A << B) public static function shl0(lhs :system.Int64, rhs :system.Int64) :system.Int64; - @:op(A << B) public static function shl1(lhs :system.Int64, rhs : Int) :system.Int64; - @:op(A << B) public static function shl2(lhs : Int, rhs :system.Int64) :system.Int64; - - @:op(A << B) public static function shl5(lhs : system.Int64, rhs : system.Char) : system.Int64; - @:op(A << B) public static function shl6(lhs : system.Int64, rhs : system.Byte) : system.Int64; - @:op(A << B) public static function shl7(lhs : system.Int64, rhs : system.Int16) : system.Int64; - @:op(A << B) public static function shl8(lhs : system.Int64, rhs : system.Int32) : system.Int64; - // @:op(A << B) public static function shl9(lhs : system.Int64, rhs : system.Int64) : system.Int64; - @:op(A << B) public static function shl10(lhs : system.Int64, rhs : system.SByte) : system.Int64; - @:op(A << B) public static function shl11(lhs : system.Int64, rhs : system.UInt16) : system.Int64; - // @:op(A << B) public static function shl12(lhs : system.Int64, rhs : system.UInt32) : system.Int64; - // @:op-A << B) public static function shl13(lhs : system.Int64, rhs : system.UInt64) : system.Int64; - - - @:op(A >> B) public static function shr0(lhs :system.Int64, rhs :system.Int64) :system.Int64; - @:op(A >> B) public static function shr1(lhs :system.Int64, rhs : Int) :system.Int64; - @:op(A >> B) public static function shr2(lhs : Int, rhs :system.Int64) :system.Int64; - - @:op(A >> B) public static function shr5(lhs : system.Int64, rhs : system.Char) : system.Int64; - @:op(A >> B) public static function shr6(lhs : system.Int64, rhs : system.Byte) : system.Int64; - @:op(A >> B) public static function shr7(lhs : system.Int64, rhs : system.Int16) : system.Int64; - @:op(A >> B) public static function shr8(lhs : system.Int64, rhs : system.Int32) : system.Int64; - // @:op(A >> B) public static function shr9(lhs : system.Int64, rhs : system.Int64) : system.Int64; - @:op(A >> B) public static function shr10(lhs : system.Int64, rhs : system.SByte) : system.Int64; - @:op(A >> B) public static function shr11(lhs : system.Int64, rhs : system.UInt16) : system.Int64; - // @:op(A >> B) public static function shr12(lhs : system.Int64, rhs : system.UInt32) : system.Int64; - // @:op-A >> B) public static function shr13(lhs : system.Int64, rhs : system.UInt64) : system.Int64; - - - @:op(A > B) public static function gt0(lhs :system.Int64, rhs : system.Int64) : system.Boolean; - @:op(A > B) public static function gt1(lhs :system.Int64, rhs : Int) : system.Boolean; - @:op(A > B) public static function gt2(lhs : Int, rhs :system.Int64) : system.Boolean; - @:op(A > B) public static function gt3(lhs :system.Int64, rhs : Float) : system.Boolean; - @:op(A > B) public static function gt4(lhs : Float, rhs :system.Int64) : system.Boolean; - - @:op(A > B) public static function gt5(lhs : system.Int64, rhs : system.Char) : system.Boolean; - @:op(A > B) public static function gt6(lhs : system.Int64, rhs : system.Byte) : system.Boolean; - @:op(A > B) public static function gt7(lhs : system.Int64, rhs : system.Int16) : system.Boolean; - @:op(A > B) public static function gt8(lhs : system.Int64, rhs : system.Int32) : system.Boolean; - @:op(A > B) public static function gt9(lhs : system.Int64, rhs : system.Int64) : system.Boolean; - @:op(A > B) public static function gt10(lhs : system.Int64, rhs : system.SByte) : system.Boolean; - @:op(A > B) public static function gt11(lhs : system.Int64, rhs : system.UInt16) : system.Boolean; - @:op(A > B) public static function gt12(lhs : system.Int64, rhs : system.UInt32) : system.Boolean; - @:op(A > B) public static function gt13(lhs : system.Int64, rhs : system.UInt64) : system.Boolean; - @:op(A > B) public static function gt14(lhs : system.Int64, rhs : system.Single) : system.Boolean; - @:op(A > B) public static function gt15(lhs : system.Int64, rhs : system.Double) : system.Boolean; - - - @:op(A < B) public static function lt0(lhs :system.Int64, rhs :system.Int64) : system.Boolean; - @:op(A < B) public static function lt1(lhs :system.Int64, rhs : Int) : system.Boolean; - @:op(A < B) public static function lt2(lhs : Int, rhs :system.Int64) : system.Boolean; - @:op(A < B) public static function lt3(lhs :system.Int64, rhs : Float) : system.Boolean; - @:op(A < B) public static function lt4(lhs : Float, rhs :system.Int64) : system.Boolean; - - @:op(A < B) public static function lt5(lhs : system.Int64, rhs : system.Char) : system.Boolean; - @:op(A < B) public static function lt6(lhs : system.Int64, rhs : system.Byte) : system.Boolean; - @:op(A < B) public static function lt7(lhs : system.Int64, rhs : system.Int16) : system.Boolean; - @:op(A < B) public static function lt8(lhs : system.Int64, rhs : system.Int32) : system.Boolean; - @:op(A < B) public static function lt9(lhs : system.Int64, rhs : system.Int64) : system.Boolean; - @:op(A < B) public static function lt10(lhs : system.Int64, rhs : system.SByte) : system.Boolean; - @:op(A < B) public static function lt11(lhs : system.Int64, rhs : system.UInt16) : system.Boolean; - @:op(A < B) public static function lt12(lhs : system.Int64, rhs : system.UInt32) : system.Boolean; - @:op(A < B) public static function lt13(lhs : system.Int64, rhs : system.UInt64) : system.Boolean; - @:op(A < B) public static function lt14(lhs : system.Int64, rhs : system.Single) : system.Boolean; - @:op(A < B) public static function lt15(lhs : system.Int64, rhs : system.Double) : system.Boolean; - - - @:op(A >= B) public static function gte0(lhs :system.Int64, rhs :system.Int64) : system.Boolean; - @:op(A >= B) public static function gte1(lhs :system.Int64, rhs : Int) : system.Boolean; - @:op(A >= B) public static function gte2(lhs : Int, rhs :system.Int64) : system.Boolean; - @:op(A >= B) public static function gte3(lhs :system.Int64, rhs : Float) : system.Boolean; - @:op(A >= B) public static function gte4(lhs : Float, rhs :system.Int64) : system.Boolean; - - @:op(A >= B) public static function gte5(lhs : system.Int64, rhs : system.Char) : system.Boolean; - @:op(A >= B) public static function gte6(lhs : system.Int64, rhs : system.Byte) : system.Boolean; - @:op(A >= B) public static function gte7(lhs : system.Int64, rhs : system.Int16) : system.Boolean; - @:op(A >= B) public static function gte8(lhs : system.Int64, rhs : system.Int32) : system.Boolean; - @:op(A >= B) public static function gte9(lhs : system.Int64, rhs : system.Int64) : system.Boolean; - @:op(A >= B) public static function gte10(lhs : system.Int64, rhs : system.SByte) : system.Boolean; - @:op(A >= B) public static function gte11(lhs : system.Int64, rhs : system.UInt16) : system.Boolean; - @:op(A >= B) public static function gte12(lhs : system.Int64, rhs : system.UInt32) : system.Boolean; - @:op(A >= B) public static function gte13(lhs : system.Int64, rhs : system.UInt64) : system.Boolean; - @:op(A >= B) public static function gte14(lhs : system.Int64, rhs : system.Single) : system.Boolean; - @:op(A >= B) public static function gte15(lhs : system.Int64, rhs : system.Double) : system.Boolean; - - - @:op(A <= B) public static function lte0(lhs :system.Int64, rhs :system.Int64) : system.Boolean; - @:op(A <= B) public static function lte1(lhs :system.Int64, rhs : Int) : system.Boolean; - @:op(A <= B) public static function lte2(lhs : Int, rhs :system.Int64) : system.Boolean; - @:op(A <= B) public static function lte3(lhs :system.Int64, rhs : Float) : system.Boolean; - @:op(A <= B) public static function lte4(lhs : Float, rhs :system.Int64) : system.Boolean; - - @:op(A <= B) public static function lte5(lhs : system.Int64, rhs : system.Char) : system.Boolean; - @:op(A <= B) public static function lte6(lhs : system.Int64, rhs : system.Byte) : system.Boolean; - @:op(A <= B) public static function lte7(lhs : system.Int64, rhs : system.Int16) : system.Boolean; - @:op(A <= B) public static function lte8(lhs : system.Int64, rhs : system.Int32) : system.Boolean; - @:op(A <= B) public static function lte9(lhs : system.Int64, rhs : system.Int64) : system.Boolean; - @:op(A <= B) public static function lte10(lhs : system.Int64, rhs : system.SByte) : system.Boolean; - @:op(A <= B) public static function lte11(lhs : system.Int64, rhs : system.UInt16) : system.Boolean; - @:op(A <= B) public static function lte12(lhs : system.Int64, rhs : system.UInt32) : system.Boolean; - @:op(A <= B) public static function lte13(lhs : system.Int64, rhs : system.UInt64) : system.Boolean; - @:op(A <= B) public static function lte14(lhs : system.Int64, rhs : system.Single) : system.Boolean; - @:op(A <= B) public static function lte15(lhs : system.Int64, rhs : system.Double) : system.Boolean; - - - @:op(A == B) public static function eq0(lhs :system.Int64, rhs :system.Int64) : system.Boolean; - @:op(A == B) public static function eq1(lhs :system.Int64, rhs : Int) : system.Boolean; - @:op(A == B) public static function eq2(lhs : Int, rhs :system.Int64) : system.Boolean; - @:op(A == B) public static function eq3(lhs :system.Int64, rhs : Float) : system.Boolean; - @:op(A == B) public static function eq4(lhs : Float, rhs :system.Int64) : system.Boolean; - - @:op(A == B) public static function eq5(lhs : system.Int64, rhs : system.Char) : system.Boolean; - @:op(A == B) public static function eq6(lhs : system.Int64, rhs : system.Byte) : system.Boolean; - @:op(A == B) public static function eq7(lhs : system.Int64, rhs : system.Int16) : system.Boolean; - @:op(A == B) public static function eq8(lhs : system.Int64, rhs : system.Int32) : system.Boolean; - @:op(A == B) public static function eq9(lhs : system.Int64, rhs : system.Int64) : system.Boolean; - @:op(A == B) public static function eq10(lhs : system.Int64, rhs : system.SByte) : system.Boolean; - @:op(A == B) public static function eq11(lhs : system.Int64, rhs : system.UInt16) : system.Boolean; - @:op(A == B) public static function eq12(lhs : system.Int64, rhs : system.UInt32) : system.Boolean; - @:op(A == B) public static function eq13(lhs : system.Int64, rhs : system.UInt64) : system.Boolean; - @:op(A == B) public static function eq14(lhs : system.Int64, rhs : system.Single) : system.Boolean; - @:op(A == B) public static function eq15(lhs : system.Int64, rhs : system.Double) : system.Boolean; - - - @:op(A != B) public static function neq0(lhs :system.Int64, rhs :system.Int64) : system.Boolean; - @:op(A != B) public static function neq1(lhs :system.Int64, rhs : Int) : system.Boolean; - @:op(A != B) public static function neq2(lhs : Int, rhs :system.Int64) : system.Boolean; - @:op(A != B) public static function neq3(lhs :system.Int64, rhs : Float) : system.Boolean; - @:op(A != B) public static function neq4(lhs : Float, rhs :system.Int64) : system.Boolean; - - @:op(A != B) public static function neq5(lhs : system.Int64, rhs : system.Char) : system.Boolean; - @:op(A != B) public static function neq6(lhs : system.Int64, rhs : system.Byte) : system.Boolean; - @:op(A != B) public static function neq7(lhs : system.Int64, rhs : system.Int16) : system.Boolean; - @:op(A != B) public static function neq8(lhs : system.Int64, rhs : system.Int32) : system.Boolean; - @:op(A != B) public static function neq9(lhs : system.Int64, rhs : system.Int64) : system.Boolean; - @:op(A != B) public static function neq10(lhs : system.Int64, rhs : system.SByte) : system.Boolean; - @:op(A != B) public static function neq11(lhs : system.Int64, rhs : system.UInt16) : system.Boolean; - @:op(A != B) public static function neq12(lhs : system.Int64, rhs : system.UInt32) : system.Boolean; - @:op(A != B) public static function neq13(lhs : system.Int64, rhs : system.UInt64) : system.Boolean; - @:op(A != B) public static function neq14(lhs : system.Int64, rhs : system.Single) : system.Boolean; - @:op(A != B) public static function neq15(lhs : system.Int64, rhs : system.Double) : system.Boolean; - - - @:op(A & B) public static function and0(lhs :system.Int64, rhs :system.Int64) :system.Int64; - @:op(A & B) public static function and1(lhs :system.Int64, rhs : Int) :system.Int64; - @:op(A & B) public static function and2(lhs : Int, rhs :system.Int64) :system.Int64; - - @:op(A & B) public static function and5(lhs : system.Int64, rhs : system.Char) : system.Int64; - @:op(A & B) public static function and6(lhs : system.Int64, rhs : system.Byte) : system.Int64; - @:op(A & B) public static function and7(lhs : system.Int64, rhs : system.Int16) : system.Int64; - @:op(A & B) public static function and8(lhs : system.Int64, rhs : system.Int32) : system.Int64; - @:op(A & B) public static function and9(lhs : system.Int64, rhs : system.Int64) : system.Int64; - @:op(A & B) public static function and10(lhs : system.Int64, rhs : system.SByte) : system.Int64; - @:op(A & B) public static function and11(lhs : system.Int64, rhs : system.UInt16) : system.Int64; - @:op(A & B) public static function and12(lhs : system.Int64, rhs : system.UInt32) : system.Int64; - //@:op(A & B) public static function and13(lhs : system.Int64, rhs : system.UInt64) : system.Int64; - - - @:op(A | B) public static function or0(lhs :system.Int64, rhs :system.Int64) :system.Int64; - @:op(A | B) public static function or1(lhs :system.Int64, rhs : Int) :system.Int64; - @:op(A | B) public static function or2(lhs : Int, rhs :system.Int64) :system.Int64; - - @:op(A | B) public static function or5(lhs : system.Int64, rhs : system.Char) : system.Int64; - @:op(A | B) public static function or6(lhs : system.Int64, rhs : system.Byte) : system.Int64; - @:op(A | B) public static function or7(lhs : system.Int64, rhs : system.Int16) : system.Int64; - @:op(A | B) public static function or8(lhs : system.Int64, rhs : system.Int32) : system.Int64; - @:op(A | B) public static function or9(lhs : system.Int64, rhs : system.Int64) : system.Int64; - @:op(A | B) public static function or10(lhs : system.Int64, rhs : system.SByte) : system.Int64; - @:op(A | B) public static function or11(lhs : system.Int64, rhs : system.UInt16) : system.Int64; - @:op(A | B) public static function or12(lhs : system.Int64, rhs : system.UInt32) : system.Int64; - //@:op(A | B) public static function or13(lhs : system.Int64, rhs : system.UInt64) : system.UInt64; - - @:op(A ^ B) public static function xor0(lhs :system.Int64, rhs : system.Int64) :system.Int64; - @:op(A ^ B) public static function xor1(lhs :system.Int64, rhs : Int) :system.Int64; - @:op(A ^ B) public static function xor2(lhs : Int, rhs :system.Int64) :system.Int64; - - @:op(A ^ B) public static function xor5(lhs : system.Int64, rhs : system.Char) : system.Int64; - @:op(A ^ B) public static function xor6(lhs : system.Int64, rhs : system.Byte) : system.Int64; - @:op(A ^ B) public static function xor7(lhs : system.Int64, rhs : system.Int16) : system.Int64; - @:op(A ^ B) public static function xor8(lhs : system.Int64, rhs : system.Int32) : system.Int64; - @:op(A ^ B) public static function xor9(lhs : system.Int64, rhs : system.Int64) : system.Int64; - @:op(A ^ B) public static function xor10(lhs : system.Int64, rhs : system.SByte) : system.Int64; - @:op(A ^ B) public static function xor11(lhs : system.Int64, rhs : system.UInt16) : system.Int64; - @:op(A ^ B) public static function xor12(lhs : system.Int64, rhs : system.UInt32) : system.Int64; - //@:op(A ^ B) public static function xor13(lhs : system.Int64, rhs : system.UInt64) : system.UInt64; -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/Int64Array.hx b/Phase/Mscorlib/system/Int64Array.hx deleted file mode 100644 index 1afeea79b..000000000 --- a/Phase/Mscorlib/system/Int64Array.hx +++ /dev/null @@ -1,21 +0,0 @@ -package system; - -abstract Int64Array(js.html.Int32Array) -{ - public inline function new(length:Int32) this = new js.html.Int32Array(length.ToHaxeInt()); - - @:from public static inline function fromArray(a:Array):Int64Array return cast new js.html.Int32Array(untyped a); - @:from public static inline function fromFixedArray(a:FixedArray):Int64Array return cast new js.html.Int32Array(untyped a.ToHaxeArray()); - - public var Length(get, never):Int32; - public inline function get_Length() : Int32 return this.length; - - @:op([]) public inline function get(index:Int32):Int64 return this[index.ToHaxeInt()]; - @:op([]) public inline function set(index:Int32, val:Int64):Int64 return this[index.ToHaxeInt()] = val.ToHaxeFloat(); - - public inline function iterator() : Iterator return new Int64ArrayIterator(this); - - public inline function ToEnumerable() : system.collections.generic.IEnumerable return new Int64ArrayEnumerable(this); - - public static inline function empty(size:Int32) return new Int64Array(size); -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/Int64ArrayEnumerable.hx b/Phase/Mscorlib/system/Int64ArrayEnumerable.hx deleted file mode 100644 index 40da6eb91..000000000 --- a/Phase/Mscorlib/system/Int64ArrayEnumerable.hx +++ /dev/null @@ -1,18 +0,0 @@ -package system; - -import system.collections.generic.IEnumerable; -import system.collections.generic.IEnumerator; - -class Int64ArrayEnumerable implements IEnumerable -{ - private var _array:js.html.Int32Array; - public function new(array:js.html.Int32Array) - { - _array= array; - } - - public function GetEnumerator() : IEnumerator - { - return new Int64ArrayEnumerator(_array); - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/Int64ArrayEnumerator.hx b/Phase/Mscorlib/system/Int64ArrayEnumerator.hx deleted file mode 100644 index 14f6757ac..000000000 --- a/Phase/Mscorlib/system/Int64ArrayEnumerator.hx +++ /dev/null @@ -1,29 +0,0 @@ -package system; - -import system.collections.generic.IEnumerator; - -class Int64ArrayEnumerator implements IEnumerator -{ - private var _array:js.html.Int32Array; - private var _i:Int; - - public function new(array:js.html.Int32Array) - { - _array = array; - _i = -1; - } - - public var Current(get, never):Int64; - public function get_Current() : Int64 return _array[_i]; - public function MoveNext() : Bool - { - if(_i >= _array.length - 1) return false; - _i++; - return true; - - } - public function Reset():Void - { - _i = -1; - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/Int64ArrayIterator.hx b/Phase/Mscorlib/system/Int64ArrayIterator.hx deleted file mode 100644 index 36d984acb..000000000 --- a/Phase/Mscorlib/system/Int64ArrayIterator.hx +++ /dev/null @@ -1,19 +0,0 @@ -package system; - -class Int64ArrayIterator -{ - private var _array:js.html.Int32Array; - private var _i:Int; - - public function new(array:js.html.Int32Array) - { - _array = array; - _i = 0; - } - - public function hasNext() return _i < _array.length; - public function next() : Int64 - { - return _array[_i++]; - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/InvalidCastException.hx b/Phase/Mscorlib/system/InvalidCastException.hx deleted file mode 100644 index 9279c42ef..000000000 --- a/Phase/Mscorlib/system/InvalidCastException.hx +++ /dev/null @@ -1,10 +0,0 @@ -package system; - -class InvalidCastException extends Exception -{ - public function new(message:String = "") - { - super(); - Exception_CsString(message); - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/Nullable.hx b/Phase/Mscorlib/system/Nullable.hx deleted file mode 100644 index 84ba532e2..000000000 --- a/Phase/Mscorlib/system/Nullable.hx +++ /dev/null @@ -1,9 +0,0 @@ -package system; - -abstract Nullable(Null) from T -{ - public inline function new(t:T) this = t; - - public var Value(get, never):T; - public inline function get_Value() : T return this; -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/Object.hx b/Phase/Mscorlib/system/Object.hx deleted file mode 100644 index bc07599b8..000000000 --- a/Phase/Mscorlib/system/Object.hx +++ /dev/null @@ -1,3 +0,0 @@ -package system; - -typedef Object = Any; \ No newline at end of file diff --git a/Phase/Mscorlib/system/ObjectExtensions.hx b/Phase/Mscorlib/system/ObjectExtensions.hx deleted file mode 100644 index 1f0d1587e..000000000 --- a/Phase/Mscorlib/system/ObjectExtensions.hx +++ /dev/null @@ -1,26 +0,0 @@ -package system; - -class ObjectExtensions -{ - public static inline function ToString(v:system.Object) : CsString return Std.string(v); - public static inline function ReferenceEquals(a:system.Object, b:system.Object) : system.Boolean return a == b; - public static inline function GetType(a:T) : system.CsType return new system.CsType(Type.getClass(a)); - - public static function Equals_Object(left:T1, right:T2) : Bool - { - var equals = Reflect.field(left, "Equals"); - if(equals == null) - { - equals = Reflect.field(left, "Equals_Object"); - } - - if(equals != null) - { - return Reflect.callMethod(left, cast equals, [right]); - } - else - { - return untyped left == untyped right; - } - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/OverflowException.hx b/Phase/Mscorlib/system/OverflowException.hx deleted file mode 100644 index d887640b2..000000000 --- a/Phase/Mscorlib/system/OverflowException.hx +++ /dev/null @@ -1,10 +0,0 @@ -package system; - -class OverflowException extends Exception -{ - public function new(message:String = "") - { - super(); - Exception_CsString(message); - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/SByte.hx b/Phase/Mscorlib/system/SByte.hx deleted file mode 100644 index 98607b0cb..000000000 --- a/Phase/Mscorlib/system/SByte.hx +++ /dev/null @@ -1,319 +0,0 @@ -package system; - - -abstract SByte(Int) from Int -{ - public inline function new(i:Int) this = system.Convert.ToInt8(i); - - public inline function ToHaxeInt(): Int return this; - public inline function ToString() : system.CsString return Std.string(this); - - public inline function ToBoolean_IFormatProvider(provider:IFormatProvider) : system.Boolean return system.Convert.ToBoolean_SByte(this); - public inline function ToChar_IFormatProvider(provider:IFormatProvider) : system.Char return system.Convert.ToChar_SByte(this); - public inline function ToSByte_IFormatProvider(provider:IFormatProvider) : system.SByte return system.Convert.ToSByte_SByte(this); - public inline function ToByte_IFormatProvider(provider:IFormatProvider) : system.Byte return system.Convert.ToByte_SByte(this); - public inline function ToInt16_IFormatProvider(provider:IFormatProvider) : system.Int16 return system.Convert.ToInt16_SByte(this); - public inline function ToUInt16_IFormatProvider(provider:IFormatProvider) : system.UInt16 return system.Convert.ToUInt16_SByte(this); - public inline function ToInt32_IFormatProvider(provider:IFormatProvider) : system.Int32 return system.Convert.ToInt32_SByte(this); - public inline function ToUInt32_IFormatProvider(provider:IFormatProvider) : system.UInt32 return system.Convert.ToUInt32_SByte(this); - public inline function ToInt64_IFormatProvider(provider:IFormatProvider) : system.Int64 return system.Convert.ToInt64_SByte(this); - public inline function ToUInt64_IFormatProvider(provider:IFormatProvider) : system.UInt64 return system.Convert.ToUInt64_SByte(this); - public inline function ToSingle_IFormatProvider(provider:IFormatProvider) : system.Single return system.Convert.ToSingle_SByte(this); - public inline function ToDouble_IFormatProvider(provider:IFormatProvider) : system.Double return system.Convert.ToDouble_SByte(this); - - public inline function GetHashCode() : system.Int32 return this ^ (this << 8); - - @:op(-A) public inline function neg() : Int return -this; - - @:op(~A)public inline function not() : Int return ~this; - - @:op(A++)public inline function postinc() : system.SByte return this++; - @:op(++A)public inline function preinc() : system.SByte return ++this; - - @:op(A--)public inline function postdec() : system.SByte return this--; - @:op(--A)public inline function predec() : system.SByte return --this; - - @:op(A * B) public static function mul0(lhs : system.SByte, rhs : system.SByte) : system.Int32; - @:op(A * B) public static function mul1(lhs : system.SByte, rhs : Int) : system.Int32; - @:op(A * B) public static function mul2(lhs : Int, rhs : system.SByte) : system.Int32; - @:op(A * B) public static function mul3(lhs : system.SByte, rhs : Float) : system.Double; - @:op(A * B) public static function mul4(lhs : Float, rhs : system.SByte) : system.Double; - - @:op(A * B) public static function mul5(lhs : system.SByte, rhs : system.Char) : system.Int32; - @:op(A * B) public static function mul6(lhs : system.SByte, rhs : system.Byte) : system.Int32; - @:op(A * B) public static function mul7(lhs : system.SByte, rhs : system.Int16) : system.Int32; - @:op(A * B) public static function mul8(lhs : system.SByte, rhs : system.Int32) : system.Int32; - @:op(A * B) public static function mul9(lhs : system.SByte, rhs : system.Int64) : system.Int64; - @:op(A * B) public static function mul10(lhs : system.SByte, rhs : system.SByte) : system.Int32; - @:op(A * B) public static function mul11(lhs : system.SByte, rhs : system.UInt16) : system.Int32; - @:op(A * B) public static function mul12(lhs : system.SByte, rhs : system.UInt32) : system.Int64; - // @:op(A * B) public static function mul13(lhs : system.SByte, rhs : system.UInt64) : system.UInt64; - @:op(A * B) public static function mul14(lhs : system.SByte, rhs : system.Single) : system.Single; - @:op(A * B) public static function mul15(lhs : system.SByte, rhs : system.Double) : system.Double; - - - @:op(A / B) public static inline function div0(lhs : system.SByte, rhs : system.SByte) : system.Int32 return Std.int(lhs.ToHaxeInt() / rhs.ToHaxeInt()); - @:op(A / B) public static inline function div1(lhs : system.SByte, rhs : Int) : system.Int32 return Std.int(lhs.ToHaxeInt() / rhs); - @:op(A / B) public static inline function div2(lhs : Int, rhs : system.SByte) : system.Int32 return Std.int(lhs / rhs.ToHaxeInt()); - @:op(A / B) public static function div3(lhs : system.SByte, rhs : Float) : system.Double; - @:op(A / B) public static function div4(lhs : Float, rhs : system.SByte) : system.Double; - - @:op(A / B) public static function div5(lhs : system.SByte, rhs : system.Char) : system.Int32; - @:op(A / B) public static function div6(lhs : system.SByte, rhs : system.Byte) : system.Int32; - @:op(A / B) public static function div7(lhs : system.SByte, rhs : system.Int16) : system.Int32; - @:op(A / B) public static function div8(lhs : system.SByte, rhs : system.Int32) : system.Int32; - @:op(A / B) public static function div9(lhs : system.SByte, rhs : system.Int64) : system.Int64; - @:op(A / B) public static function div10(lhs : system.SByte, rhs : system.SByte) : system.Int32; - @:op(A / B) public static function div11(lhs : system.SByte, rhs : system.UInt16) : system.Int32; - @:op(A / B) public static function div12(lhs : system.SByte, rhs : system.UInt32) : system.Int64; - // @:op(A / B) public static function div13(lhs : system.SByte, rhs : system.UInt64) : system.UInt64; - @:op(A / B) public static function div14(lhs : system.SByte, rhs : system.Single) : system.Single; - @:op(A / B) public static function div15(lhs : system.SByte, rhs : system.Double) : system.Double; - - - @:op(A % B) public static function mod0(lhs : system.SByte, rhs : system.SByte) : system.Int32; - @:op(A % B) public static function mod1(lhs : system.SByte, rhs : Int) : system.Int32; - @:op(A % B) public static function mod2(lhs : Int, rhs : system.SByte) : system.Int32; - @:op(A % B) public static function mod3(lhs : system.SByte, rhs : Float) : system.Double; - @:op(A % B) public static function mod4(lhs : Float, rhs : system.SByte) : system.Double; - - @:op(A % B) public static function mod5(lhs : system.SByte, rhs : system.Char) : system.Int32; - @:op(A % B) public static function mod6(lhs : system.SByte, rhs : system.Byte) : system.Int32; - @:op(A % B) public static function mod7(lhs : system.SByte, rhs : system.Int16) : system.Int32; - @:op(A % B) public static function mod8(lhs : system.SByte, rhs : system.Int32) : system.Int32; - @:op(A % B) public static function mod9(lhs : system.SByte, rhs : system.Int64) : system.Int64; - @:op(A % B) public static function mod10(lhs : system.SByte, rhs : system.SByte) : system.Int32; - @:op(A % B) public static function mod11(lhs : system.SByte, rhs : system.UInt16) : system.Int32; - @:op(A % B) public static function mod12(lhs : system.SByte, rhs : system.UInt32) : system.Int64; - // @:op(A % B) public static function mod13(lhs : system.SByte, rhs : system.UInt64) : system.UInt64; - @:op(A % B) public static function mod14(lhs : system.SByte, rhs : system.Single) : system.Single; - @:op(A % B) public static function mod15(lhs : system.SByte, rhs : system.Double) : system.Double; - - - @:op(A + B) public static function add0(lhs : system.SByte, rhs : system.SByte) : system.Int32; - @:op(A + B) public static function add1(lhs : system.SByte, rhs : Int) : system.Int32; - @:op(A + B) public static function add2(lhs : Int, rhs : system.SByte) : system.Int32; - @:op(A + B) public static function add3(lhs : system.SByte, rhs : Float) : system.Double; - @:op(A + B) public static function add4(lhs : Float, rhs : system.SByte) : system.Double; - - @:op(A + B) public static function add5(lhs : system.SByte, rhs : system.Char) : system.Int32; - @:op(A + B) public static function add6(lhs : system.SByte, rhs : system.Byte) : system.Int32; - @:op(A + B) public static function add7(lhs : system.SByte, rhs : system.Int16) : system.Int32; - @:op(A + B) public static function add8(lhs : system.SByte, rhs : system.Int32) : system.Int32; - @:op(A + B) public static function add9(lhs : system.SByte, rhs : system.Int64) : system.Int64; - @:op(A + B) public static function add10(lhs : system.SByte, rhs : system.SByte) : system.Int32; - @:op(A + B) public static function add11(lhs : system.SByte, rhs : system.UInt16) : system.Int32; - @:op(A + B) public static function add12(lhs : system.SByte, rhs : system.UInt32) : system.Int64; - // @:op(A + B) public static function add13(lhs : system.SByte, rhs : system.UInt64) : system.UInt64; - @:op(A + B) public static function add14(lhs : system.SByte, rhs : system.Single) : system.Single; - @:op(A + B) public static function add15(lhs : system.SByte, rhs : system.Double) : system.Double; - @:op(A + B) public static inline function add16(lhs : system.SByte, rhs : system.CsString) : system.CsString return lhs.ToString() + rhs; - @:op(A + B) public static inline function add17(lhs : system.SByte, rhs : String) : system.CsString return lhs.ToString() + rhs; - - - @:op(A - B) public static function sub0(lhs : system.SByte, rhs : system.SByte) : system.Int32; - @:op(A - B) public static function sub1(lhs : system.SByte, rhs : Int) : system.Int32; - @:op(A - B) public static function sub2(lhs : Int, rhs : system.SByte) : system.Int32; - @:op(A - B) public static function sub3(lhs : system.SByte, rhs : Float) : system.Double; - @:op(A - B) public static function sub4(lhs : Float, rhs : system.SByte) : system.Double; - - @:op(A - B) public static function sub5(lhs : system.SByte, rhs : system.Char) : system.Int32; - @:op(A - B) public static function sub6(lhs : system.SByte, rhs : system.Byte) : system.Int32; - @:op(A - B) public static function sub7(lhs : system.SByte, rhs : system.Int16) : system.Int32; - @:op(A - B) public static function sub8(lhs : system.SByte, rhs : system.Int32) : system.Int32; - @:op(A - B) public static function sub9(lhs : system.SByte, rhs : system.Int64) : system.Int64; - @:op(A - B) public static function sub10(lhs : system.SByte, rhs : system.SByte) : system.Int32; - @:op(A - B) public static function sub11(lhs : system.SByte, rhs : system.UInt16) : system.Int32; - @:op(A - B) public static function sub12(lhs : system.SByte, rhs : system.UInt32) : system.Int64; - // @:op-A - B) public static function sub13(lhs : system.SByte, rhs : system.UInt64) : system.UInt64; - @:op(A - B) public static function sub14(lhs : system.SByte, rhs : system.Single) : system.Single; - @:op(A - B) public static function sub15(lhs : system.SByte, rhs : system.Double) : system.Double; - - - @:op(A << B) public static function shl0(lhs : system.SByte, rhs : system.SByte) : system.Int32; - @:op(A << B) public static function shl1(lhs : system.SByte, rhs : Int) : system.Int32; - @:op(A << B) public static function shl2(lhs : Int, rhs : system.SByte) : system.Int32; - - @:op(A << B) public static function shl5(lhs : system.SByte, rhs : system.Char) : system.Int32; - @:op(A << B) public static function shl6(lhs : system.SByte, rhs : system.Byte) : system.Int32; - @:op(A << B) public static function shl7(lhs : system.SByte, rhs : system.Int16) : system.Int32; - @:op(A << B) public static function shl8(lhs : system.SByte, rhs : system.Int32) : system.Int32; - // @:op(A << B) public static function shl9(lhs : system.SByte, rhs : system.Int64) : system.Int64; - @:op(A << B) public static function shl10(lhs : system.SByte, rhs : system.SByte) : system.Int32; - @:op(A << B) public static function shl11(lhs : system.SByte, rhs : system.UInt16) : system.Int32; - // @:op(A << B) public static function shl12(lhs : system.SByte, rhs : system.UInt32) : system.Int64; - // @:op-A << B) public static function shl13(lhs : system.SByte, rhs : system.UInt64) : system.UInt64; - - @:op(A >> B) public static function shr0(lhs : system.SByte, rhs : system.SByte) : system.Int32; - @:op(A >> B) public static function shr1(lhs : system.SByte, rhs : Int) : system.Int32; - @:op(A >> B) public static function shr2(lhs : Int, rhs : system.SByte) : system.Int32; - - @:op(A >> B) public static function shr5(lhs : system.SByte, rhs : system.Char) : system.Int32; - @:op(A >> B) public static function shr6(lhs : system.SByte, rhs : system.Byte) : system.Int32; - @:op(A >> B) public static function shr7(lhs : system.SByte, rhs : system.Int16) : system.Int32; - @:op(A >> B) public static function shr8(lhs : system.SByte, rhs : system.Int32) : system.Int32; - // @:op(A >> B) public static function shr9(lhs : system.SByte, rhs : system.Int64) : system.Int64; - @:op(A >> B) public static function shr10(lhs : system.SByte, rhs : system.SByte) : system.Int32; - @:op(A >> B) public static function shr11(lhs : system.SByte, rhs : system.UInt16) : system.Int32; - // @:op(A >> B) public static function shr12(lhs : system.SByte, rhs : system.UInt32) : system.Int64; - // @:op-A >> B) public static function shr13(lhs : system.SByte, rhs : system.UInt64) : system.UInt64; - - - @:op(A > B) public static function gt0(lhs : system.SByte, rhs : system.SByte) : system.Boolean; - @:op(A > B) public static function gt1(lhs : system.SByte, rhs : Int) : system.Boolean; - @:op(A > B) public static function gt2(lhs : Int, rhs : system.SByte) : system.Boolean; - @:op(A > B) public static function gt3(lhs : system.SByte, rhs : Float) : system.Boolean; - @:op(A > B) public static function gt4(lhs : Float, rhs : system.SByte) : system.Boolean; - - @:op(A > B) public static function gt5(lhs : system.SByte, rhs : system.Char) : system.Boolean; - @:op(A > B) public static function gt6(lhs : system.SByte, rhs : system.Byte) : system.Boolean; - @:op(A > B) public static function gt7(lhs : system.SByte, rhs : system.Int16) : system.Boolean; - @:op(A > B) public static function gt8(lhs : system.SByte, rhs : system.Int32) : system.Boolean; - @:op(A > B) public static function gt9(lhs : system.SByte, rhs : system.Int64) : system.Boolean; - @:op(A > B) public static function gt10(lhs : system.SByte, rhs : system.SByte) : system.Boolean; - @:op(A > B) public static function gt11(lhs : system.SByte, rhs : system.UInt16) : system.Boolean; - @:op(A > B) public static function gt12(lhs : system.SByte, rhs : system.UInt32) : system.Boolean; - @:op(A > B) public static function gt13(lhs : system.SByte, rhs : system.UInt64) : system.Boolean; - @:op(A > B) public static function gt14(lhs : system.SByte, rhs : system.Single) : system.Boolean; - @:op(A > B) public static function gt15(lhs : system.SByte, rhs : system.Double) : system.Boolean; - - - @:op(A < B) public static function lt0(lhs : system.SByte, rhs : system.SByte) : system.Boolean; - @:op(A < B) public static function lt1(lhs : system.SByte, rhs : Int) : system.Boolean; - @:op(A < B) public static function lt2(lhs : Int, rhs : system.SByte) : system.Boolean; - @:op(A < B) public static function lt3(lhs : system.SByte, rhs : Float) : system.Boolean; - @:op(A < B) public static function lt4(lhs : Float, rhs : system.SByte) : system.Boolean; - - @:op(A < B) public static function lt5(lhs : system.SByte, rhs : system.Char) : system.Boolean; - @:op(A < B) public static function lt6(lhs : system.SByte, rhs : system.Byte) : system.Boolean; - @:op(A < B) public static function lt7(lhs : system.SByte, rhs : system.Int16) : system.Boolean; - @:op(A < B) public static function lt8(lhs : system.SByte, rhs : system.Int32) : system.Boolean; - @:op(A < B) public static function lt9(lhs : system.SByte, rhs : system.Int64) : system.Boolean; - @:op(A < B) public static function lt10(lhs : system.SByte, rhs : system.SByte) : system.Boolean; - @:op(A < B) public static function lt11(lhs : system.SByte, rhs : system.UInt16) : system.Boolean; - @:op(A < B) public static function lt12(lhs : system.SByte, rhs : system.UInt32) : system.Boolean; - @:op(A < B) public static function lt13(lhs : system.SByte, rhs : system.UInt64) : system.Boolean; - @:op(A < B) public static function lt14(lhs : system.SByte, rhs : system.Single) : system.Boolean; - @:op(A < B) public static function lt15(lhs : system.SByte, rhs : system.Double) : system.Boolean; - - - @:op(A >= B) public static function gte0(lhs : system.SByte, rhs : system.SByte) : system.Boolean; - @:op(A >= B) public static function gte1(lhs : system.SByte, rhs : Int) : system.Boolean; - @:op(A >= B) public static function gte2(lhs : Int, rhs : system.SByte) : system.Boolean; - @:op(A >= B) public static function gte3(lhs : system.SByte, rhs : Float) : system.Boolean; - @:op(A >= B) public static function gte4(lhs : Float, rhs : system.SByte) : system.Boolean; - - @:op(A >= B) public static function gte5(lhs : system.SByte, rhs : system.Char) : system.Boolean; - @:op(A >= B) public static function gte6(lhs : system.SByte, rhs : system.Byte) : system.Boolean; - @:op(A >= B) public static function gte7(lhs : system.SByte, rhs : system.Int16) : system.Boolean; - @:op(A >= B) public static function gte8(lhs : system.SByte, rhs : system.Int32) : system.Boolean; - @:op(A >= B) public static function gte9(lhs : system.SByte, rhs : system.Int64) : system.Boolean; - @:op(A >= B) public static function gte10(lhs : system.SByte, rhs : system.SByte) : system.Boolean; - @:op(A >= B) public static function gte11(lhs : system.SByte, rhs : system.UInt16) : system.Boolean; - @:op(A >= B) public static function gte12(lhs : system.SByte, rhs : system.UInt32) : system.Boolean; - @:op(A >= B) public static function gte13(lhs : system.SByte, rhs : system.UInt64) : system.Boolean; - @:op(A >= B) public static function gte14(lhs : system.SByte, rhs : system.Single) : system.Boolean; - @:op(A >= B) public static function gte15(lhs : system.SByte, rhs : system.Double) : system.Boolean; - - - @:op(A <= B) public static function lte0(lhs : system.SByte, rhs : system.SByte) : system.Boolean; - @:op(A <= B) public static function lte1(lhs : system.SByte, rhs : Int) : system.Boolean; - @:op(A <= B) public static function lte2(lhs : Int, rhs : system.SByte) : system.Boolean; - @:op(A <= B) public static function lte3(lhs : system.SByte, rhs : Float) : system.Boolean; - @:op(A <= B) public static function lte4(lhs : Float, rhs : system.SByte) : system.Boolean; - - @:op(A <= B) public static function lte5(lhs : system.SByte, rhs : system.Char) : system.Boolean; - @:op(A <= B) public static function lte6(lhs : system.SByte, rhs : system.Byte) : system.Boolean; - @:op(A <= B) public static function lte7(lhs : system.SByte, rhs : system.Int16) : system.Boolean; - @:op(A <= B) public static function lte8(lhs : system.SByte, rhs : system.Int32) : system.Boolean; - @:op(A <= B) public static function lte9(lhs : system.SByte, rhs : system.Int64) : system.Boolean; - @:op(A <= B) public static function lte10(lhs : system.SByte, rhs : system.SByte) : system.Boolean; - @:op(A <= B) public static function lte11(lhs : system.SByte, rhs : system.UInt16) : system.Boolean; - @:op(A <= B) public static function lte12(lhs : system.SByte, rhs : system.UInt32) : system.Boolean; - @:op(A <= B) public static function lte13(lhs : system.SByte, rhs : system.UInt64) : system.Boolean; - @:op(A <= B) public static function lte14(lhs : system.SByte, rhs : system.Single) : system.Boolean; - @:op(A <= B) public static function lte15(lhs : system.SByte, rhs : system.Double) : system.Boolean; - - - @:op(A == B) public static function eq0(lhs : system.SByte, rhs : system.SByte) : system.Boolean; - @:op(A == B) public static function eq1(lhs : system.SByte, rhs : Int) : system.Boolean; - @:op(A == B) public static function eq2(lhs : Int, rhs : system.SByte) : system.Boolean; - @:op(A == B) public static function eq3(lhs : system.SByte, rhs : Float) : system.Boolean; - @:op(A == B) public static function eq4(lhs : Float, rhs : system.SByte) : system.Boolean; - - @:op(A == B) public static function eq5(lhs : system.SByte, rhs : system.Char) : system.Boolean; - @:op(A == B) public static function eq6(lhs : system.SByte, rhs : system.Byte) : system.Boolean; - @:op(A == B) public static function eq7(lhs : system.SByte, rhs : system.Int16) : system.Boolean; - @:op(A == B) public static function eq8(lhs : system.SByte, rhs : system.Int32) : system.Boolean; - @:op(A == B) public static function eq9(lhs : system.SByte, rhs : system.Int64) : system.Boolean; - @:op(A == B) public static function eq10(lhs : system.SByte, rhs : system.SByte) : system.Boolean; - @:op(A == B) public static function eq11(lhs : system.SByte, rhs : system.UInt16) : system.Boolean; - @:op(A == B) public static function eq12(lhs : system.SByte, rhs : system.UInt32) : system.Boolean; - @:op(A == B) public static function eq13(lhs : system.SByte, rhs : system.UInt64) : system.Boolean; - @:op(A == B) public static function eq14(lhs : system.SByte, rhs : system.Single) : system.Boolean; - @:op(A == B) public static function eq15(lhs : system.SByte, rhs : system.Double) : system.Boolean; - - - @:op(A != B) public static function neq0(lhs : system.SByte, rhs : system.SByte) : system.Boolean; - @:op(A != B) public static function neq1(lhs : system.SByte, rhs : Int) : system.Boolean; - @:op(A != B) public static function neq2(lhs : Int, rhs : system.SByte) : system.Boolean; - @:op(A != B) public static function neq3(lhs : system.SByte, rhs : Float) : system.Boolean; - @:op(A != B) public static function neq4(lhs : Float, rhs : system.SByte) : system.Boolean; - - @:op(A != B) public static function neq5(lhs : system.SByte, rhs : system.Char) : system.Boolean; - @:op(A != B) public static function neq6(lhs : system.SByte, rhs : system.Byte) : system.Boolean; - @:op(A != B) public static function neq7(lhs : system.SByte, rhs : system.Int16) : system.Boolean; - @:op(A != B) public static function neq8(lhs : system.SByte, rhs : system.Int32) : system.Boolean; - @:op(A != B) public static function neq9(lhs : system.SByte, rhs : system.Int64) : system.Boolean; - @:op(A != B) public static function neq10(lhs : system.SByte, rhs : system.SByte) : system.Boolean; - @:op(A != B) public static function neq11(lhs : system.SByte, rhs : system.UInt16) : system.Boolean; - @:op(A != B) public static function neq12(lhs : system.SByte, rhs : system.UInt32) : system.Boolean; - @:op(A != B) public static function neq13(lhs : system.SByte, rhs : system.UInt64) : system.Boolean; - @:op(A != B) public static function neq14(lhs : system.SByte, rhs : system.Single) : system.Boolean; - @:op(A != B) public static function neq15(lhs : system.SByte, rhs : system.Double) : system.Boolean; - - - @:op(A & B) public static function and0(lhs : system.SByte, rhs : system.SByte) : system.Int32; - @:op(A & B) public static function and1(lhs : system.SByte, rhs : Int) : system.Int32; - @:op(A & B) public static function and2(lhs : Int, rhs : system.SByte) : system.Int32; - - @:op(A & B) public static function and5(lhs : system.SByte, rhs : system.Char) : system.Int32; - @:op(A & B) public static function and6(lhs : system.SByte, rhs : system.Byte) : system.Int32; - @:op(A & B) public static function and7(lhs : system.SByte, rhs : system.Int16) : system.Int32; - @:op(A & B) public static function and8(lhs : system.SByte, rhs : system.Int32) : system.Int32; - @:op(A & B) public static function and9(lhs : system.SByte, rhs : system.Int64) : system.Int64; - @:op(A & B) public static function and10(lhs : system.SByte, rhs : system.SByte) : system.Int32; - @:op(A & B) public static function and11(lhs : system.SByte, rhs : system.UInt16) : system.Int32; - @:op(A & B) public static function and12(lhs : system.SByte, rhs : system.UInt32) : system.Int64; - //@:op(A & B) public static function and13(lhs : system.SByte, rhs : system.UInt64) : system.UInt64; - - - @:op(A | B) public static function or0(lhs : system.SByte, rhs : system.SByte) : system.Int32; - @:op(A | B) public static function or1(lhs : system.SByte, rhs : Int) : system.Int32; - @:op(A | B) public static function or2(lhs : Int, rhs : system.SByte) : system.Int32; - - @:op(A | B) public static function or5(lhs : system.SByte, rhs : system.Char) : system.Int32; - @:op(A | B) public static function or6(lhs : system.SByte, rhs : system.Byte) : system.Int32; - @:op(A | B) public static function or7(lhs : system.SByte, rhs : system.Int16) : system.Int32; - @:op(A | B) public static function or8(lhs : system.SByte, rhs : system.Int32) : system.Int32; - @:op(A | B) public static function or9(lhs : system.SByte, rhs : system.Int64) : system.Int64; - @:op(A | B) public static function or10(lhs : system.SByte, rhs : system.SByte) : system.Int32; - @:op(A | B) public static function or11(lhs : system.SByte, rhs : system.UInt16) : system.Int32; - @:op(A | B) public static function or12(lhs : system.SByte, rhs : system.UInt32) : system.Int64; - //@:op(A | B) public static function or13(lhs : system.SByte, rhs : system.UInt64) : system.UInt64; - - - @:op(A ^ B) public static function xor0(lhs : system.SByte, rhs : system.SByte) : system.Int32; - @:op(A ^ B) public static function xor1(lhs : system.SByte, rhs : Int) : system.Int32; - @:op(A ^ B) public static function xor2(lhs : Int, rhs : system.SByte) : system.Int32; - - @:op(A ^ B) public static function xor5(lhs : system.SByte, rhs : system.Char) : system.Int32; - @:op(A ^ B) public static function xor6(lhs : system.SByte, rhs : system.Byte) : system.Int32; - @:op(A ^ B) public static function xor7(lhs : system.SByte, rhs : system.Int16) : system.Int32; - @:op(A ^ B) public static function xor8(lhs : system.SByte, rhs : system.Int32) : system.Int32; - @:op(A ^ B) public static function xor9(lhs : system.SByte, rhs : system.Int64) : system.Int64; - @:op(A ^ B) public static function xor10(lhs : system.SByte, rhs : system.SByte) : system.Int32; - @:op(A ^ B) public static function xor11(lhs : system.SByte, rhs : system.UInt16) : system.Int32; - @:op(A ^ B) public static function xor12(lhs : system.SByte, rhs : system.UInt32) : system.Int64; - //@:op(A ^ B) public static function xor13(lhs : system.SByte, rhs : system.UInt64) : system.UInt64; -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/SByteArray.hx b/Phase/Mscorlib/system/SByteArray.hx deleted file mode 100644 index b82bf6f22..000000000 --- a/Phase/Mscorlib/system/SByteArray.hx +++ /dev/null @@ -1,21 +0,0 @@ -package system; - -abstract SByteArray(js.html.Int8Array) -{ - public inline function new(length:Int32) this = new js.html.Int8Array(length.ToHaxeInt()); - - @:from public static inline function fromArray(a:Array):SByteArray return cast new js.html.Int8Array(untyped a); - @:from public static inline function fromFixedArray(a:FixedArray):SByteArray return cast new js.html.Int8Array(untyped a.ToHaxeArray()); - - public var Length(get, never):Int32; - public inline function get_Length() : Int32 return this.length; - - @:op([]) public inline function get(index:Int32):SByte return this[index.ToHaxeInt()]; - @:op([]) public inline function set(index:Int32, val:SByte):SByte return this[index.ToHaxeInt()] = val.ToHaxeFloat(); - - public inline function iterator() : Iterator return new SByteArrayIterator(this); - - public inline function ToEnumerable() : system.collections.generic.IEnumerable return new SByteArrayEnumerable(this); - - public static inline function empty(size:Int32) return new SByteArray(size); -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/SByteArrayEnumerable.hx b/Phase/Mscorlib/system/SByteArrayEnumerable.hx deleted file mode 100644 index c0c05e62f..000000000 --- a/Phase/Mscorlib/system/SByteArrayEnumerable.hx +++ /dev/null @@ -1,18 +0,0 @@ -package system; - -import system.collections.generic.IEnumerable; -import system.collections.generic.IEnumerator; - -class SByteArrayEnumerable implements IEnumerable -{ - private var _array:js.html.Int8Array; - public function new(array:js.html.Int8Array) - { - _array= array; - } - - public function GetEnumerator() : IEnumerator - { - return new SByteArrayEnumerator(_array); - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/SByteArrayEnumerator.hx b/Phase/Mscorlib/system/SByteArrayEnumerator.hx deleted file mode 100644 index 995245882..000000000 --- a/Phase/Mscorlib/system/SByteArrayEnumerator.hx +++ /dev/null @@ -1,29 +0,0 @@ -package system; - -import system.collections.generic.IEnumerator; - -class SByteArrayEnumerator implements IEnumerator -{ - private var _array:js.html.Int8Array; - private var _i:Int; - - public function new(array:js.html.Int8Array) - { - _array = array; - _i = -1; - } - - public var Current(get, never):SByte; - public function get_Current() : SByte return _array[_i]; - public function MoveNext() : Bool - { - if(_i >= _array.length - 1) return false; - _i++; - return true; - - } - public function Reset():Void - { - _i = -1; - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/SByteArrayIterator.hx b/Phase/Mscorlib/system/SByteArrayIterator.hx deleted file mode 100644 index 6eaa472b1..000000000 --- a/Phase/Mscorlib/system/SByteArrayIterator.hx +++ /dev/null @@ -1,19 +0,0 @@ -package system; - -class SByteArrayIterator -{ - private var _array:js.html.Int8Array; - private var _i:Int; - - public function new(array:js.html.Int8Array) - { - _array = array; - _i = 0; - } - - public function hasNext() return _i < _array.length; - public function next() : SByte - { - return _array[_i++]; - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/Single.hx b/Phase/Mscorlib/system/Single.hx deleted file mode 100644 index 60edf8652..000000000 --- a/Phase/Mscorlib/system/Single.hx +++ /dev/null @@ -1,244 +0,0 @@ -package system; - -abstract Single(Float) from Float from Int -{ - public inline function new(i:Float) this = i; - - @:from public static inline function FromInt(i:Int) : Single return new Single(i); - @:from public static inline function FromByte(i:system.Byte) : Single return new Single(i.ToHaxeInt()); - @:from public static inline function FromInt16(i:system.Int16) : Single return new Single(i.ToHaxeInt()); - @:from public static inline function FromInt32(i:system.Int32) : Single return new Single(i.ToHaxeInt()); - @:from public static inline function FromInt64(i:system.Int64) : Single return new Single(i.ToHaxeInt()); - @:from public static inline function FromSByte(i:system.Byte) : Single return new Single(i.ToHaxeInt()); - @:from public static inline function FromUInt16(i:system.UInt16) : Single return new Single(i.ToHaxeInt()); - @:from public static inline function FromUInt32(i:system.UInt32) : Single return new Single(i.ToHaxeInt()); - @:from public static inline function FromUInt64(i:system.UInt64) : Single return new Single(i.ToHaxeInt()); - - public inline function ToBoolean_IFormatProvider(provider:IFormatProvider) : system.Boolean return system.Convert.ToBoolean_Single(this); - public inline function ToChar_IFormatProvider(provider:IFormatProvider) : system.Char return system.Convert.ToChar_Single(this); - public inline function ToSByte_IFormatProvider(provider:IFormatProvider) : system.SByte return system.Convert.ToSByte_Single(this); - public inline function ToByte_IFormatProvider(provider:IFormatProvider) : system.Byte return system.Convert.ToByte_Single(this); - public inline function ToInt16_IFormatProvider(provider:IFormatProvider) : system.Int16 return system.Convert.ToInt16_Single(this); - public inline function ToUInt16_IFormatProvider(provider:IFormatProvider) : system.UInt16 return system.Convert.ToUInt16_Single(this); - public inline function ToInt32_IFormatProvider(provider:IFormatProvider) : system.Int32 return system.Convert.ToInt32_Single(this); - public inline function ToUInt32_IFormatProvider(provider:IFormatProvider) : system.UInt32 return system.Convert.ToUInt32_Single(this); - public inline function ToInt64_IFormatProvider(provider:IFormatProvider) : system.Int64 return system.Convert.ToInt64_Single(this); - public inline function ToUInt64_IFormatProvider(provider:IFormatProvider) : system.UInt64 return system.Convert.ToUInt64_Single(this); - public inline function ToSingle_IFormatProvider(provider:IFormatProvider) : system.Single return system.Convert.ToSingle_Single(this); - public inline function ToDouble_IFormatProvider(provider:IFormatProvider) : system.Double return system.Convert.ToDouble_Single(this); - - - public inline function ToHaxeFloat(): Float return this; - public inline function ToString() : CsString return Std.string(this); - public function GetHashCode() : system.Int32 return system.Convert.ToHashCode_Single(this); - - public static var NaN(get, never) : system.Single; - public static inline function get_NaN() : system.Single return Math.NaN; - public static inline function IsNaN(v:system.Single) : system.Boolean return Math.isNaN(v.ToHaxeFloat()); - - @:op(-A) public inline function neg() : system.Single return -this; - - @:op(A++) public inline function postinc() : system.Single return this++; - @:op(++A) public inline function preinc() : system.Single return ++this; - - @:op(A--) public inline function postdec() : system.Single return this--; - @:op(--A) public inline function predec() : system.Single return --this; - - @:op(A * B) public static function mul1(lhs : system.Single, rhs : Int) : system.Single; - @:op(A * B) public static function mul2(lhs : Int, rhs : system.Single) : system.Single; - @:op(A * B) public static function mul3(lhs : system.Single, rhs : Float) : system.Single; - @:op(A * B) public static function mul4(lhs : Float, rhs : system.Single) : system.Single; - - @:op(A * B) public static function mul5(lhs : system.Single, rhs : system.Char) : system.Single; - @:op(A * B) public static function mul6(lhs : system.Single, rhs : system.Byte) : system.Single; - @:op(A * B) public static function mul7(lhs : system.Single, rhs : system.Int16) : system.Single; - @:op(A * B) public static function mul8(lhs : system.Single, rhs : system.Int32) : system.Single; - @:op(A * B) public static function mul9(lhs : system.Single, rhs : system.Int64) : system.Single; - @:op(A * B) public static function mul10(lhs : system.Single, rhs : system.SByte) : system.Single; - @:op(A * B) public static function mul11(lhs : system.Single, rhs : system.UInt16) : system.Single; - @:op(A * B) public static function mul12(lhs : system.Single, rhs : system.UInt32) : system.Single; - @:op(A * B) public static function mul13(lhs : system.Single, rhs : system.UInt64) : system.Single; - @:op(A * B) public static function mul14(lhs : system.Single, rhs : system.Single) : system.Single; - @:op(A * B) public static function mul15(lhs : system.Single, rhs : system.Double) : system.Single; - - - @:op(A / B) public static function div1(lhs : system.Single, rhs : Int) : system.Single; - @:op(A / B) public static function div2(lhs : Int, rhs : system.Single) : system.Single; - @:op(A / B) public static function div3(lhs : system.Single, rhs : Float) : system.Single; - @:op(A / B) public static function div4(lhs : Float, rhs : system.Single) : system.Single; - - @:op(A / B) public static function div5(lhs : system.Single, rhs : system.Char) : system.Single; - @:op(A / B) public static function div6(lhs : system.Single, rhs : system.Byte) : system.Single; - @:op(A / B) public static function div7(lhs : system.Single, rhs : system.Int16) : system.Single; - @:op(A / B) public static function div8(lhs : system.Single, rhs : system.Int32) : system.Single; - @:op(A / B) public static function div9(lhs : system.Single, rhs : system.Int64) : system.Single; - @:op(A / B) public static function div10(lhs : system.Single, rhs : system.SByte) : system.Single; - @:op(A / B) public static function div11(lhs : system.Single, rhs : system.UInt16) : system.Single; - @:op(A / B) public static function div12(lhs : system.Single, rhs : system.UInt32) : system.Single; - @:op(A / B) public static function div13(lhs : system.Single, rhs : system.UInt64) : system.Single; - @:op(A / B) public static function div14(lhs : system.Single, rhs : system.Single) : system.Single; - @:op(A / B) public static function div15(lhs : system.Single, rhs : system.Double) : system.Single; - - - @:op(A % B) public static function mod1(lhs : system.Single, rhs : Int) : system.Single; - @:op(A % B) public static function mod2(lhs : Int, rhs : system.Single) : system.Single; - @:op(A % B) public static function mod3(lhs : system.Single, rhs : Float) : system.Single; - @:op(A % B) public static function mod4(lhs : Float, rhs : system.Single) : system.Single; - - @:op(A % B) public static function mod5(lhs : system.Single, rhs : system.Char) : system.Single; - @:op(A % B) public static function mod6(lhs : system.Single, rhs : system.Byte) : system.Single; - @:op(A % B) public static function mod7(lhs : system.Single, rhs : system.Int16) : system.Single; - @:op(A % B) public static function mod8(lhs : system.Single, rhs : system.Int32) : system.Single; - @:op(A % B) public static function mod9(lhs : system.Single, rhs : system.Int64) : system.Single; - @:op(A % B) public static function mod10(lhs : system.Single, rhs : system.SByte) : system.Single; - @:op(A % B) public static function mod11(lhs : system.Single, rhs : system.UInt16) : system.Single; - @:op(A % B) public static function mod12(lhs : system.Single, rhs : system.UInt32) : system.Single; - @:op(A % B) public static function mod13(lhs : system.Single, rhs : system.UInt64) : system.Single; - @:op(A % B) public static function mod14(lhs : system.Single, rhs : system.Single) : system.Single; - @:op(A % B) public static function mod15(lhs : system.Single, rhs : system.Double) : system.Single; - - - @:op(A + B) public static function add1(lhs : system.Single, rhs : Int) : system.Single; - @:op(A + B) public static function add2(lhs : Int, rhs : system.Single) : system.Single; - @:op(A + B) public static function add3(lhs : system.Single, rhs : Float) : system.Single; - @:op(A + B) public static function add4(lhs : Float, rhs : system.Single) : system.Single; - - @:op(A + B) public static function add5(lhs : system.Single, rhs : system.Char) : system.Single; - @:op(A + B) public static function add6(lhs : system.Single, rhs : system.Byte) : system.Single; - @:op(A + B) public static function add7(lhs : system.Single, rhs : system.Int16) : system.Single; - @:op(A + B) public static function add8(lhs : system.Single, rhs : system.Int32) : system.Single; - @:op(A + B) public static function add9(lhs : system.Single, rhs : system.Int64) : system.Single; - @:op(A + B) public static function add10(lhs : system.Single, rhs : system.SByte) : system.Single; - @:op(A + B) public static function add11(lhs : system.Single, rhs : system.UInt16) : system.Single; - @:op(A + B) public static function add12(lhs : system.Single, rhs : system.UInt32) : system.Single; - @:op(A + B) public static function add13(lhs : system.Single, rhs : system.UInt64) : system.Single; - @:op(A + B) public static function add14(lhs : system.Single, rhs : system.Single) : system.Single; - @:op(A + B) public static function add15(lhs : system.Single, rhs : system.Double) : system.Single; - @:op(A + B) public static inline function add16(lhs : system.Single, rhs : system.CsString) : system.CsString return lhs.ToString() + rhs; - @:op(A + B) public static inline function add17(lhs : system.Single, rhs : String) : system.CsString return lhs.ToString() + rhs; - - - @:op(A - B) public static function sub1(lhs : system.Single, rhs : Int) : system.Single; - @:op(A - B) public static function sub2(lhs : Int, rhs : system.Single) : system.Single; - @:op(A - B) public static function sub3(lhs : system.Single, rhs : Float) : system.Single; - @:op(A - B) public static function sub4(lhs : Float, rhs : system.Single) : system.Single; - - @:op(A - B) public static function sub5(lhs : system.Single, rhs : system.Char) : system.Single; - @:op(A - B) public static function sub6(lhs : system.Single, rhs : system.Byte) : system.Single; - @:op(A - B) public static function sub7(lhs : system.Single, rhs : system.Int16) : system.Single; - @:op(A - B) public static function sub8(lhs : system.Single, rhs : system.Int32) : system.Single; - @:op(A - B) public static function sub9(lhs : system.Single, rhs : system.Int64) : system.Single; - @:op(A - B) public static function sub10(lhs : system.Single, rhs : system.SByte) : system.Single; - @:op(A - B) public static function sub11(lhs : system.Single, rhs : system.UInt16) : system.Single; - @:op(A - B) public static function sub12(lhs : system.Single, rhs : system.UInt32) : system.Single; - @:op(A - B) public static function sub13(lhs : system.Single, rhs : system.UInt64) : system.Single; - @:op(A - B) public static function sub14(lhs : system.Single, rhs : system.Single) : system.Single; - @:op(A - B) public static function sub15(lhs : system.Single, rhs : system.Double) : system.Single; - - - @:op(A > B) public static function gt1(lhs : system.Single, rhs : Int) : system.Boolean; - @:op(A > B) public static function gt2(lhs : Int, rhs : system.Single) : system.Boolean; - @:op(A > B) public static function gt3(lhs : system.Single, rhs : Float) : system.Boolean; - @:op(A > B) public static function gt4(lhs : Float, rhs : system.Single) : system.Boolean; - - @:op(A > B) public static function gt5(lhs : system.Single, rhs : system.Char) : system.Boolean; - @:op(A > B) public static function gt6(lhs : system.Single, rhs : system.Byte) : system.Boolean; - @:op(A > B) public static function gt7(lhs : system.Single, rhs : system.Int16) : system.Boolean; - @:op(A > B) public static function gt8(lhs : system.Single, rhs : system.Int32) : system.Boolean; - @:op(A > B) public static function gt9(lhs : system.Single, rhs : system.Int64) : system.Boolean; - @:op(A > B) public static function gt10(lhs : system.Single, rhs : system.SByte) : system.Boolean; - @:op(A > B) public static function gt11(lhs : system.Single, rhs : system.UInt16) : system.Boolean; - @:op(A > B) public static function gt12(lhs : system.Single, rhs : system.UInt32) : system.Boolean; - @:op(A > B) public static function gt13(lhs : system.Single, rhs : system.UInt64) : system.Boolean; - @:op(A > B) public static function gt14(lhs : system.Single, rhs : system.Single) : system.Boolean; - @:op(A > B) public static function gt15(lhs : system.Single, rhs : system.Double) : system.Boolean; - - @:op(A < B) public static function lt1(lhs : system.Single, rhs : Int) : system.Boolean; - @:op(A < B) public static function lt2(lhs : Int, rhs : system.Single) : system.Boolean; - @:op(A < B) public static function lt3(lhs : system.Single, rhs : Float) : system.Boolean; - @:op(A < B) public static function lt4(lhs : Float, rhs : system.Single) : system.Boolean; - - @:op(A < B) public static function lt5(lhs : system.Single, rhs : system.Char) : system.Boolean; - @:op(A < B) public static function lt6(lhs : system.Single, rhs : system.Byte) : system.Boolean; - @:op(A < B) public static function lt7(lhs : system.Single, rhs : system.Int16) : system.Boolean; - @:op(A < B) public static function lt8(lhs : system.Single, rhs : system.Int32) : system.Boolean; - @:op(A < B) public static function lt9(lhs : system.Single, rhs : system.Int64) : system.Boolean; - @:op(A < B) public static function lt10(lhs : system.Single, rhs : system.SByte) : system.Boolean; - @:op(A < B) public static function lt11(lhs : system.Single, rhs : system.UInt16) : system.Boolean; - @:op(A < B) public static function lt12(lhs : system.Single, rhs : system.UInt32) : system.Boolean; - @:op(A < B) public static function lt13(lhs : system.Single, rhs : system.UInt64) : system.Boolean; - @:op(A < B) public static function lt14(lhs : system.Single, rhs : system.Single) : system.Boolean; - @:op(A < B) public static function lt15(lhs : system.Single, rhs : system.Double) : system.Boolean; - - - @:op(A >= B) public static function gte1(lhs : system.Single, rhs : Int) : system.Boolean; - @:op(A >= B) public static function gte2(lhs : Int, rhs : system.Single) : system.Boolean; - @:op(A >= B) public static function gte3(lhs : system.Single, rhs : Float) : system.Boolean; - @:op(A >= B) public static function gte4(lhs : Float, rhs : system.Single) : system.Boolean; - - @:op(A >= B) public static function gte5(lhs : system.Single, rhs : system.Char) : system.Boolean; - @:op(A >= B) public static function gte6(lhs : system.Single, rhs : system.Byte) : system.Boolean; - @:op(A >= B) public static function gte7(lhs : system.Single, rhs : system.Int16) : system.Boolean; - @:op(A >= B) public static function gte8(lhs : system.Single, rhs : system.Int32) : system.Boolean; - @:op(A >= B) public static function gte9(lhs : system.Single, rhs : system.Int64) : system.Boolean; - @:op(A >= B) public static function gte10(lhs : system.Single, rhs : system.SByte) : system.Boolean; - @:op(A >= B) public static function gte11(lhs : system.Single, rhs : system.UInt16) : system.Boolean; - @:op(A >= B) public static function gte12(lhs : system.Single, rhs : system.UInt32) : system.Boolean; - @:op(A >= B) public static function gte13(lhs : system.Single, rhs : system.UInt64) : system.Boolean; - @:op(A >= B) public static function gte14(lhs : system.Single, rhs : system.Single) : system.Boolean; - @:op(A >= B) public static function gte15(lhs : system.Single, rhs : system.Double) : system.Boolean; - - - @:op(A <= B) public static function lte1(lhs : system.Single, rhs : Int) : system.Boolean; - @:op(A <= B) public static function lte2(lhs : Int, rhs : system.Single) : system.Boolean; - @:op(A <= B) public static function lte3(lhs : system.Single, rhs : Float) : system.Boolean; - @:op(A <= B) public static function lte4(lhs : Float, rhs : system.Single) : system.Boolean; - - @:op(A <= B) public static function lte5(lhs : system.Single, rhs : system.Char) : system.Boolean; - @:op(A <= B) public static function lte6(lhs : system.Single, rhs : system.Byte) : system.Boolean; - @:op(A <= B) public static function lte7(lhs : system.Single, rhs : system.Int16) : system.Boolean; - @:op(A <= B) public static function lte8(lhs : system.Single, rhs : system.Int32) : system.Boolean; - @:op(A <= B) public static function lte9(lhs : system.Single, rhs : system.Int64) : system.Boolean; - @:op(A <= B) public static function lte10(lhs : system.Single, rhs : system.SByte) : system.Boolean; - @:op(A <= B) public static function lte11(lhs : system.Single, rhs : system.UInt16) : system.Boolean; - @:op(A <= B) public static function lte12(lhs : system.Single, rhs : system.UInt32) : system.Boolean; - @:op(A <= B) public static function lte13(lhs : system.Single, rhs : system.UInt64) : system.Boolean; - @:op(A <= B) public static function lte14(lhs : system.Single, rhs : system.Single) : system.Boolean; - @:op(A <= B) public static function lte15(lhs : system.Single, rhs : system.Double) : system.Boolean; - - @:op(A == B) public static function eq1(lhs : system.Single, rhs : Int) : system.Boolean; - @:op(A == B) public static function eq2(lhs : Int, rhs : system.Single) : system.Boolean; - @:op(A == B) public static function eq3(lhs : system.Single, rhs : Float) : system.Boolean; - @:op(A == B) public static function eq4(lhs : Float, rhs : system.Single) : system.Boolean; - - @:op(A == B) public static function eq5(lhs : system.Single, rhs : system.Char) : system.Boolean; - @:op(A == B) public static function eq6(lhs : system.Single, rhs : system.Byte) : system.Boolean; - @:op(A == B) public static function eq7(lhs : system.Single, rhs : system.Int16) : system.Boolean; - @:op(A == B) public static function eq8(lhs : system.Single, rhs : system.Int32) : system.Boolean; - @:op(A == B) public static function eq9(lhs : system.Single, rhs : system.Int64) : system.Boolean; - @:op(A == B) public static function eq10(lhs : system.Single, rhs : system.SByte) : system.Boolean; - @:op(A == B) public static function eq11(lhs : system.Single, rhs : system.UInt16) : system.Boolean; - @:op(A == B) public static function eq12(lhs : system.Single, rhs : system.UInt32) : system.Boolean; - @:op(A == B) public static function eq13(lhs : system.Single, rhs : system.UInt64) : system.Boolean; - @:op(A == B) public static function eq14(lhs : system.Single, rhs : system.Single) : system.Boolean; - @:op(A == B) public static function eq15(lhs : system.Single, rhs : system.Double) : system.Boolean; - - - @:op(A != B) public static function neq1(lhs : system.Single, rhs : Int) : system.Boolean; - @:op(A != B) public static function neq2(lhs : Int, rhs : system.Single) : system.Boolean; - @:op(A != B) public static function neq3(lhs : system.Single, rhs : Float) : system.Boolean; - @:op(A != B) public static function neq4(lhs : Float, rhs : system.Single) : system.Boolean; - - @:op(A != B) public static function neq5(lhs : system.Single, rhs : system.Char) : system.Boolean; - @:op(A != B) public static function neq6(lhs : system.Single, rhs : system.Byte) : system.Boolean; - @:op(A != B) public static function neq7(lhs : system.Single, rhs : system.Int16) : system.Boolean; - @:op(A != B) public static function neq8(lhs : system.Single, rhs : system.Int32) : system.Boolean; - @:op(A != B) public static function neq9(lhs : system.Single, rhs : system.Int64) : system.Boolean; - @:op(A != B) public static function neq10(lhs : system.Single, rhs : system.SByte) : system.Boolean; - @:op(A != B) public static function neq11(lhs : system.Single, rhs : system.UInt16) : system.Boolean; - @:op(A != B) public static function neq12(lhs : system.Single, rhs : system.UInt32) : system.Boolean; - @:op(A != B) public static function neq13(lhs : system.Single, rhs : system.UInt64) : system.Boolean; - @:op(A != B) public static function neq14(lhs : system.Single, rhs : system.Single) : system.Boolean; - @:op(A != B) public static function neq15(lhs : system.Single, rhs : system.Double) : system.Boolean; - -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/SingleArray.hx b/Phase/Mscorlib/system/SingleArray.hx deleted file mode 100644 index b46c6b59c..000000000 --- a/Phase/Mscorlib/system/SingleArray.hx +++ /dev/null @@ -1,21 +0,0 @@ -package system; - -abstract SingleArray(js.html.Float32Array) -{ - public inline function new(length:Int32) this = new js.html.Float32Array(length.ToHaxeInt()); - - @:from public static inline function fromArray(a:Array):SingleArray return cast new js.html.Float32Array(untyped a); - @:from public static inline function fromFixedArray(a:FixedArray):SingleArray return cast new js.html.Float32Array(untyped a.ToHaxeArray()); - - public var Length(get, never):Int32; - public inline function get_Length() : Int32 return this.length; - - @:op([]) public inline function get(index:Int32):Single return this[index.ToHaxeInt()]; - @:op([]) public inline function set(index:Int32, val:Single):Single return this[index.ToHaxeInt()] = val.ToHaxeFloat(); - - public inline function iterator() : Iterator return new SingleArrayIterator(this); - - public inline function ToEnumerable() : system.collections.generic.IEnumerable return new SingleArrayEnumerable(this); - - public static inline function empty(size:Int32) return new SingleArray(size); -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/SingleArrayEnumerable.hx b/Phase/Mscorlib/system/SingleArrayEnumerable.hx deleted file mode 100644 index 99e63cc21..000000000 --- a/Phase/Mscorlib/system/SingleArrayEnumerable.hx +++ /dev/null @@ -1,18 +0,0 @@ -package system; - -import system.collections.generic.IEnumerable; -import system.collections.generic.IEnumerator; - -class SingleArrayEnumerable implements IEnumerable -{ - private var _array:js.html.Float32Array; - public function new(array:js.html.Float32Array) - { - _array= array; - } - - public function GetEnumerator() : IEnumerator - { - return new SingleArrayEnumerator(_array); - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/SingleArrayEnumerator.hx b/Phase/Mscorlib/system/SingleArrayEnumerator.hx deleted file mode 100644 index 3ca962f4d..000000000 --- a/Phase/Mscorlib/system/SingleArrayEnumerator.hx +++ /dev/null @@ -1,29 +0,0 @@ -package system; - -import system.collections.generic.IEnumerator; - -class SingleArrayEnumerator implements IEnumerator -{ - private var _array:js.html.Float32Array; - private var _i:Int; - - public function new(array:js.html.Float32Array) - { - _array = array; - _i = -1; - } - - public var Current(get, never):Single; - public function get_Current() : Single return _array[_i]; - public function MoveNext() : Bool - { - if(_i >= _array.length - 1) return false; - _i++; - return true; - - } - public function Reset():Void - { - _i = -1; - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/SingleArrayIterator.hx b/Phase/Mscorlib/system/SingleArrayIterator.hx deleted file mode 100644 index 619d9da31..000000000 --- a/Phase/Mscorlib/system/SingleArrayIterator.hx +++ /dev/null @@ -1,19 +0,0 @@ -package system; - -class SingleArrayIterator -{ - private var _array:js.html.Float32Array; - private var _i:Int; - - public function new(array:js.html.Float32Array) - { - _array = array; - _i = 0; - } - - public function hasNext() return _i < _array.length; - public function next() - { - return _array[_i++]; - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/UInt16.hx b/Phase/Mscorlib/system/UInt16.hx deleted file mode 100644 index 898882fdc..000000000 --- a/Phase/Mscorlib/system/UInt16.hx +++ /dev/null @@ -1,321 +0,0 @@ -package system; - - -abstract UInt16(Int) from Int -{ - public inline function new(i:Int) this = system.Convert.ToUInt16(i); - - public inline function ToHaxeInt(): Int return this; - public inline function ToString() : system.CsString return Std.string(this); - - public inline function ToBoolean_IFormatProvider(provider:IFormatProvider) : system.Boolean return system.Convert.ToBoolean_UInt16(this); - public inline function ToChar_IFormatProvider(provider:IFormatProvider) : system.Char return system.Convert.ToChar_UInt16(this); - public inline function ToSByte_IFormatProvider(provider:IFormatProvider) : system.SByte return system.Convert.ToSByte_UInt16(this); - public inline function ToByte_IFormatProvider(provider:IFormatProvider) : system.Byte return system.Convert.ToByte_UInt16(this); - public inline function ToInt16_IFormatProvider(provider:IFormatProvider) : system.Int16 return system.Convert.ToInt16_UInt16(this); - public inline function ToUInt16_IFormatProvider(provider:IFormatProvider) : system.UInt16 return system.Convert.ToUInt16_UInt16(this); - public inline function ToInt32_IFormatProvider(provider:IFormatProvider) : system.Int32 return system.Convert.ToInt32_UInt16(this); - public inline function ToUInt32_IFormatProvider(provider:IFormatProvider) : system.UInt32 return system.Convert.ToUInt32_UInt16(this); - public inline function ToInt64_IFormatProvider(provider:IFormatProvider) : system.Int64 return system.Convert.ToInt64_UInt16(this); - public inline function ToUInt64_IFormatProvider(provider:IFormatProvider) : system.UInt64 return system.Convert.ToUInt64_UInt16(this); - public inline function ToSingle_IFormatProvider(provider:IFormatProvider) : system.Single return system.Convert.ToSingle_UInt16(this); - public inline function ToDouble_IFormatProvider(provider:IFormatProvider) : system.Double return system.Convert.ToDouble_UInt16(this); - - public function GetHashCode() : system.Int32 return this; - - @:op(-A) public inline function neg() : system.Int32 return -this; - - @:op(~A)public inline function not() : system.Int32 return ~this; - - @:op(A++)public inline function postinc() : system.UInt16 return this++; - @:op(++A)public inline function preinc() : system.UInt16 return ++this; - - @:op(A--)public inline function postdec() : system.UInt16 return this--; - @:op(--A)public inline function predec() : system.UInt16 return --this; - - @:op(A * B) public static function mul0(lhs : system.UInt16, rhs : system.UInt16) : system.Int32; - @:op(A * B) public static function mul1(lhs : system.UInt16, rhs : Int) : system.Int32; - @:op(A * B) public static function mul2(lhs : Int, rhs : system.UInt16) : system.Int32; - @:op(A * B) public static function mul3(lhs : system.UInt16, rhs : Float) : system.Double; - @:op(A * B) public static function mul4(lhs : Float, rhs : system.UInt16) : system.Double; - - @:op(A * B) public static function mul5(lhs : system.UInt16, rhs : system.Char) : system.Int32; - @:op(A * B) public static function mul6(lhs : system.UInt16, rhs : system.Byte) : system.Int32; - @:op(A * B) public static function mul7(lhs : system.UInt16, rhs : system.Int16) : system.Int32; - @:op(A * B) public static function mul8(lhs : system.UInt16, rhs : system.Int32) : system.Int32; - @:op(A * B) public static function mul9(lhs : system.UInt16, rhs : system.Int64) : system.Int64; - @:op(A * B) public static function mul10(lhs : system.UInt16, rhs : system.SByte) : system.Int32; - @:op(A * B) public static function mul11(lhs : system.UInt16, rhs : system.UInt16) : system.Int32; - @:op(A * B) public static function mul12(lhs : system.UInt16, rhs : system.UInt32) : system.UInt32; - @:op(A * B) public static function mul13(lhs : system.UInt16, rhs : system.UInt64) : system.UInt64; - @:op(A * B) public static function mul14(lhs : system.UInt16, rhs : system.Single) : system.Single; - @:op(A * B) public static function mul15(lhs : system.UInt16, rhs : system.Double) : system.Double; - - - @:op(A / B) public static inline function div0(lhs : system.UInt16, rhs : system.UInt16) : system.Int32 return Std.int(lhs.ToHaxeInt() / rhs.ToHaxeInt()); - @:op(A / B) public static inline function div1(lhs : system.UInt16, rhs : Int) : system.Int32 return Std.int(lhs.ToHaxeInt() / rhs); - @:op(A / B) public static inline function div2(lhs : Int, rhs : system.UInt16) : system.Int32 return Std.int(lhs / rhs.ToHaxeInt()); - @:op(A / B) public static function div3(lhs : system.UInt16, rhs : Float) : system.Double; - @:op(A / B) public static function div4(lhs : Float, rhs : system.UInt16) : system.Double; - - @:op(A / B) public static function div5(lhs : system.UInt16, rhs : system.Char) : system.Int32; - @:op(A / B) public static function div6(lhs : system.UInt16, rhs : system.Byte) : system.Int32; - @:op(A / B) public static function div7(lhs : system.UInt16, rhs : system.Int16) : system.Int32; - @:op(A / B) public static function div8(lhs : system.UInt16, rhs : system.Int32) : system.Int32; - @:op(A / B) public static function div9(lhs : system.UInt16, rhs : system.Int64) : system.Int64; - @:op(A / B) public static function div10(lhs : system.UInt16, rhs : system.SByte) : system.Int32; - @:op(A / B) public static function div11(lhs : system.UInt16, rhs : system.UInt16) : system.Int32; - @:op(A / B) public static function div12(lhs : system.UInt16, rhs : system.UInt32) : system.UInt32; - @:op(A / B) public static function div13(lhs : system.UInt16, rhs : system.UInt64) : system.UInt64; - @:op(A / B) public static function div14(lhs : system.UInt16, rhs : system.Single) : system.Single; - @:op(A / B) public static function div15(lhs : system.UInt16, rhs : system.Double) : system.Double; - - - @:op(A % B) public static function mod0(lhs : system.UInt16, rhs : system.UInt16) : system.Int32; - @:op(A % B) public static function mod1(lhs : system.UInt16, rhs : Int) : system.Int32; - @:op(A % B) public static function mod2(lhs : Int, rhs : system.UInt16) : system.Int32; - @:op(A % B) public static function mod3(lhs : system.UInt16, rhs : Float) : system.Double; - @:op(A % B) public static function mod4(lhs : Float, rhs : system.UInt16) : system.Double; - - @:op(A % B) public static function mod5(lhs : system.UInt16, rhs : system.Char) : system.Int32; - @:op(A % B) public static function mod6(lhs : system.UInt16, rhs : system.Byte) : system.Int32; - @:op(A % B) public static function mod7(lhs : system.UInt16, rhs : system.Int16) : system.Int32; - @:op(A % B) public static function mod8(lhs : system.UInt16, rhs : system.Int32) : system.Int32; - @:op(A % B) public static function mod9(lhs : system.UInt16, rhs : system.Int64) : system.Int64; - @:op(A % B) public static function mod10(lhs : system.UInt16, rhs : system.SByte) : system.Int32; - @:op(A % B) public static function mod11(lhs : system.UInt16, rhs : system.UInt16) : system.Int32; - @:op(A % B) public static function mod12(lhs : system.UInt16, rhs : system.UInt32) : system.UInt32; - @:op(A % B) public static function mod13(lhs : system.UInt16, rhs : system.UInt64) : system.UInt64; - @:op(A % B) public static function mod14(lhs : system.UInt16, rhs : system.Single) : system.Single; - @:op(A % B) public static function mod15(lhs : system.UInt16, rhs : system.Double) : system.Double; - - - @:op(A + B) public static function add0(lhs : system.UInt16, rhs : system.UInt16) : system.Int32; - @:op(A + B) public static function add1(lhs : system.UInt16, rhs : Int) : system.Int32; - @:op(A + B) public static function add2(lhs : Int, rhs : system.UInt16) : system.Int32; - @:op(A + B) public static function add3(lhs : system.UInt16, rhs : Float) : system.Double; - @:op(A + B) public static function add4(lhs : Float, rhs : system.UInt16) : system.Double; - - @:op(A + B) public static function add5(lhs : system.UInt16, rhs : system.Char) : system.Int32; - @:op(A + B) public static function add6(lhs : system.UInt16, rhs : system.Byte) : system.Int32; - @:op(A + B) public static function add7(lhs : system.UInt16, rhs : system.Int16) : system.Int32; - @:op(A + B) public static function add8(lhs : system.UInt16, rhs : system.Int32) : system.Int32; - @:op(A + B) public static function add9(lhs : system.UInt16, rhs : system.Int64) : system.Int64; - @:op(A + B) public static function add10(lhs : system.UInt16, rhs : system.SByte) : system.Int32; - @:op(A + B) public static function add11(lhs : system.UInt16, rhs : system.UInt16) : system.Int32; - @:op(A + B) public static function add12(lhs : system.UInt16, rhs : system.UInt32) : system.UInt32; - @:op(A + B) public static function add13(lhs : system.UInt16, rhs : system.UInt64) : system.UInt64; - @:op(A + B) public static function add14(lhs : system.UInt16, rhs : system.Single) : system.Single; - @:op(A + B) public static function add15(lhs : system.UInt16, rhs : system.Double) : system.Double; - @:op(A + B) public static inline function add16(lhs : system.UInt16, rhs : system.CsString) : system.CsString return lhs.ToString() + rhs; - @:op(A + B) public static inline function add17(lhs : system.UInt16, rhs : String) : system.CsString return lhs.ToString() + rhs; - - - @:op(A - B) public static function sub0(lhs : system.UInt16, rhs : system.UInt16) : system.Int32; - @:op(A - B) public static function sub1(lhs : system.UInt16, rhs : Int) : system.Int32; - @:op(A - B) public static function sub2(lhs : Int, rhs : system.UInt16) : system.Int32; - @:op(A - B) public static function sub3(lhs : system.UInt16, rhs : Float) : system.Double; - @:op(A - B) public static function sub4(lhs : Float, rhs : system.UInt16) : system.Double; - - @:op(A - B) public static function sub5(lhs : system.UInt16, rhs : system.Char) : system.Int32; - @:op(A - B) public static function sub6(lhs : system.UInt16, rhs : system.Byte) : system.Int32; - @:op(A - B) public static function sub7(lhs : system.UInt16, rhs : system.Int16) : system.Int32; - @:op(A - B) public static function sub8(lhs : system.UInt16, rhs : system.Int32) : system.Int32; - @:op(A - B) public static function sub9(lhs : system.UInt16, rhs : system.Int64) : system.Int64; - @:op(A - B) public static function sub10(lhs : system.UInt16, rhs : system.SByte) : system.Int32; - @:op(A - B) public static function sub11(lhs : system.UInt16, rhs : system.UInt16) : system.Int32; - @:op(A - B) public static function sub12(lhs : system.UInt16, rhs : system.UInt32) : system.UInt32; - @:op(A - B) public static function sub13(lhs : system.UInt16, rhs : system.UInt64) : system.UInt64; - @:op(A - B) public static function sub14(lhs : system.UInt16, rhs : system.Single) : system.Single; - @:op(A - B) public static function sub15(lhs : system.UInt16, rhs : system.Double) : system.Double; - - - @:op(A << B) public static function shl0(lhs : system.UInt16, rhs : system.UInt16) : system.Int32; - @:op(A << B) public static function shl1(lhs : system.UInt16, rhs : Int) : system.Int32; - @:op(A << B) public static function shl2(lhs : Int, rhs : system.UInt16) : system.Int32; - - @:op(A << B) public static function shl5(lhs : system.UInt16, rhs : system.Char) : system.Int32; - @:op(A << B) public static function shl6(lhs : system.UInt16, rhs : system.Byte) : system.Int32; - @:op(A << B) public static function shl7(lhs : system.UInt16, rhs : system.Int16) : system.Int32; - @:op(A << B) public static function shl8(lhs : system.UInt16, rhs : system.Int32) : system.Int32; - // @:op(A << B) public static function shl9(lhs : system.UInt16, rhs : system.Int64) : system.Int64; - @:op(A << B) public static function shl10(lhs : system.UInt16, rhs : system.SByte) : system.Int32; - @:op(A << B) public static function shl11(lhs : system.UInt16, rhs : system.UInt16) : system.Int32; - // @:op(A << B) public static function shl12(lhs : system.UInt16, rhs : system.UInt32) : system.UInt32; - // @:op(A << B) public static function shl13(lhs : system.UInt16, rhs : system.UInt64) : system.UInt64; - - - @:op(A >> B) public static function shr0(lhs : system.UInt16, rhs : system.UInt16) : system.Int32; - @:op(A >> B) public static function shr1(lhs : system.UInt16, rhs : Int) : system.Int32; - @:op(A >> B) public static function shr2(lhs : Int, rhs : system.UInt16) : system.Int32; - - @:op(A >> B) public static function shr5(lhs : system.UInt16, rhs : system.Char) : system.Int32; - @:op(A >> B) public static function shr6(lhs : system.UInt16, rhs : system.Byte) : system.Int32; - @:op(A >> B) public static function shr7(lhs : system.UInt16, rhs : system.Int16) : system.Int32; - @:op(A >> B) public static function shr8(lhs : system.UInt16, rhs : system.Int32) : system.Int32; - // @:op(A >> B) public static function shr9(lhs : system.UInt16, rhs : system.Int64) : system.Int64; - @:op(A >> B) public static function shr10(lhs : system.UInt16, rhs : system.SByte) : system.Int32; - @:op(A >> B) public static function shr11(lhs : system.UInt16, rhs : system.UInt16) : system.Int32; - // @:op(A >> B) public static function shr12(lhs : system.UInt16, rhs : system.UInt32) : system.UInt32; - // @:op(A >> B) public static function shr13(lhs : system.UInt16, rhs : system.UInt64) : system.UInt64; - - - @:op(A > B) public static function gt0(lhs : system.UInt16, rhs : system.UInt16) : system.Boolean; - @:op(A > B) public static function gt1(lhs : system.UInt16, rhs : Int) : system.Boolean; - @:op(A > B) public static function gt2(lhs : Int, rhs : system.UInt16) : system.Boolean; - @:op(A > B) public static function gt3(lhs : system.UInt16, rhs : Float) : system.Boolean; - @:op(A > B) public static function gt4(lhs : Float, rhs : system.UInt16) : system.Boolean; - - @:op(A > B) public static function gt5(lhs : system.UInt16, rhs : system.Char) : system.Boolean; - @:op(A > B) public static function gt6(lhs : system.UInt16, rhs : system.Byte) : system.Boolean; - @:op(A > B) public static function gt7(lhs : system.UInt16, rhs : system.Int16) : system.Boolean; - @:op(A > B) public static function gt8(lhs : system.UInt16, rhs : system.Int32) : system.Boolean; - @:op(A > B) public static function gt9(lhs : system.UInt16, rhs : system.Int64) : system.Boolean; - @:op(A > B) public static function gt10(lhs : system.UInt16, rhs : system.SByte) : system.Boolean; - @:op(A > B) public static function gt11(lhs : system.UInt16, rhs : system.UInt16) : system.Boolean; - @:op(A > B) public static function gt12(lhs : system.UInt16, rhs : system.UInt32) : system.Boolean; - @:op(A > B) public static function gt13(lhs : system.UInt16, rhs : system.UInt64) : system.Boolean; - @:op(A > B) public static function gt14(lhs : system.UInt16, rhs : system.Single) : system.Boolean; - @:op(A > B) public static function gt15(lhs : system.UInt16, rhs : system.Double) : system.Boolean; - - - @:op(A < B) public static function lt0(lhs : system.UInt16, rhs : system.UInt16) : system.Boolean; - @:op(A < B) public static function lt1(lhs : system.UInt16, rhs : Int) : system.Boolean; - @:op(A < B) public static function lt2(lhs : Int, rhs : system.UInt16) : system.Boolean; - @:op(A < B) public static function lt3(lhs : system.UInt16, rhs : Float) : system.Boolean; - @:op(A < B) public static function lt4(lhs : Float, rhs : system.UInt16) : system.Boolean; - - @:op(A < B) public static function lt5(lhs : system.UInt16, rhs : system.Char) : system.Boolean; - @:op(A < B) public static function lt6(lhs : system.UInt16, rhs : system.Byte) : system.Boolean; - @:op(A < B) public static function lt7(lhs : system.UInt16, rhs : system.Int16) : system.Boolean; - @:op(A < B) public static function lt8(lhs : system.UInt16, rhs : system.Int32) : system.Boolean; - @:op(A < B) public static function lt9(lhs : system.UInt16, rhs : system.Int64) : system.Boolean; - @:op(A < B) public static function lt10(lhs : system.UInt16, rhs : system.SByte) : system.Boolean; - @:op(A < B) public static function lt11(lhs : system.UInt16, rhs : system.UInt16) : system.Boolean; - @:op(A < B) public static function lt12(lhs : system.UInt16, rhs : system.UInt32) : system.Boolean; - @:op(A < B) public static function lt13(lhs : system.UInt16, rhs : system.UInt64) : system.Boolean; - @:op(A < B) public static function lt14(lhs : system.UInt16, rhs : system.Single) : system.Boolean; - @:op(A < B) public static function lt15(lhs : system.UInt16, rhs : system.Double) : system.Boolean; - - - @:op(A >= B) public static function gte0(lhs : system.UInt16, rhs : system.UInt16) : system.Boolean; - @:op(A >= B) public static function gte1(lhs : system.UInt16, rhs : Int) : system.Boolean; - @:op(A >= B) public static function gte2(lhs : Int, rhs : system.UInt16) : system.Boolean; - @:op(A >= B) public static function gte3(lhs : system.UInt16, rhs : Float) : system.Boolean; - @:op(A >= B) public static function gte4(lhs : Float, rhs : system.UInt16) : system.Boolean; - - @:op(A >= B) public static function gte5(lhs : system.UInt16, rhs : system.Char) : system.Boolean; - @:op(A >= B) public static function gte6(lhs : system.UInt16, rhs : system.Byte) : system.Boolean; - @:op(A >= B) public static function gte7(lhs : system.UInt16, rhs : system.Int16) : system.Boolean; - @:op(A >= B) public static function gte8(lhs : system.UInt16, rhs : system.Int32) : system.Boolean; - @:op(A >= B) public static function gte9(lhs : system.UInt16, rhs : system.Int64) : system.Boolean; - @:op(A >= B) public static function gte10(lhs : system.UInt16, rhs : system.SByte) : system.Boolean; - @:op(A >= B) public static function gte11(lhs : system.UInt16, rhs : system.UInt16) : system.Boolean; - @:op(A >= B) public static function gte12(lhs : system.UInt16, rhs : system.UInt32) : system.Boolean; - @:op(A >= B) public static function gte13(lhs : system.UInt16, rhs : system.UInt64) : system.Boolean; - @:op(A >= B) public static function gte14(lhs : system.UInt16, rhs : system.Single) : system.Boolean; - @:op(A >= B) public static function gte15(lhs : system.UInt16, rhs : system.Double) : system.Boolean; - - - @:op(A <= B) public static function lte0(lhs : system.UInt16, rhs : system.UInt16) : system.Boolean; - @:op(A <= B) public static function lte1(lhs : system.UInt16, rhs : Int) : system.Boolean; - @:op(A <= B) public static function lte2(lhs : Int, rhs : system.UInt16) : system.Boolean; - @:op(A <= B) public static function lte3(lhs : system.UInt16, rhs : Float) : system.Boolean; - @:op(A <= B) public static function lte4(lhs : Float, rhs : system.UInt16) : system.Boolean; - - @:op(A <= B) public static function lte5(lhs : system.UInt16, rhs : system.Char) : system.Boolean; - @:op(A <= B) public static function lte6(lhs : system.UInt16, rhs : system.Byte) : system.Boolean; - @:op(A <= B) public static function lte7(lhs : system.UInt16, rhs : system.Int16) : system.Boolean; - @:op(A <= B) public static function lte8(lhs : system.UInt16, rhs : system.Int32) : system.Boolean; - @:op(A <= B) public static function lte9(lhs : system.UInt16, rhs : system.Int64) : system.Boolean; - @:op(A <= B) public static function lte10(lhs : system.UInt16, rhs : system.SByte) : system.Boolean; - @:op(A <= B) public static function lte11(lhs : system.UInt16, rhs : system.UInt16) : system.Boolean; - @:op(A <= B) public static function lte12(lhs : system.UInt16, rhs : system.UInt32) : system.Boolean; - @:op(A <= B) public static function lte13(lhs : system.UInt16, rhs : system.UInt64) : system.Boolean; - @:op(A <= B) public static function lte14(lhs : system.UInt16, rhs : system.Single) : system.Boolean; - @:op(A <= B) public static function lte15(lhs : system.UInt16, rhs : system.Double) : system.Boolean; - - - @:op(A == B) public static function eq0(lhs : system.UInt16, rhs : system.UInt16) : system.Boolean; - @:op(A == B) public static function eq1(lhs : system.UInt16, rhs : Int) : system.Boolean; - @:op(A == B) public static function eq2(lhs : Int, rhs : system.UInt16) : system.Boolean; - @:op(A == B) public static function eq3(lhs : system.UInt16, rhs : Float) : system.Boolean; - @:op(A == B) public static function eq4(lhs : Float, rhs : system.UInt16) : system.Boolean; - - @:op(A == B) public static function eq5(lhs : system.UInt16, rhs : system.Char) : system.Boolean; - @:op(A == B) public static function eq6(lhs : system.UInt16, rhs : system.Byte) : system.Boolean; - @:op(A == B) public static function eq7(lhs : system.UInt16, rhs : system.Int16) : system.Boolean; - @:op(A == B) public static function eq8(lhs : system.UInt16, rhs : system.Int32) : system.Boolean; - @:op(A == B) public static function eq9(lhs : system.UInt16, rhs : system.Int64) : system.Boolean; - @:op(A == B) public static function eq10(lhs : system.UInt16, rhs : system.SByte) : system.Boolean; - @:op(A == B) public static function eq11(lhs : system.UInt16, rhs : system.UInt16) : system.Boolean; - @:op(A == B) public static function eq12(lhs : system.UInt16, rhs : system.UInt32) : system.Boolean; - @:op(A == B) public static function eq13(lhs : system.UInt16, rhs : system.UInt64) : system.Boolean; - @:op(A == B) public static function eq14(lhs : system.UInt16, rhs : system.Single) : system.Boolean; - @:op(A == B) public static function eq15(lhs : system.UInt16, rhs : system.Double) : system.Boolean; - - - @:op(A != B) public static function neq0(lhs : system.UInt16, rhs : system.UInt16) : system.Boolean; - @:op(A != B) public static function neq1(lhs : system.UInt16, rhs : Int) : system.Boolean; - @:op(A != B) public static function neq2(lhs : Int, rhs : system.UInt16) : system.Boolean; - @:op(A != B) public static function neq3(lhs : system.UInt16, rhs : Float) : system.Boolean; - @:op(A != B) public static function neq4(lhs : Float, rhs : system.UInt16) : system.Boolean; - - @:op(A != B) public static function neq5(lhs : system.UInt16, rhs : system.Char) : system.Boolean; - @:op(A != B) public static function neq6(lhs : system.UInt16, rhs : system.Byte) : system.Boolean; - @:op(A != B) public static function neq7(lhs : system.UInt16, rhs : system.Int16) : system.Boolean; - @:op(A != B) public static function neq8(lhs : system.UInt16, rhs : system.Int32) : system.Boolean; - @:op(A != B) public static function neq9(lhs : system.UInt16, rhs : system.Int64) : system.Boolean; - @:op(A != B) public static function neq10(lhs : system.UInt16, rhs : system.SByte) : system.Boolean; - @:op(A != B) public static function neq11(lhs : system.UInt16, rhs : system.UInt16) : system.Boolean; - @:op(A != B) public static function neq12(lhs : system.UInt16, rhs : system.UInt32) : system.Boolean; - @:op(A != B) public static function neq13(lhs : system.UInt16, rhs : system.UInt64) : system.Boolean; - @:op(A != B) public static function neq14(lhs : system.UInt16, rhs : system.Single) : system.Boolean; - @:op(A != B) public static function neq15(lhs : system.UInt16, rhs : system.Double) : system.Boolean; - - - @:op(A & B) public static function and0(lhs : system.UInt16, rhs : system.UInt16) : system.Int32; - @:op(A & B) public static function and1(lhs : system.UInt16, rhs : Int) : system.Int32; - @:op(A & B) public static function and2(lhs : Int, rhs : system.UInt16) : system.Int32; - - @:op(A & B) public static function and5(lhs : system.UInt16, rhs : system.Char) : system.Int32; - @:op(A & B) public static function and6(lhs : system.UInt16, rhs : system.Byte) : system.Int32; - @:op(A & B) public static function and7(lhs : system.UInt16, rhs : system.Int16) : system.Int32; - @:op(A & B) public static function and8(lhs : system.UInt16, rhs : system.Int32) : system.Int32; - @:op(A & B) public static function and9(lhs : system.UInt16, rhs : system.Int64) : system.Int64; - @:op(A & B) public static function and10(lhs : system.UInt16, rhs : system.SByte) : system.Int32; - @:op(A & B) public static function and11(lhs : system.UInt16, rhs : system.UInt16) : system.Int32; - @:op(A & B) public static function and12(lhs : system.UInt16, rhs : system.UInt32) : system.UInt32; - @:op(A & B) public static function and13(lhs : system.UInt16, rhs : system.UInt64) : system.UInt64; - - - @:op(A | B) public static function or0(lhs : system.UInt16, rhs : system.UInt16) : system.Int32; - @:op(A | B) public static function or1(lhs : system.UInt16, rhs : Int) : system.Int32; - @:op(A | B) public static function or2(lhs : Int, rhs : system.UInt16) : system.Int32; - - @:op(A | B) public static function or5(lhs : system.UInt16, rhs : system.Char) : system.Int32; - @:op(A | B) public static function or6(lhs : system.UInt16, rhs : system.Byte) : system.Int32; - @:op(A | B) public static function or7(lhs : system.UInt16, rhs : system.Int16) : system.Int32; - @:op(A | B) public static function or8(lhs : system.UInt16, rhs : system.Int32) : system.Int32; - @:op(A | B) public static function or9(lhs : system.UInt16, rhs : system.Int64) : system.Int64; - @:op(A | B) public static function or10(lhs : system.UInt16, rhs : system.SByte) : system.Int32; - @:op(A | B) public static function or11(lhs : system.UInt16, rhs : system.UInt16) : system.Int32; - @:op(A | B) public static function or12(lhs : system.UInt16, rhs : system.UInt32) : system.UInt32; - @:op(A | B) public static function or13(lhs : system.UInt16, rhs : system.UInt64) : system.UInt64; - - - @:op(A ^ B) public static function xor0(lhs : system.UInt16, rhs : system.UInt16) : system.Int32; - @:op(A ^ B) public static function xor1(lhs : system.UInt16, rhs : Int) : system.Int32; - @:op(A ^ B) public static function xor2(lhs : Int, rhs : system.UInt16) : system.Int32; - - @:op(A ^ B) public static function xor5(lhs : system.UInt16, rhs : system.Char) : system.Int32; - @:op(A ^ B) public static function xor6(lhs : system.UInt16, rhs : system.Byte) : system.Int32; - @:op(A ^ B) public static function xor7(lhs : system.UInt16, rhs : system.Int16) : system.Int32; - @:op(A ^ B) public static function xor8(lhs : system.UInt16, rhs : system.Int32) : system.Int32; - @:op(A ^ B) public static function xor9(lhs : system.UInt16, rhs : system.Int64) : system.Int64; - @:op(A ^ B) public static function xor10(lhs : system.UInt16, rhs : system.SByte) : system.Int32; - @:op(A ^ B) public static function xor11(lhs : system.UInt16, rhs : system.UInt16) : system.Int32; - @:op(A ^ B) public static function xor12(lhs : system.UInt16, rhs : system.UInt32) : system.UInt32; - @:op(A ^ B) public static function xor13(lhs : system.UInt16, rhs : system.UInt64) : system.UInt64; - -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/UInt16Array.hx b/Phase/Mscorlib/system/UInt16Array.hx deleted file mode 100644 index af471c71f..000000000 --- a/Phase/Mscorlib/system/UInt16Array.hx +++ /dev/null @@ -1,21 +0,0 @@ -package system; - -abstract UInt16Array(js.html.Uint16Array) -{ - public inline function new(length:Int32) this = new js.html.Uint16Array(length.ToHaxeInt()); - - @:from public static inline function fromArray(a:Array):UInt16Array return cast new js.html.Uint16Array(untyped a); - @:from public static inline function fromFixedArray(a:FixedArray):UInt16Array return cast new js.html.Uint16Array(untyped a.ToHaxeArray()); - - public var Length(get, never):Int32; - public inline function get_Length() : Int32 return this.length; - - @:op([]) public inline function get(index:Int32):UInt16 return this[index.ToHaxeInt()]; - @:op([]) public inline function set(index:Int32, val:UInt16):UInt16 return this[index.ToHaxeInt()] = val.ToHaxeFloat(); - - public inline function iterator() : Iterator return new UInt16ArrayIterator(this); - - public inline function ToEnumerable() : system.collections.generic.IEnumerable return new UInt16ArrayEnumerable(this); - - public static inline function empty(size:Int32) return new UInt16Array(size); -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/UInt16ArrayEnumerable.hx b/Phase/Mscorlib/system/UInt16ArrayEnumerable.hx deleted file mode 100644 index d599804d2..000000000 --- a/Phase/Mscorlib/system/UInt16ArrayEnumerable.hx +++ /dev/null @@ -1,18 +0,0 @@ -package system; - -import system.collections.generic.IEnumerable; -import system.collections.generic.IEnumerator; - -class UInt16ArrayEnumerable implements IEnumerable -{ - private var _array:js.html.Uint16Array; - public function new(array:js.html.Uint16Array) - { - _array= array; - } - - public function GetEnumerator() : IEnumerator - { - return new UInt16ArrayEnumerator(_array); - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/UInt16ArrayEnumerator.hx b/Phase/Mscorlib/system/UInt16ArrayEnumerator.hx deleted file mode 100644 index 8af965218..000000000 --- a/Phase/Mscorlib/system/UInt16ArrayEnumerator.hx +++ /dev/null @@ -1,29 +0,0 @@ -package system; - -import system.collections.generic.IEnumerator; - -class UInt16ArrayEnumerator implements IEnumerator -{ - private var _array:js.html.Uint16Array; - private var _i:Int; - - public function new(array:js.html.Uint16Array) - { - _array = array; - _i = -1; - } - - public var Current(get, never):UInt16; - public function get_Current() : UInt16 return _array[_i]; - public function MoveNext() : Bool - { - if(_i >= _array.length - 1) return false; - _i++; - return true; - - } - public function Reset():Void - { - _i = -1; - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/UInt16Iterator.hx b/Phase/Mscorlib/system/UInt16Iterator.hx deleted file mode 100644 index d77d630dd..000000000 --- a/Phase/Mscorlib/system/UInt16Iterator.hx +++ /dev/null @@ -1,19 +0,0 @@ -package system; - -class UInt16ArrayIterator -{ - private var _array:js.html.Uint16Array; - private var _i:Int; - - public function new(array:js.html.Uint16Array) - { - _array = array; - _i = 0; - } - - public function hasNext() return _i < _array.length; - public function next() - { - return _array[_i++]; - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/UInt32.hx b/Phase/Mscorlib/system/UInt32.hx deleted file mode 100644 index b5c35f397..000000000 --- a/Phase/Mscorlib/system/UInt32.hx +++ /dev/null @@ -1,319 +0,0 @@ -package system; - -abstract UInt32(Int) from Int -{ - public inline function new(i:Int) this = system.Convert.ToUInt32(i); - - public inline function ToHaxeInt(): Int return this; - public inline function ToString() : system.CsString return Std.string(this); - - public inline function ToBoolean_IFormatProvider(provider:IFormatProvider) : system.Boolean return system.Convert.ToBoolean_UInt32(this); - public inline function ToChar_IFormatProvider(provider:IFormatProvider) : system.Char return system.Convert.ToChar_UInt32(this); - public inline function ToSByte_IFormatProvider(provider:IFormatProvider) : system.SByte return system.Convert.ToSByte_UInt32(this); - public inline function ToByte_IFormatProvider(provider:IFormatProvider) : system.Byte return system.Convert.ToByte_UInt32(this); - public inline function ToInt16_IFormatProvider(provider:IFormatProvider) : system.Int16 return system.Convert.ToInt16_UInt32(this); - public inline function ToUInt16_IFormatProvider(provider:IFormatProvider) : system.UInt16 return system.Convert.ToUInt16_UInt32(this); - public inline function ToInt32_IFormatProvider(provider:IFormatProvider) : system.Int32 return system.Convert.ToInt32_UInt32(this); - public inline function ToUInt32_IFormatProvider(provider:IFormatProvider) : system.UInt32 return system.Convert.ToUInt32_UInt32(this); - public inline function ToInt64_IFormatProvider(provider:IFormatProvider) : system.Int64 return system.Convert.ToInt64_UInt32(this); - public inline function ToUInt64_IFormatProvider(provider:IFormatProvider) : system.UInt64 return system.Convert.ToUInt64_UInt32(this); - public inline function ToSingle_IFormatProvider(provider:IFormatProvider) : system.Single return system.Convert.ToSingle_UInt32(this); - public inline function ToDouble_IFormatProvider(provider:IFormatProvider) : system.Double return system.Convert.ToDouble_UInt32(this); - - public function GetHashCode() : system.Int32 return this; - - @:op(-A) public inline function neg() : system.Int64 return -this; - - @:op(~A)public inline function not() : system.UInt32 return ~this; - - @:op(A++)public inline function postinc() : system.UInt32 return this++; - @:op(++A)public inline function preinc() : system.UInt32 return ++this; - - @:op(A--)public inline function postdec() : system.UInt32 return this--; - @:op(--A)public inline function predec() : system.UInt32 return --this; - - @:op(A * B) public static function mul0(lhs : system.UInt32, rhs : system.UInt32) : system.UInt32; - @:op(A * B) public static function mul1(lhs : system.UInt32, rhs : Int) : system.Int64; - @:op(A * B) public static function mul2(lhs : Int, rhs : system.UInt32) : system.Int64; - @:op(A * B) public static function mul3(lhs : system.UInt32, rhs : Float) : system.Double; - @:op(A * B) public static function mul4(lhs : Float, rhs : system.UInt32) : system.Double; - - @:op(A * B) public static function mul5(lhs : system.UInt32, rhs : system.Char) : system.Int32; - @:op(A * B) public static function mul6(lhs : system.UInt32, rhs : system.Byte) : system.Int32; - @:op(A * B) public static function mul7(lhs : system.UInt32, rhs : system.Int16) : system.Int32; - @:op(A * B) public static function mul8(lhs : system.UInt32, rhs : system.Int32) : system.Int32; - @:op(A * B) public static function mul9(lhs : system.UInt32, rhs : system.Int64) : system.Int64; - @:op(A * B) public static function mul10(lhs : system.UInt32, rhs : system.SByte) : system.Int32; - @:op(A * B) public static function mul11(lhs : system.UInt32, rhs : system.UInt16) : system.Int32; - @:op(A * B) public static function mul12(lhs : system.UInt32, rhs : system.UInt32) : system.UInt32; - @:op(A * B) public static function mul13(lhs : system.UInt32, rhs : system.UInt64) : system.UInt64; - @:op(A * B) public static function mul14(lhs : system.UInt32, rhs : system.Single) : system.Single; - @:op(A * B) public static function mul15(lhs : system.UInt32, rhs : system.Double) : system.Double; - - - @:op(A / B) public static inline function div0(lhs : system.UInt32, rhs : system.UInt32) : system.UInt32 return Std.int(lhs.ToHaxeInt() / rhs.ToHaxeInt()); - @:op(A / B) public static inline function div1(lhs : system.UInt32, rhs : Int) : system.Int64 return Std.int(lhs.ToHaxeInt() / rhs); - @:op(A / B) public static inline function div2(lhs : Int, rhs : system.UInt32) : system.Int64 return Std.int(lhs / rhs.ToHaxeInt()); - @:op(A / B) public static function div3(lhs : system.UInt32, rhs : Float) : system.Double; - @:op(A / B) public static function div4(lhs : Float, rhs : system.UInt32) : system.Double; - - @:op(A / B) public static function div5(lhs : system.UInt32, rhs : system.Char) : system.Int32; - @:op(A / B) public static function div6(lhs : system.UInt32, rhs : system.Byte) : system.Int32; - @:op(A / B) public static function div7(lhs : system.UInt32, rhs : system.Int16) : system.Int32; - @:op(A / B) public static function div8(lhs : system.UInt32, rhs : system.Int32) : system.Int32; - @:op(A / B) public static function div9(lhs : system.UInt32, rhs : system.Int64) : system.Int64; - @:op(A / B) public static function div10(lhs : system.UInt32, rhs : system.SByte) : system.Int32; - @:op(A / B) public static function div11(lhs : system.UInt32, rhs : system.UInt16) : system.Int32; - @:op(A / B) public static function div12(lhs : system.UInt32, rhs : system.UInt32) : system.UInt32; - @:op(A / B) public static function div13(lhs : system.UInt32, rhs : system.UInt64) : system.UInt64; - @:op(A / B) public static function div14(lhs : system.UInt32, rhs : system.Single) : system.Single; - @:op(A / B) public static function div15(lhs : system.UInt32, rhs : system.Double) : system.Double; - - - @:op(A % B) public static function mod0(lhs : system.UInt32, rhs : system.UInt32) : system.UInt32; - @:op(A % B) public static function mod1(lhs : system.UInt32, rhs : Int) : system.Int64; - @:op(A % B) public static function mod2(lhs : Int, rhs : system.UInt32) : system.Int64; - @:op(A % B) public static function mod3(lhs : system.UInt32, rhs : Float) : system.Double; - @:op(A % B) public static function mod4(lhs : Float, rhs : system.UInt32) : system.Double; - - @:op(A % B) public static function mod5(lhs : system.UInt32, rhs : system.Char) : system.Int32; - @:op(A % B) public static function mod6(lhs : system.UInt32, rhs : system.Byte) : system.Int32; - @:op(A % B) public static function mod7(lhs : system.UInt32, rhs : system.Int16) : system.Int32; - @:op(A % B) public static function mod8(lhs : system.UInt32, rhs : system.Int32) : system.Int32; - @:op(A % B) public static function mod9(lhs : system.UInt32, rhs : system.Int64) : system.Int64; - @:op(A % B) public static function mod10(lhs : system.UInt32, rhs : system.SByte) : system.Int32; - @:op(A % B) public static function mod11(lhs : system.UInt32, rhs : system.UInt16) : system.Int32; - @:op(A % B) public static function mod12(lhs : system.UInt32, rhs : system.UInt32) : system.UInt32; - @:op(A % B) public static function mod13(lhs : system.UInt32, rhs : system.UInt64) : system.UInt64; - @:op(A % B) public static function mod14(lhs : system.UInt32, rhs : system.Single) : system.Single; - @:op(A % B) public static function mod15(lhs : system.UInt32, rhs : system.Double) : system.Double; - - - @:op(A + B) public static function add0(lhs : system.UInt32, rhs : system.UInt32) : system.UInt32; - @:op(A + B) public static function add1(lhs : system.UInt32, rhs : Int) : system.Int64; - @:op(A + B) public static function add2(lhs : Int, rhs : system.UInt32) : system.Int64; - @:op(A + B) public static function add3(lhs : system.UInt32, rhs : Float) : system.Double; - @:op(A + B) public static function add4(lhs : Float, rhs : system.UInt32) : system.Double; - - @:op(A + B) public static function add5(lhs : system.UInt32, rhs : system.Char) : system.Int32; - @:op(A + B) public static function add6(lhs : system.UInt32, rhs : system.Byte) : system.Int32; - @:op(A + B) public static function add7(lhs : system.UInt32, rhs : system.Int16) : system.Int32; - @:op(A + B) public static function add8(lhs : system.UInt32, rhs : system.Int32) : system.Int32; - @:op(A + B) public static function add9(lhs : system.UInt32, rhs : system.Int64) : system.Int64; - @:op(A + B) public static function add10(lhs : system.UInt32, rhs : system.SByte) : system.Int32; - @:op(A + B) public static function add11(lhs : system.UInt32, rhs : system.UInt16) : system.Int32; - @:op(A + B) public static function add12(lhs : system.UInt32, rhs : system.UInt32) : system.UInt32; - @:op(A + B) public static function add13(lhs : system.UInt32, rhs : system.UInt64) : system.UInt64; - @:op(A + B) public static function add14(lhs : system.UInt32, rhs : system.Single) : system.Single; - @:op(A + B) public static function add15(lhs : system.UInt32, rhs : system.Double) : system.Double; - @:op(A + B) public static inline function add16(lhs : system.UInt32, rhs : system.CsString) : system.CsString return lhs.ToString() + rhs; - @:op(A + B) public static inline function add17(lhs : system.UInt32, rhs : String) : system.CsString return lhs.ToString() + rhs; - - - @:op(A - B) public static function sub0(lhs : system.UInt32, rhs : system.UInt32) : system.UInt32; - @:op(A - B) public static function sub1(lhs : system.UInt32, rhs : Int) : system.Int64; - @:op(A - B) public static function sub2(lhs : Int, rhs : system.UInt32) : system.Int64; - @:op(A - B) public static function sub3(lhs : system.UInt32, rhs : Float) : system.Double; - @:op(A - B) public static function sub4(lhs : Float, rhs : system.UInt32) : system.Double; - - @:op(A - B) public static function sub5(lhs : system.UInt32, rhs : system.Char) : system.Int32; - @:op(A - B) public static function sub6(lhs : system.UInt32, rhs : system.Byte) : system.Int32; - @:op(A - B) public static function sub7(lhs : system.UInt32, rhs : system.Int16) : system.Int32; - @:op(A - B) public static function sub8(lhs : system.UInt32, rhs : system.Int32) : system.Int32; - @:op(A - B) public static function sub9(lhs : system.UInt32, rhs : system.Int64) : system.Int64; - @:op(A - B) public static function sub10(lhs : system.UInt32, rhs : system.SByte) : system.Int32; - @:op(A - B) public static function sub11(lhs : system.UInt32, rhs : system.UInt16) : system.Int32; - @:op(A - B) public static function sub12(lhs : system.UInt32, rhs : system.UInt32) : system.UInt32; - @:op(A - B) public static function sub13(lhs : system.UInt32, rhs : system.UInt64) : system.UInt64; - @:op(A - B) public static function sub14(lhs : system.UInt32, rhs : system.Single) : system.Single; - @:op(A - B) public static function sub15(lhs : system.UInt32, rhs : system.Double) : system.Double; - - - @:op(A << B) public static function shl0(lhs : system.UInt32, rhs : system.UInt32) : system.UInt32; - @:op(A << B) public static function shl1(lhs : system.UInt32, rhs : Int) : system.UInt32; - @:op(A << B) public static function shl2(lhs : Int, rhs : system.UInt32) : system.UInt32; - - @:op(A << B) public static function shl5(lhs : system.UInt32, rhs : system.Char) : system.Int32; - @:op(A << B) public static function shl6(lhs : system.UInt32, rhs : system.Byte) : system.Int32; - @:op(A << B) public static function shl7(lhs : system.UInt32, rhs : system.Int16) : system.Int32; - @:op(A << B) public static function shl8(lhs : system.UInt32, rhs : system.Int32) : system.Int32; - // @:op(A << B) public static function shl9(lhs : system.UInt32, rhs : system.Int64) : system.Int64; - @:op(A << B) public static function shl10(lhs : system.UInt32, rhs : system.SByte) : system.Int32; - @:op(A << B) public static function shl11(lhs : system.UInt32, rhs : system.UInt16) : system.Int32; - // @:op(A << B) public static function shl12(lhs : system.UInt32, rhs : system.UInt32) : system.UInt32; - // @:op(A << B) public static function shl13(lhs : system.UInt32, rhs : system.UInt64) : system.UInt64; - - - @:op(A >> B) public static function shr0(lhs : system.UInt32, rhs : system.UInt32) : system.UInt32; - @:op(A >> B) public static function shr1(lhs : system.UInt32, rhs : Int) : system.UInt32; - @:op(A >> B) public static function shr2(lhs : Int, rhs : system.UInt32) : system.UInt32; - - @:op(A >> B) public static function shr5(lhs : system.UInt32, rhs : system.Char) : system.Int32; - @:op(A >> B) public static function shr6(lhs : system.UInt32, rhs : system.Byte) : system.Int32; - @:op(A >> B) public static function shr7(lhs : system.UInt32, rhs : system.Int16) : system.Int32; - @:op(A >> B) public static function shr8(lhs : system.UInt32, rhs : system.Int32) : system.Int32; - // @:op(A >> B) public static function shr9(lhs : system.UInt32, rhs : system.Int64) : system.Int64; - @:op(A >> B) public static function shr10(lhs : system.UInt32, rhs : system.SByte) : system.Int32; - @:op(A >> B) public static function shr11(lhs : system.UInt32, rhs : system.UInt16) : system.Int32; - // @:op(A >> B) public static function shr12(lhs : system.UInt32, rhs : system.UInt32) : system.UInt32; - // @:op(A >> B) public static function shr13(lhs : system.UInt32, rhs : system.UInt64) : system.UInt64; - - - @:op(A > B) public static function gt0(lhs : system.UInt32, rhs : system.UInt32) : system.Boolean; - @:op(A > B) public static function gt1(lhs : system.UInt32, rhs : Int) : system.Boolean; - @:op(A > B) public static function gt2(lhs : Int, rhs : system.UInt32) : system.Boolean; - @:op(A > B) public static function gt3(lhs : system.UInt32, rhs : Float) : system.Boolean; - @:op(A > B) public static function gt4(lhs : Float, rhs : system.UInt32) : system.Boolean; - - @:op(A > B) public static function gt5(lhs : system.UInt32, rhs : system.Char) : system.Boolean; - @:op(A > B) public static function gt6(lhs : system.UInt32, rhs : system.Byte) : system.Boolean; - @:op(A > B) public static function gt7(lhs : system.UInt32, rhs : system.Int16) : system.Boolean; - @:op(A > B) public static function gt8(lhs : system.UInt32, rhs : system.Int32) : system.Boolean; - @:op(A > B) public static function gt9(lhs : system.UInt32, rhs : system.Int64) : system.Boolean; - @:op(A > B) public static function gt10(lhs : system.UInt32, rhs : system.SByte) : system.Boolean; - @:op(A > B) public static function gt11(lhs : system.UInt32, rhs : system.UInt16) : system.Boolean; - @:op(A > B) public static function gt12(lhs : system.UInt32, rhs : system.UInt32) : system.Boolean; - @:op(A > B) public static function gt13(lhs : system.UInt32, rhs : system.UInt64) : system.Boolean; - @:op(A > B) public static function gt14(lhs : system.UInt32, rhs : system.Single) : system.Boolean; - @:op(A > B) public static function gt15(lhs : system.UInt32, rhs : system.Double) : system.Boolean; - - - @:op(A < B) public static function lt0(lhs : system.UInt32, rhs : system.UInt32) : system.Boolean; - @:op(A < B) public static function lt1(lhs : system.UInt32, rhs : Int) : system.Boolean; - @:op(A < B) public static function lt2(lhs : Int, rhs : system.UInt32) : system.Boolean; - @:op(A < B) public static function lt3(lhs : system.UInt32, rhs : Float) : system.Boolean; - @:op(A < B) public static function lt4(lhs : Float, rhs : system.UInt32) : system.Boolean; - - @:op(A < B) public static function lt5(lhs : system.UInt32, rhs : system.Char) : system.Boolean; - @:op(A < B) public static function lt6(lhs : system.UInt32, rhs : system.Byte) : system.Boolean; - @:op(A < B) public static function lt7(lhs : system.UInt32, rhs : system.Int16) : system.Boolean; - @:op(A < B) public static function lt8(lhs : system.UInt32, rhs : system.Int32) : system.Boolean; - @:op(A < B) public static function lt9(lhs : system.UInt32, rhs : system.Int64) : system.Boolean; - @:op(A < B) public static function lt10(lhs : system.UInt32, rhs : system.SByte) : system.Boolean; - @:op(A < B) public static function lt11(lhs : system.UInt32, rhs : system.UInt16) : system.Boolean; - @:op(A < B) public static function lt12(lhs : system.UInt32, rhs : system.UInt32) : system.Boolean; - @:op(A < B) public static function lt13(lhs : system.UInt32, rhs : system.UInt64) : system.Boolean; - @:op(A < B) public static function lt14(lhs : system.UInt32, rhs : system.Single) : system.Boolean; - @:op(A < B) public static function lt15(lhs : system.UInt32, rhs : system.Double) : system.Boolean; - - - @:op(A >= B) public static function gte0(lhs : system.UInt32, rhs : system.UInt32) : system.Boolean; - @:op(A >= B) public static function gte1(lhs : system.UInt32, rhs : Int) : system.Boolean; - @:op(A >= B) public static function gte2(lhs : Int, rhs : system.UInt32) : system.Boolean; - @:op(A >= B) public static function gte3(lhs : system.UInt32, rhs : Float) : system.Boolean; - @:op(A >= B) public static function gte4(lhs : Float, rhs : system.UInt32) : system.Boolean; - - @:op(A >= B) public static function gte5(lhs : system.UInt32, rhs : system.Char) : system.Boolean; - @:op(A >= B) public static function gte6(lhs : system.UInt32, rhs : system.Byte) : system.Boolean; - @:op(A >= B) public static function gte7(lhs : system.UInt32, rhs : system.Int16) : system.Boolean; - @:op(A >= B) public static function gte8(lhs : system.UInt32, rhs : system.Int32) : system.Boolean; - @:op(A >= B) public static function gte9(lhs : system.UInt32, rhs : system.Int64) : system.Boolean; - @:op(A >= B) public static function gte10(lhs : system.UInt32, rhs : system.SByte) : system.Boolean; - @:op(A >= B) public static function gte11(lhs : system.UInt32, rhs : system.UInt16) : system.Boolean; - @:op(A >= B) public static function gte12(lhs : system.UInt32, rhs : system.UInt32) : system.Boolean; - @:op(A >= B) public static function gte13(lhs : system.UInt32, rhs : system.UInt64) : system.Boolean; - @:op(A >= B) public static function gte14(lhs : system.UInt32, rhs : system.Single) : system.Boolean; - @:op(A >= B) public static function gte15(lhs : system.UInt32, rhs : system.Double) : system.Boolean; - - - @:op(A <= B) public static function lte0(lhs : system.UInt32, rhs : system.UInt32) : system.Boolean; - @:op(A <= B) public static function lte1(lhs : system.UInt32, rhs : Int) : system.Boolean; - @:op(A <= B) public static function lte2(lhs : Int, rhs : system.UInt32) : system.Boolean; - @:op(A <= B) public static function lte3(lhs : system.UInt32, rhs : Float) : system.Boolean; - @:op(A <= B) public static function lte4(lhs : Float, rhs : system.UInt32) : system.Boolean; - - @:op(A <= B) public static function lte5(lhs : system.UInt32, rhs : system.Char) : system.Boolean; - @:op(A <= B) public static function lte6(lhs : system.UInt32, rhs : system.Byte) : system.Boolean; - @:op(A <= B) public static function lte7(lhs : system.UInt32, rhs : system.Int16) : system.Boolean; - @:op(A <= B) public static function lte8(lhs : system.UInt32, rhs : system.Int32) : system.Boolean; - @:op(A <= B) public static function lte9(lhs : system.UInt32, rhs : system.Int64) : system.Boolean; - @:op(A <= B) public static function lte10(lhs : system.UInt32, rhs : system.SByte) : system.Boolean; - @:op(A <= B) public static function lte11(lhs : system.UInt32, rhs : system.UInt16) : system.Boolean; - @:op(A <= B) public static function lte12(lhs : system.UInt32, rhs : system.UInt32) : system.Boolean; - @:op(A <= B) public static function lte13(lhs : system.UInt32, rhs : system.UInt64) : system.Boolean; - @:op(A <= B) public static function lte14(lhs : system.UInt32, rhs : system.Single) : system.Boolean; - @:op(A <= B) public static function lte15(lhs : system.UInt32, rhs : system.Double) : system.Boolean; - - - @:op(A == B) public static function eq0(lhs : system.UInt32, rhs : system.UInt32) : system.Boolean; - @:op(A == B) public static function eq1(lhs : system.UInt32, rhs : Int) : system.Boolean; - @:op(A == B) public static function eq2(lhs : Int, rhs : system.UInt32) : system.Boolean; - @:op(A == B) public static function eq3(lhs : system.UInt32, rhs : Float) : system.Boolean; - @:op(A == B) public static function eq4(lhs : Float, rhs : system.UInt32) : system.Boolean; - - @:op(A == B) public static function eq5(lhs : system.UInt32, rhs : system.Char) : system.Boolean; - @:op(A == B) public static function eq6(lhs : system.UInt32, rhs : system.Byte) : system.Boolean; - @:op(A == B) public static function eq7(lhs : system.UInt32, rhs : system.Int16) : system.Boolean; - @:op(A == B) public static function eq8(lhs : system.UInt32, rhs : system.Int32) : system.Boolean; - @:op(A == B) public static function eq9(lhs : system.UInt32, rhs : system.Int64) : system.Boolean; - @:op(A == B) public static function eq10(lhs : system.UInt32, rhs : system.SByte) : system.Boolean; - @:op(A == B) public static function eq11(lhs : system.UInt32, rhs : system.UInt16) : system.Boolean; - @:op(A == B) public static function eq12(lhs : system.UInt32, rhs : system.UInt32) : system.Boolean; - @:op(A == B) public static function eq13(lhs : system.UInt32, rhs : system.UInt64) : system.Boolean; - @:op(A == B) public static function eq14(lhs : system.UInt32, rhs : system.Single) : system.Boolean; - @:op(A == B) public static function eq15(lhs : system.UInt32, rhs : system.Double) : system.Boolean; - - - @:op(A != B) public static function neq0(lhs : system.UInt32, rhs : system.UInt32) : system.Boolean; - @:op(A != B) public static function neq1(lhs : system.UInt32, rhs : Int) : system.Boolean; - @:op(A != B) public static function neq2(lhs : Int, rhs : system.UInt32) : system.Boolean; - @:op(A != B) public static function neq3(lhs : system.UInt32, rhs : Float) : system.Boolean; - @:op(A != B) public static function neq4(lhs : Float, rhs : system.UInt32) : system.Boolean; - - @:op(A != B) public static function neq5(lhs : system.UInt32, rhs : system.Char) : system.Boolean; - @:op(A != B) public static function neq6(lhs : system.UInt32, rhs : system.Byte) : system.Boolean; - @:op(A != B) public static function neq7(lhs : system.UInt32, rhs : system.Int16) : system.Boolean; - @:op(A != B) public static function neq8(lhs : system.UInt32, rhs : system.Int32) : system.Boolean; - @:op(A != B) public static function neq9(lhs : system.UInt32, rhs : system.Int64) : system.Boolean; - @:op(A != B) public static function neq10(lhs : system.UInt32, rhs : system.SByte) : system.Boolean; - @:op(A != B) public static function neq11(lhs : system.UInt32, rhs : system.UInt16) : system.Boolean; - @:op(A != B) public static function neq12(lhs : system.UInt32, rhs : system.UInt32) : system.Boolean; - @:op(A != B) public static function neq13(lhs : system.UInt32, rhs : system.UInt64) : system.Boolean; - @:op(A != B) public static function neq14(lhs : system.UInt32, rhs : system.Single) : system.Boolean; - @:op(A != B) public static function neq15(lhs : system.UInt32, rhs : system.Double) : system.Boolean; - - @:op(A & B) public static function and0(lhs : system.UInt32, rhs : system.UInt32) : system.UInt32; - @:op(A & B) public static function and1(lhs : system.UInt32, rhs : Int) : system.Int64; - @:op(A & B) public static function and2(lhs : Int, rhs : system.UInt32) : system.Int64; - - @:op(A & B) public static function and5(lhs : system.UInt32, rhs : system.Char) : system.Int32; - @:op(A & B) public static function and6(lhs : system.UInt32, rhs : system.Byte) : system.Int32; - @:op(A & B) public static function and7(lhs : system.UInt32, rhs : system.Int16) : system.Int32; - @:op(A & B) public static function and8(lhs : system.UInt32, rhs : system.Int32) : system.Int32; - @:op(A & B) public static function and9(lhs : system.UInt32, rhs : system.Int64) : system.Int64; - @:op(A & B) public static function and10(lhs : system.UInt32, rhs : system.SByte) : system.Int32; - @:op(A & B) public static function and11(lhs : system.UInt32, rhs : system.UInt16) : system.Int32; - @:op(A & B) public static function and12(lhs : system.UInt32, rhs : system.UInt32) : system.UInt32; - @:op(A & B) public static function and13(lhs : system.UInt32, rhs : system.UInt64) : system.UInt64; - - - @:op(A | B) public static function or0(lhs : system.UInt32, rhs : system.UInt32) : system.UInt32; - @:op(A | B) public static function or1(lhs : system.UInt32, rhs : Int) : system.Int64; - @:op(A | B) public static function or2(lhs : Int, rhs : system.UInt32) : system.Int64; - - @:op(A | B) public static function or5(lhs : system.UInt32, rhs : system.Char) : system.Int32; - @:op(A | B) public static function or6(lhs : system.UInt32, rhs : system.Byte) : system.Int32; - @:op(A | B) public static function or7(lhs : system.UInt32, rhs : system.Int16) : system.Int32; - @:op(A | B) public static function or8(lhs : system.UInt32, rhs : system.Int32) : system.Int32; - @:op(A | B) public static function or9(lhs : system.UInt32, rhs : system.Int64) : system.Int64; - @:op(A | B) public static function or10(lhs : system.UInt32, rhs : system.SByte) : system.Int32; - @:op(A | B) public static function or11(lhs : system.UInt32, rhs : system.UInt16) : system.Int32; - @:op(A | B) public static function or12(lhs : system.UInt32, rhs : system.UInt32) : system.UInt32; - @:op(A | B) public static function or13(lhs : system.UInt32, rhs : system.UInt64) : system.UInt64; - - - @:op(A ^ B) public static function xor0(lhs : system.UInt32, rhs : system.UInt32) : system.UInt32; - @:op(A ^ B) public static function xor1(lhs : Int, rhs : system.UInt32) : system.Int64; - @:op(A ^ B) public static function xor2(lhs : system.UInt32, rhs : Int) : system.Int64; - - @:op(A ^ B) public static function xor5(lhs : system.UInt32, rhs : system.Char) : system.Int32; - @:op(A ^ B) public static function xor6(lhs : system.UInt32, rhs : system.Byte) : system.Int32; - @:op(A ^ B) public static function xor7(lhs : system.UInt32, rhs : system.Int16) : system.Int32; - @:op(A ^ B) public static function xor8(lhs : system.UInt32, rhs : system.Int32) : system.Int32; - @:op(A ^ B) public static function xor9(lhs : system.UInt32, rhs : system.Int64) : system.Int64; - @:op(A ^ B) public static function xor10(lhs : system.UInt32, rhs : system.SByte) : system.Int32; - @:op(A ^ B) public static function xor11(lhs : system.UInt32, rhs : system.UInt16) : system.Int32; - @:op(A ^ B) public static function xor12(lhs : system.UInt32, rhs : system.UInt32) : system.UInt32; - @:op(A ^ B) public static function xor13(lhs : system.UInt32, rhs : system.UInt64) : system.UInt64; - -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/UInt32Array.hx b/Phase/Mscorlib/system/UInt32Array.hx deleted file mode 100644 index e85e80cae..000000000 --- a/Phase/Mscorlib/system/UInt32Array.hx +++ /dev/null @@ -1,21 +0,0 @@ -package system; - -abstract UInt32Array(js.html.Uint32Array) -{ - public inline function new(length:Int32) this = new js.html.Uint32Array(length.ToHaxeInt()); - - @:from public static inline function fromArray(a:Array):UInt32Array return cast new js.html.Uint32Array(untyped a); - @:from public static inline function fromFixedArray(a:FixedArray):UInt32Array return cast new js.html.Uint32Array(untyped a.ToHaxeArray()); - - public var Length(get, never):Int32; - public inline function get_Length() : Int32 return this.length; - - @:op([]) public inline function get(index:Int32):UInt32 return this[index.ToHaxeInt()]; - @:op([]) public inline function set(index:Int32, val:UInt32):UInt32 return this[index.ToHaxeInt()] = val.ToHaxeFloat(); - - public inline function iterator() : Iterator return new UInt32ArrayIterator(this); - - public inline function ToEnumerable() : system.collections.generic.IEnumerable return new UInt32ArrayEnumerable(this); - - public static inline function empty(size:Int32) return new UInt32Array(size); -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/UInt32ArrayEnumerable.hx b/Phase/Mscorlib/system/UInt32ArrayEnumerable.hx deleted file mode 100644 index 8ee89450b..000000000 --- a/Phase/Mscorlib/system/UInt32ArrayEnumerable.hx +++ /dev/null @@ -1,18 +0,0 @@ -package system; - -import system.collections.generic.IEnumerable; -import system.collections.generic.IEnumerator; - -class UInt32ArrayEnumerable implements IEnumerable -{ - private var _array:js.html.Uint32Array; - public function new(array:js.html.Uint32Array) - { - _array= array; - } - - public function GetEnumerator() : IEnumerator - { - return new UInt32ArrayEnumerator(_array); - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/UInt32ArrayEnumerator.hx b/Phase/Mscorlib/system/UInt32ArrayEnumerator.hx deleted file mode 100644 index 0450005ba..000000000 --- a/Phase/Mscorlib/system/UInt32ArrayEnumerator.hx +++ /dev/null @@ -1,29 +0,0 @@ -package system; - -import system.collections.generic.IEnumerator; - -class UInt32ArrayEnumerator implements IEnumerator -{ - private var _array:js.html.Uint32Array; - private var _i:Int; - - public function new(array:js.html.Uint32Array) - { - _array = array; - _i = -1; - } - - public var Current(get, never):UInt32; - public function get_Current() : UInt32 return _array[_i]; - public function MoveNext() : Bool - { - if(_i >= _array.length - 1) return false; - _i++; - return true; - - } - public function Reset():Void - { - _i = -1; - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/UInt32ArrayIterator.hx b/Phase/Mscorlib/system/UInt32ArrayIterator.hx deleted file mode 100644 index 0db0f0f46..000000000 --- a/Phase/Mscorlib/system/UInt32ArrayIterator.hx +++ /dev/null @@ -1,19 +0,0 @@ -package system; - -class UInt32ArrayIterator -{ - private var _array:js.html.Uint32Array; - private var _i:Int; - - public function new(array:js.html.Uint32Array) - { - _array = array; - _i = 0; - } - - public function hasNext() return _i < _array.length; - public function next() : UInt32 - { - return _array[_i++]; - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/UInt64.hx b/Phase/Mscorlib/system/UInt64.hx deleted file mode 100644 index e8ae4820d..000000000 --- a/Phase/Mscorlib/system/UInt64.hx +++ /dev/null @@ -1,286 +0,0 @@ -package system; - -abstract UInt64(Int) from Int -{ - public inline function new(i:Int) this = system.Convert.ToUInt64(i); - - public inline function ToHaxeInt(): Int return this; - public inline function ToString() : system.CsString return Std.string(this); - - public inline function ToBoolean_IFormatProvider(provider:IFormatProvider) : system.Boolean return system.Convert.ToBoolean_UInt64(this); - public inline function ToChar_IFormatProvider(provider:IFormatProvider) : system.Char return system.Convert.ToChar_UInt64(this); - public inline function ToSByte_IFormatProvider(provider:IFormatProvider) : system.SByte return system.Convert.ToSByte_UInt64(this); - public inline function ToByte_IFormatProvider(provider:IFormatProvider) : system.Byte return system.Convert.ToByte_UInt64(this); - public inline function ToInt16_IFormatProvider(provider:IFormatProvider) : system.Int16 return system.Convert.ToInt16_UInt64(this); - public inline function ToUInt16_IFormatProvider(provider:IFormatProvider) : system.UInt16 return system.Convert.ToUInt16_UInt64(this); - public inline function ToInt32_IFormatProvider(provider:IFormatProvider) : system.Int32 return system.Convert.ToInt32_UInt64(this); - public inline function ToUInt32_IFormatProvider(provider:IFormatProvider) : system.UInt32 return system.Convert.ToUInt32_UInt64(this); - public inline function ToInt64_IFormatProvider(provider:IFormatProvider) : system.Int64 return system.Convert.ToInt64_UInt64(this); - public inline function ToUInt64_IFormatProvider(provider:IFormatProvider) : system.UInt64 return system.Convert.ToUInt64_UInt64(this); - public inline function ToSingle_IFormatProvider(provider:IFormatProvider) : system.Single return system.Convert.ToSingle_UInt64(this); - public inline function ToDouble_IFormatProvider(provider:IFormatProvider) : system.Double return system.Convert.ToDouble_UInt64(this); - - public function GetHashCode() : system.Int32 return this; - - @:op(~A)public inline function not() : system.UInt64 return ~this; - - @:op(A++)public inline function postinc() : system.UInt64 return this++; - @:op(++A)public inline function preinc() : system.UInt64 return ++this; - - @:op(A--)public inline function postdec() : system.UInt64 return this--; - @:op(--A)public inline function predec() : system.UInt64 return --this; - - @:op(A * B) public static function mul0(lhs : system.UInt64, rhs : system.UInt64) : system.UInt64; - @:op(A * B) public static function mul1(lhs : system.UInt64, rhs : Float) : system.Double; - @:op(A * B) public static function mul2(lhs : Float, rhs : system.UInt64) : system.Double; - - @:op(A * B) public static function mul5(lhs : system.UInt64, rhs : system.Char) : system.UInt64; - @:op(A * B) public static function mul6(lhs : system.UInt64, rhs : system.Byte) : system.UInt64; - //@:op(A * B) public static function mul7(lhs : system.UInt64, rhs : system.Int16) : system.UInt64; - //@:op(A * B) public static function mul8(lhs : system.UInt64, rhs : system.Int32) : system.UInt64; - //@:op(A * B) public static function mul9(lhs : system.UInt64, rhs : system.Int64) : system.UInt64; - //@:op(A * B) public static function mul10(lhs : system.UInt64, rhs : system.SByte) : system.UInt64; - @:op(A * B) public static function mul11(lhs : system.UInt64, rhs : system.UInt16) : system.UInt64; - @:op(A * B) public static function mul12(lhs : system.UInt64, rhs : system.UInt32) : system.UInt64; - @:op(A * B) public static function mul13(lhs : system.UInt64, rhs : system.UInt64) : system.UInt64; - @:op(A * B) public static function mul14(lhs : system.UInt64, rhs : system.Single) : system.Single; - @:op(A * B) public static function mul15(lhs : system.UInt64, rhs : system.Double) : system.Double; - - - @:op(A / B) public static inline function div0(lhs : system.UInt64, rhs : system.UInt64) : system.UInt64 return Std.int(lhs.ToHaxeInt() / rhs.ToHaxeInt()); - @:op(A / B) public static function div1(lhs : system.UInt64, rhs : Float) : system.Double; - @:op(A / B) public static function div2(lhs : Float, rhs : system.UInt64) : system.Double; - - @:op(A / B) public static function div5(lhs : system.UInt64, rhs : system.Char) : system.UInt64; - @:op(A / B) public static function div6(lhs : system.UInt64, rhs : system.Byte) : system.UInt64; - //@:op(A / B) public static function div7(lhs : system.UInt64, rhs : system.Int16) : system.UInt64; - //@:op(A / B) public static function div8(lhs : system.UInt64, rhs : system.Int32) : system.UInt64; - //@:op(A / B) public static function div9(lhs : system.UInt64, rhs : system.Int64) : system.UInt64; - //@:op(A / B) public static function div10(lhs : system.UInt64, rhs : system.SByte) : system.UInt64; - @:op(A / B) public static function div11(lhs : system.UInt64, rhs : system.UInt16) : system.UInt64; - @:op(A / B) public static function div12(lhs : system.UInt64, rhs : system.UInt32) : system.UInt64; - @:op(A / B) public static function div13(lhs : system.UInt64, rhs : system.UInt64) : system.UInt64; - @:op(A / B) public static function div14(lhs : system.UInt64, rhs : system.Single) : system.Single; - @:op(A / B) public static function div15(lhs : system.UInt64, rhs : system.Double) : system.Double; - - - @:op(A % B) public static function mod0(lhs : system.UInt64, rhs : system.UInt64) : system.UInt64; - @:op(A % B) public static function mod1(lhs : system.UInt64, rhs : Float) : system.Double; - @:op(A % B) public static function mod2(lhs : Float, rhs : system.UInt64) : system.Double; - - @:op(A % B) public static function mod5(lhs : system.UInt64, rhs : system.Char) : system.UInt64; - @:op(A % B) public static function mod6(lhs : system.UInt64, rhs : system.Byte) : system.UInt64; - //@:op(A % B) public static function mod7(lhs : system.UInt64, rhs : system.Int16) : system.UInt64; - //@:op(A % B) public static function mod8(lhs : system.UInt64, rhs : system.Int32) : system.UInt64; - //@:op(A % B) public static function mod9(lhs : system.UInt64, rhs : system.Int64) : system.UInt64; - //@:op(A % B) public static function mod10(lhs : system.UInt64, rhs : system.SByte) : system.UInt64; - @:op(A % B) public static function mod11(lhs : system.UInt64, rhs : system.UInt16) : system.UInt64; - @:op(A % B) public static function mod12(lhs : system.UInt64, rhs : system.UInt32) : system.UInt64; - @:op(A % B) public static function mod13(lhs : system.UInt64, rhs : system.UInt64) : system.UInt64; - @:op(A % B) public static function mod14(lhs : system.UInt64, rhs : system.Single) : system.Single; - @:op(A % B) public static function mod15(lhs : system.UInt64, rhs : system.Double) : system.Double; - - - @:op(A + B) public static function add0(lhs : system.UInt64, rhs : system.UInt64) : system.UInt64; - @:op(A + B) public static function add1(lhs : system.UInt64, rhs : Float) : system.Double; - @:op(A + B) public static function add2(lhs : Float, rhs : system.UInt64) : system.Double; - - @:op(A + B) public static function add5(lhs : system.UInt64, rhs : system.Char) : system.UInt64; - @:op(A + B) public static function add6(lhs : system.UInt64, rhs : system.Byte) : system.UInt64; - //@:op(A + B) public static function add7(lhs : system.UInt64, rhs : system.Int16) : system.UInt64; - //@:op(A + B) public static function add8(lhs : system.UInt64, rhs : system.Int32) : system.UInt64; - //@:op(A + B) public static function add9(lhs : system.UInt64, rhs : system.Int64) : system.UInt64; - //@:op(A + B) public static function add10(lhs : system.UInt64, rhs : system.SByte) : system.UInt64; - @:op(A + B) public static function add11(lhs : system.UInt64, rhs : system.UInt16) : system.UInt64; - @:op(A + B) public static function add12(lhs : system.UInt64, rhs : system.UInt32) : system.UInt64; - @:op(A + B) public static function add13(lhs : system.UInt64, rhs : system.UInt64) : system.UInt64; - @:op(A + B) public static function add14(lhs : system.UInt64, rhs : system.Single) : system.Single; - @:op(A + B) public static function add15(lhs : system.UInt64, rhs : system.Double) : system.Double; - @:op(A + B) public static inline function add16(lhs : system.UInt64, rhs : system.CsString) : system.CsString return lhs.ToString() + rhs; - @:op(A + B) public static inline function add17(lhs : system.UInt64, rhs : String) : system.CsString return lhs.ToString() + rhs; - - - @:op(A - B) public static function sub0(lhs : system.UInt64, rhs : system.UInt64) : system.UInt64; - @:op(A - B) public static function sub1(lhs : system.UInt64, rhs : Float) : system.Double; - @:op(A - B) public static function sub2(lhs : Float, rhs : system.UInt64) : system.Double; - - @:op(A - B) public static function sub5(lhs : system.UInt64, rhs : system.Char) : system.UInt64; - @:op(A - B) public static function sub6(lhs : system.UInt64, rhs : system.Byte) : system.UInt64; - //@:op(A - B) public static function sub7(lhs : system.UInt64, rhs : system.Int16) : system.UInt64; - //@:op(A - B) public static function sub8(lhs : system.UInt64, rhs : system.Int32) : system.UInt64; - //@:op(A - B) public static function sub9(lhs : system.UInt64, rhs : system.Int64) : system.UInt64; - //@:op(A - B) public static function sub10(lhs : system.UInt64, rhs : system.SByte) : system.UInt64; - @:op(A - B) public static function sub11(lhs : system.UInt64, rhs : system.UInt16) : system.UInt64; - @:op(A - B) public static function sub12(lhs : system.UInt64, rhs : system.UInt32) : system.UInt64; - @:op(A - B) public static function sub13(lhs : system.UInt64, rhs : system.UInt64) : system.UInt64; - @:op(A - B) public static function sub14(lhs : system.UInt64, rhs : system.Single) : system.Single; - @:op(A - B) public static function sub15(lhs : system.UInt64, rhs : system.Double) : system.Double; - - - @:op(A << B) public static function shl0(lhs : system.UInt64, rhs : system.UInt64) : system.UInt64; - @:op(A << B) public static function shl1(lhs : system.UInt64, rhs : Int) : system.UInt64; - @:op(A << B) public static function shl2(lhs : Int, rhs : system.UInt64) : system.UInt64; - - @:op(A << B) public static function shl5(lhs : system.UInt64, rhs : system.Char) : system.UInt64; - @:op(A << B) public static function shl6(lhs : system.UInt64, rhs : system.Byte) : system.UInt64; - @:op(A << B) public static function shl7(lhs : system.UInt64, rhs : system.Int16) : system.UInt64; - @:op(A << B) public static function shl8(lhs : system.UInt64, rhs : system.Int32) : system.UInt64; - // @:op(A << B) public static function shl9(lhs : system.UInt64, rhs : system.Int64) : system.UInt64; - @:op(A << B) public static function shl10(lhs : system.UInt64, rhs : system.SByte) : system.UInt64; - @:op(A << B) public static function shl11(lhs : system.UInt64, rhs : system.UInt16) : system.UInt64; - // @:op(A << B) public static function shl12(lhs : system.UInt64, rhs : system.UInt32) : system.UInt64; - // @:op(A << B) public static function shl13(lhs : system.UInt64, rhs : system.UInt64) : system.UInt64; - - - @:op(A >> B) public static function shr0(lhs : system.UInt64, rhs : system.UInt64) : system.UInt64; - @:op(A >> B) public static function shr1(lhs : system.UInt64, rhs : Int) : system.UInt64; - @:op(A >> B) public static function shr2(lhs : Int, rhs : system.UInt64) : system.UInt64; - - @:op(A >> B) public static function shr5(lhs : system.UInt64, rhs : system.Char) : system.UInt64; - @:op(A >> B) public static function shr6(lhs : system.UInt64, rhs : system.Byte) : system.UInt64; - @:op(A >> B) public static function shr7(lhs : system.UInt64, rhs : system.Int16) : system.UInt64; - @:op(A >> B) public static function shr8(lhs : system.UInt64, rhs : system.Int32) : system.UInt64; - // @:op(A >> B) public static function shr9(lhs : system.UInt64, rhs : system.Int64) : system.UInt64; - @:op(A >> B) public static function shr10(lhs : system.UInt64, rhs : system.SByte) : system.UInt64; - @:op(A >> B) public static function shr11(lhs : system.UInt64, rhs : system.UInt16) : system.UInt64; - // @:op(A >> B) public static function shr12(lhs : system.UInt64, rhs : system.UInt32) : system.UInt64; - // @:op(A >> B) public static function shr13(lhs : system.UInt64, rhs : system.UInt64) : system.UInt64; - - - @:op(A > B) public static function gt0(lhs : system.UInt64, rhs : system.UInt64) : system.Boolean; - @:op(A > B) public static function gt1(lhs : system.UInt64, rhs : Float) : system.Boolean; - @:op(A > B) public static function gt2(lhs : Float, rhs : system.UInt64) : system.Boolean; - - @:op(A > B) public static function gt5(lhs : system.UInt64, rhs : system.Char) : system.Boolean; - @:op(A > B) public static function gt6(lhs : system.UInt64, rhs : system.Byte) : system.Boolean; - //@:op(A > B) public static function gt7(lhs : system.UInt64, rhs : system.Int16) : system.Boolean; - //@:op(A > B) public static function gt8(lhs : system.UInt64, rhs : system.Int32) : system.Boolean; - //@:op(A > B) public static function gt9(lhs : system.UInt64, rhs : system.Int64) : system.Boolean; - //@:op(A > B) public static function gt10(lhs : system.UInt64, rhs : system.SByte) : system.Boolean; - @:op(A > B) public static function gt11(lhs : system.UInt64, rhs : system.UInt16) : system.Boolean; - @:op(A > B) public static function gt12(lhs : system.UInt64, rhs : system.UInt32) : system.Boolean; - @:op(A > B) public static function gt13(lhs : system.UInt64, rhs : system.UInt64) : system.Boolean; - @:op(A > B) public static function gt14(lhs : system.UInt64, rhs : system.Single) : system.Boolean; - @:op(A > B) public static function gt15(lhs : system.UInt64, rhs : system.Double) : system.Boolean; - - - @:op(A < B) public static function lt0(lhs : system.UInt64, rhs : system.UInt64) : system.Boolean; - @:op(A < B) public static function lt1(lhs : system.UInt64, rhs : Float) : system.Boolean; - @:op(A < B) public static function lt2(lhs : Float, rhs : system.UInt64) : system.Boolean; - - @:op(A < B) public static function lt5(lhs : system.UInt64, rhs : system.Char) : system.Boolean; - @:op(A < B) public static function lt6(lhs : system.UInt64, rhs : system.Byte) : system.Boolean; - //@:op(A < B) public static function lt7(lhs : system.UInt64, rhs : system.Int16) : system.Boolean; - //@:op(A < B) public static function lt8(lhs : system.UInt64, rhs : system.Int32) : system.Boolean; - //@:op(A < B) public static function lt9(lhs : system.UInt64, rhs : system.Int64) : system.Boolean; - //@:op(A < B) public static function lt10(lhs : system.UInt64, rhs : system.SByte) : system.Boolean; - @:op(A < B) public static function lt11(lhs : system.UInt64, rhs : system.UInt16) : system.Boolean; - @:op(A < B) public static function lt12(lhs : system.UInt64, rhs : system.UInt32) : system.Boolean; - @:op(A < B) public static function lt13(lhs : system.UInt64, rhs : system.UInt64) : system.Boolean; - @:op(A < B) public static function lt14(lhs : system.UInt64, rhs : system.Single) : system.Boolean; - @:op(A < B) public static function lt15(lhs : system.UInt64, rhs : system.Double) : system.Boolean; - - - @:op(A >= B) public static function gte0(lhs : system.UInt64, rhs : system.UInt64) : system.Boolean; - @:op(A >= B) public static function gte1(lhs : system.UInt64, rhs : Float) : system.Boolean; - @:op(A >= B) public static function gte2(lhs : Float, rhs : system.UInt64) : system.Boolean; - - @:op(A >= B) public static function gte5(lhs : system.UInt64, rhs : system.Char) : system.Boolean; - @:op(A >= B) public static function gte6(lhs : system.UInt64, rhs : system.Byte) : system.Boolean; - //@:op(A >= B) public static function gte7(lhs : system.UInt64, rhs : system.Int16) : system.Boolean; - //@:op(A >= B) public static function gte8(lhs : system.UInt64, rhs : system.Int32) : system.Boolean; - //@:op(A >= B) public static function gte9(lhs : system.UInt64, rhs : system.Int64) : system.Boolean; - //@:op(A >= B) public static function gte10(lhs : system.UInt64, rhs : system.SByte) : system.Boolean; - @:op(A >= B) public static function gte11(lhs : system.UInt64, rhs : system.UInt16) : system.Boolean; - @:op(A >= B) public static function gte12(lhs : system.UInt64, rhs : system.UInt32) : system.Boolean; - @:op(A >= B) public static function gte13(lhs : system.UInt64, rhs : system.UInt64) : system.Boolean; - @:op(A >= B) public static function gte14(lhs : system.UInt64, rhs : system.Single) : system.Boolean; - @:op(A >= B) public static function gte15(lhs : system.UInt64, rhs : system.Double) : system.Boolean; - - - @:op(A <= B) public static function lte0(lhs : system.UInt64, rhs : system.UInt64) : system.Boolean; - @:op(A <= B) public static function lte1(lhs : system.UInt64, rhs : Float) : system.Boolean; - @:op(A <= B) public static function lte2(lhs : Float, rhs : system.UInt64) : system.Boolean; - - @:op(A <= B) public static function lte5(lhs : system.UInt64, rhs : system.Char) : system.Boolean; - @:op(A <= B) public static function lte6(lhs : system.UInt64, rhs : system.Byte) : system.Boolean; - //@:op(A <= B) public static function lte7(lhs : system.UInt64, rhs : system.Int16) : system.Boolean; - //@:op(A <= B) public static function lte8(lhs : system.UInt64, rhs : system.Int32) : system.Boolean; - //@:op(A <= B) public static function lte9(lhs : system.UInt64, rhs : system.Int64) : system.Boolean; - //@:op(A <= B) public static function lte10(lhs : system.UInt64, rhs : system.SByte) : system.Boolean; - @:op(A <= B) public static function lte11(lhs : system.UInt64, rhs : system.UInt16) : system.Boolean; - @:op(A <= B) public static function lte12(lhs : system.UInt64, rhs : system.UInt32) : system.Boolean; - @:op(A <= B) public static function lte13(lhs : system.UInt64, rhs : system.UInt64) : system.Boolean; - @:op(A <= B) public static function lte14(lhs : system.UInt64, rhs : system.Single) : system.Boolean; - @:op(A <= B) public static function lte15(lhs : system.UInt64, rhs : system.Double) : system.Boolean; - - @:op(A == B) public static function eq0(lhs : system.UInt64, rhs : system.UInt64) : system.Boolean; - @:op(A == B) public static function eq1(lhs : system.UInt64, rhs : Float) : system.Boolean; - @:op(A == B) public static function eq2(lhs : Float, rhs : system.UInt64) : system.Boolean; - - @:op(A == B) public static function eq5(lhs : system.UInt64, rhs : system.Char) : system.Boolean; - @:op(A == B) public static function eq6(lhs : system.UInt64, rhs : system.Byte) : system.Boolean; - //@:op(A == B) public static function eq7(lhs : system.UInt64, rhs : system.Int16) : system.Boolean; - //@:op(A == B) public static function eq8(lhs : system.UInt64, rhs : system.Int32) : system.Boolean; - //@:op(A == B) public static function eq9(lhs : system.UInt64, rhs : system.Int64) : system.Boolean; - //@:op(A == B) public static function eq10(lhs : system.UInt64, rhs : system.SByte) : system.Boolean; - @:op(A == B) public static function eq11(lhs : system.UInt64, rhs : system.UInt16) : system.Boolean; - @:op(A == B) public static function eq12(lhs : system.UInt64, rhs : system.UInt32) : system.Boolean; - @:op(A == B) public static function eq13(lhs : system.UInt64, rhs : system.UInt64) : system.Boolean; - @:op(A == B) public static function eq14(lhs : system.UInt64, rhs : system.Single) : system.Boolean; - @:op(A == B) public static function eq15(lhs : system.UInt64, rhs : system.Double) : system.Boolean; - - @:op(A != B) public static function neq0(lhs : system.UInt64, rhs : system.UInt64) : system.Boolean; - @:op(A != B) public static function neq1(lhs : system.UInt64, rhs : Float) : system.Boolean; - @:op(A != B) public static function neq2(lhs : Float, rhs : system.UInt64) : system.Boolean; - - @:op(A != B) public static function neq5(lhs : system.UInt64, rhs : system.Char) : system.Boolean; - @:op(A != B) public static function neq6(lhs : system.UInt64, rhs : system.Byte) : system.Boolean; - // @:op(A != B) public static function neq7(lhs : system.UInt64, rhs : system.Int16) : system.Boolean; - // @:op(A != B) public static function neq8(lhs : system.UInt64, rhs : system.Int32) : system.Boolean; - // @:op(A != B) public static function neq9(lhs : system.UInt64, rhs : system.Int64) : system.Boolean; - // @:op(A != B) public static function neq10(lhs : system.UInt64, rhs : system.SByte) : system.Boolean; - @:op(A != B) public static function neq11(lhs : system.UInt64, rhs : system.UInt16) : system.Boolean; - @:op(A != B) public static function neq12(lhs : system.UInt64, rhs : system.UInt32) : system.Boolean; - @:op(A != B) public static function neq13(lhs : system.UInt64, rhs : system.UInt64) : system.Boolean; - @:op(A != B) public static function neq14(lhs : system.UInt64, rhs : system.Single) : system.Boolean; - @:op(A != B) public static function neq15(lhs : system.UInt64, rhs : system.Double) : system.Boolean; - - - @:op(A & B) public static function and0(lhs : system.UInt64, rhs : system.UInt64) : system.UInt64; - - @:op(A & B) public static function and5(lhs : system.UInt64, rhs : system.Char) : system.UInt64; - @:op(A & B) public static function and6(lhs : system.UInt64, rhs : system.Byte) : system.UInt64; - //@:op(A & B) public static function and7(lhs : system.UInt64, rhs : system.Int16) : system.UInt64; - //@:op(A & B) public static function and8(lhs : system.UInt64, rhs : system.Int32) : system.UInt64; - //@:op(A & B) public static function and9(lhs : system.UInt64, rhs : system.Int64) : system.UInt64; - //@:op(A & B) public static function and10(lhs : system.UInt64, rhs : system.SByte) : system.UInt64; - @:op(A & B) public static function and11(lhs : system.UInt64, rhs : system.UInt16) : system.UInt64; - @:op(A & B) public static function and12(lhs : system.UInt64, rhs : system.UInt32) : system.UInt64; - @:op(A & B) public static function and13(lhs : system.UInt64, rhs : system.UInt64) : system.UInt64; - - - @:op(A | B) public static function or0(lhs : system.UInt64, rhs : system.UInt64) : system.UInt64; - - @:op(A | B) public static function or5(lhs : system.UInt64, rhs : system.Char) : system.UInt64; - @:op(A | B) public static function or6(lhs : system.UInt64, rhs : system.Byte) : system.UInt64; - @:op(A | B) public static function or7(lhs : system.UInt64, rhs : system.Int16) : system.UInt64; - //@:op(A | B) public static function or8(lhs : system.UInt64, rhs : system.Int32) : system.UInt64; - //@:op(A | B) public static function or9(lhs : system.UInt64, rhs : system.Int64) : system.UInt64; - //@:op(A | B) public static function or10(lhs : system.UInt64, rhs : system.SByte) : system.UInt64; - //@:op(A | B) public static function or11(lhs : system.UInt64, rhs : system.UInt16) : system.UInt64; - @:op(A | B) public static function or12(lhs : system.UInt64, rhs : system.UInt32) : system.UInt64; - @:op(A | B) public static function or13(lhs : system.UInt64, rhs : system.UInt64) : system.UInt64; - - @:op(A ^ B) public static function xor0(lhs : system.UInt64, rhs : system.UInt64) : system.UInt64; - - @:op(A ^ B) public static function xor5(lhs : system.UInt64, rhs : system.Char) : system.UInt64; - @:op(A ^ B) public static function xor6(lhs : system.UInt64, rhs : system.Byte) : system.UInt64; - //@:op(A ^ B) public static function xor7(lhs : system.UInt64, rhs : system.Int16) : system.UInt64; - //@:op(A ^ B) public static function xor8(lhs : system.UInt64, rhs : system.Int32) : system.UInt64; - //@:op(A ^ B) public static function xor9(lhs : system.UInt64, rhs : system.Int64) : system.UInt64; - //@:op(A ^ B) public static function xor10(lhs : system.UInt64, rhs : system.SByte) : system.UInt64; - @:op(A ^ B) public static function xor11(lhs : system.UInt64, rhs : system.UInt16) : system.UInt64; - @:op(A ^ B) public static function xor12(lhs : system.UInt64, rhs : system.UInt32) : system.UInt64; - @:op(A ^ B) public static function xor13(lhs : system.UInt64, rhs : system.UInt64) : system.UInt64; -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/UInt64Array.hx b/Phase/Mscorlib/system/UInt64Array.hx deleted file mode 100644 index 54dc6e328..000000000 --- a/Phase/Mscorlib/system/UInt64Array.hx +++ /dev/null @@ -1,21 +0,0 @@ -package system; - -abstract UInt64Array(js.html.UInt32Array) -{ - public inline function new(length:Int32) this = new js.html.UInt32Array(length.ToHaxeInt()); - - @:from public static inline function fromArray(a:Array):UInt64Array return cast new js.html.UInt32Array(untyped a); - @:from public static inline function fromFixedArray(a:FixedArray):UInt64Array return cast new js.html.UInt32Array(untyped a.ToHaxeArray()); - - public var Length(get, never):Int32; - public inline function get_Length() : Int32 return this.length; - - @:op([]) public inline function get(index:Int32):UInt64 return this[index.ToHaxeInt()]; - @:op([]) public inline function set(index:Int32, val:UInt64):UInt64 return this[index.ToHaxeInt()] = val.ToHaxeFloat(); - - public inline function iterator() : Iterator return new UInt64ArrayIterator(this); - - public inline function ToEnumerable() : system.collections.generic.IEnumerable return new UInt64ArrayEnumerable(this); - - public static inline function empty(size:Int32) return new UInt64Array(size); -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/UInt64ArrayEnumerable.hx b/Phase/Mscorlib/system/UInt64ArrayEnumerable.hx deleted file mode 100644 index 4d21a0cf0..000000000 --- a/Phase/Mscorlib/system/UInt64ArrayEnumerable.hx +++ /dev/null @@ -1,18 +0,0 @@ -package system; - -import system.collections.generic.IEnumerable; -import system.collections.generic.IEnumerator; - -class UInt64ArrayEnumerable implements IEnumerable -{ - private var _array:js.html.Uint32Array; - public function new(array:js.html.Uint32Array) - { - _array= array; - } - - public function GetEnumerator() : IEnumerator - { - return new UInt64ArrayEnumerator(_array); - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/UInt64ArrayEnumerator.hx b/Phase/Mscorlib/system/UInt64ArrayEnumerator.hx deleted file mode 100644 index ff5867887..000000000 --- a/Phase/Mscorlib/system/UInt64ArrayEnumerator.hx +++ /dev/null @@ -1,29 +0,0 @@ -package system; - -import system.collections.generic.IEnumerator; - -class UInt64ArrayEnumerator implements IEnumerator -{ - private var _array:js.html.Uint32Array; - private var _i:Int; - - public function new(array:js.html.Uint32Array) - { - _array = array; - _i = -1; - } - - public var Current(get, never):UInt64; - public function get_Current() : UInt64 return _array[_i]; - public function MoveNext() : Bool - { - if(_i >= _array.length - 1) return false; - _i++; - return true; - - } - public function Reset():Void - { - _i = -1; - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/UInt64ArrayIterator.hx b/Phase/Mscorlib/system/UInt64ArrayIterator.hx deleted file mode 100644 index 274acd785..000000000 --- a/Phase/Mscorlib/system/UInt64ArrayIterator.hx +++ /dev/null @@ -1,19 +0,0 @@ -package system; - -class UInt64ArrayIterator -{ - private var _array:js.html.Uint32Array; - private var _i:Int; - - public function new(array:js.html.Uint32Array) - { - _array = array; - _i = 0; - } - - public function hasNext() return _i < _array.length; - public function next() : UInt64 - { - return _array[_i++]; - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/collections/generic/IEnumerable.hx b/Phase/Mscorlib/system/collections/generic/IEnumerable.hx deleted file mode 100644 index fa1efefb1..000000000 --- a/Phase/Mscorlib/system/collections/generic/IEnumerable.hx +++ /dev/null @@ -1,6 +0,0 @@ -package system.collections.generic; - -interface IEnumerable -{ - function GetEnumerator() : IEnumerator; -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/collections/generic/IEnumerator.hx b/Phase/Mscorlib/system/collections/generic/IEnumerator.hx deleted file mode 100644 index 36e1edde6..000000000 --- a/Phase/Mscorlib/system/collections/generic/IEnumerator.hx +++ /dev/null @@ -1,8 +0,0 @@ -package system.collections.generic; - -interface IEnumerator -{ - var Current(get, never):T; - function MoveNext() : Bool; - function Reset():Void; -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/collections/generic/IterableEnumerable.hx b/Phase/Mscorlib/system/collections/generic/IterableEnumerable.hx deleted file mode 100644 index 8fc24d60a..000000000 --- a/Phase/Mscorlib/system/collections/generic/IterableEnumerable.hx +++ /dev/null @@ -1,15 +0,0 @@ -package system.collections.generic; - -class IterableEnumerable implements IEnumerable -{ - private var _iterable:Iterable; - public function new(i:Iterable) - { - _iterable= i; - } - - public function GetEnumerator() : IEnumerator - { - return new IteratorEnumerator(_iterable.iterator()); - } -} \ No newline at end of file diff --git a/Phase/Mscorlib/system/collections/generic/IteratorEnumerator.hx b/Phase/Mscorlib/system/collections/generic/IteratorEnumerator.hx deleted file mode 100644 index 3fdd1c509..000000000 --- a/Phase/Mscorlib/system/collections/generic/IteratorEnumerator.hx +++ /dev/null @@ -1,29 +0,0 @@ -package system.collections.generic; - -class IteratorEnumerator implements IEnumerator -{ - private var _it:Iterator; - private var _current:T; - - public function new(i:Iterator) - { - _it = i; - } - - public var Current(get, never):T; - public function get_Current() : T return _current; - public function MoveNext() : Bool - { - if(_it.hasNext()) - { - _current = _it.next(); - return true; - } - return false; - - } - public function Reset():Void - { - throw "not supported"; - } -} \ No newline at end of file diff --git a/README.md b/README.md index b5390a280..e3375c842 100644 --- a/README.md +++ b/README.md @@ -1,45 +1,53 @@ # AlphaTab -[![Official Site](https://img.shields.io/badge/site-alphatab.net-blue.svg)](https://alphatab.net) -[![License LGPLv3](https://img.shields.io/badge/license-LGPLv3-green.svg)](http://www.gnu.org/licenses/lgpl-3.0.html) +[![Official Site](https://img.shields.io/badge/site-alphatab.net-blue.svg)](https://alphatab.net) +[![License MPL-2.0](https://img.shields.io/badge/license-MPL--2.0-green.svg)](https://www.mozilla.org/en-US/MPL/2.0/) [![Twitter](https://img.shields.io/badge/twitter-alphaTabMusic-blue.svg)](https://twitter.com/alphaTabMusic) [![Facebook](https://img.shields.io/badge/facebook-alphaTabMusic-blue.svg)](https://facebook.com/alphaTabMusic) alphaTab is a cross platform music notation and guitar tablature rendering library. You can use alphaTab within your own website or application to load and display music sheets from data sources like Guitar Pro or the built in markup language named alphaTex. -![alphaTab](Images/banner.png?raw=true "alphaTab") +![alphaTab](img/banner.png?raw=true "alphaTab") ## Build Status -  | `master` | `develop` ---- | --- | --- -**Build** | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/github/CoderLine/AlphaTab?branch=master&svg=true)](https://ci.appveyor.com/project/Danielku15/alphaTab/branch/master) | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/github/CoderLine/AlphaTab?branch=develop&svg=true)](https://ci.appveyor.com/project/Danielku15/alphaTab/branch/develop) -**Documentation** | [![Documentation](https://img.shields.io/badge/docs-master-brightgreen.svg)](https://docs.alphatab.net/master) | [![Documentation](https://img.shields.io/badge/docs-develop-brightgreen.svg)](https://docs.alphatab.net/develop) +> Until the official 1.0 release we recommend to use pre release versions based on the `develop` branch. -# Features -alphaTab mostly focuses on web based platforms allowing music notation to be embedded into websites and browser based apps but is designed to be used also from .net based platforms like Windows, UWP and Xamarin. +  |   +--- | --- +**Build** | ![Build](https://github.com/CoderLine/alphaTab/workflows/Build/badge.svg?branch=develop) +**Documentation** | [![Documentation](https://img.shields.io/badge/docs-master-brightgreen.svg)](https://www2.alphatab.net/) + +# Downloads + +The latest binaries based are available for download at either npmjs.org or NuGet.org. +Use the links below to grab the binaries from the latest builds. Please do not use the GitHub releases yet, they are not maintained. We recommend using package managers to pull the files to your projects. -Before reading further you might simply jump to our demos: +> Until the official 1.0 release we recommend to use `develop` branch which are available as "pre-release" versions on the package registries. -> [Feature Demo](https://docs.alphatab.net/master/features/) - *Take a look at all visual elements that alphaTab can render* -> -> [Player Demo](https://docs.alphatab.net/master/assets/files/player.html) - *By integrating alphaSynth the you get a web based music sheet player* -> -> [alphaTex Introduction](https://docs.alphatab.net/master/alphatex/) - *Learn about alphaTex, the built-in markup language for writing music notation* +  |   +--- | --- | +**JavaScript** | [NPM](https://www.npmjs.com/package/@coderline/alphatab) +**.net** | [NuGet](https://www.nuget.org/profiles/CoderLine) -alphaTab can load music notation from various sources like Guitar Pro 3-5, Guitar Pro 6, AlphaTex and MusicXML (experimental) and render them into beautiful music sheets right within your browser (or application). Using [alphaSynth](http://github.com/CoderLine/alphaSynth) the music sheets can also be played in your browser without the need of any plugins (HTML5 Web Audio if available, fallback to flash) . +# Features +alphaTab mostly focuses on web based platforms allowing music notation to be embedded into websites and browser based apps but is designed to be used also from .net based platforms like Windows, UWP and Xamarin. + +alphaTab can load music notation from various sources like Guitar Pro 3-7, AlphaTex and MusicXML (experimental) and render them into beautiful music sheets right within your browser (or application). Using a built in midi synthesizer named alphaSynth the music sheets can also be played in your browser. * load GuitarPro 3-5, GuitarPro 6, AlphaTex or MusicXML (experimental) -* render as SVG, HTML5 canvas, GDI+,... -* display single or multiple instruments as standard music notation and guitar tablatures consisting of song information, repeats, alternate endings, guitar tunints, clefs, key signatures, time signatures, notes, rests, accidentals, drum tabs, piano grand staff, tied notes, grace notes, dead notes, ghost notes, markers, tempos, lyrics, chords, vibratos, dynamics, tap/slap/pop, fade-in, let-ring, palm-mute, string bends, whammy bar, tremolo picking, strokes, slides, trills, pick strokes, tuplets, fingering, triplet feels,... -* adapt to your responsive design by dynamic resizing -* play the music sheet via HTML5 Web Audio API and Flash +* render as SVG, HTML5 canvas, GDI+,... +* display single or multiple instruments as standard music notation and guitar tablatures consisting of song information, repeats, alternate endings, guitar tunints, clefs, key signatures, time signatures, notes, rests, accidentals, drum tabs, piano grand staff, tied notes, grace notes, dead notes, ghost notes, markers, tempos, lyrics, chords, vibratos, dynamics, tap/slap/pop, fade-in, let-ring, palm-mute, string bends, whammy bar, tremolo picking, strokes, slides, trills, pick strokes, tuplets, fingering, triplet feels,... +* adapt to your responsive design by dynamic resizing +* play the music sheet via HTML5 Web Audio API (only if browser supports it) -# Thanks to... +# Thanks to... -... the guys of BrowserStack for a free plan. This allows me to test alphaTab on all browsers on all operating systems. Only with this I can ensure that alphaTab is shown to all your visitors as expected. +... the guys of BrowserStack for a free plan. This allows me to test alphaTab on all browsers on all operating systems. Only with this I can ensure that alphaTab is shown to all your visitors as expected.

    - +

    -... to all you people using alphaTab providing new feature ideas and and bug reports. +... to [Bernhard Schelling](https://github.com/schellingb/TinySoundFont) the author of TinySoundFont and [Steve Folta](https://github.com/stevefolta/SFZero) the author of SFZero for providing the core of the synthesis engine. + +... to all you people using alphaTab providing new feature ideas and and bug reports. diff --git a/Samples/CSharp/AlphaTab.Samples.Player/AlphaTab.Samples.Player.csproj b/Samples/CSharp/AlphaTab.Samples.Player/AlphaTab.Samples.Player.csproj deleted file mode 100644 index 34a12b5bb..000000000 --- a/Samples/CSharp/AlphaTab.Samples.Player/AlphaTab.Samples.Player.csproj +++ /dev/null @@ -1,18 +0,0 @@ - - - net471 - Exe - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Samples/CSharp/AlphaTab.Samples.Player/NAudioSynthOutput.cs b/Samples/CSharp/AlphaTab.Samples.Player/NAudioSynthOutput.cs deleted file mode 100644 index 37019b9d4..000000000 --- a/Samples/CSharp/AlphaTab.Samples.Player/NAudioSynthOutput.cs +++ /dev/null @@ -1,127 +0,0 @@ -using System; -using AlphaTab.Audio.Synth; -using AlphaTab.Audio.Synth.Ds; -using AlphaTab.Audio.Synth.Util; -using NAudio.Wave; - -namespace AlphaTab.Samples.PngDump -{ - class NAudioSynthOutput : WaveProvider32, ISynthOutput - { - private const int BufferSize = 4096; - private const int BufferCount = 10; - private const int PreferredSampleRate = 44100; - - private DirectSoundOut _context; - - private CircularSampleBuffer _circularBuffer; - - private bool _finished; - - public int SampleRate - { - get { return PreferredSampleRate; } - } - - public NAudioSynthOutput() - : base(PreferredSampleRate, 2) - { - } - - public void Open() - { - _finished = false; - _circularBuffer = new CircularSampleBuffer(BufferSize * BufferCount); - - _context = new DirectSoundOut(100); - _context.Init(this); - - Ready(); - } - - public void Close() - { - _finished = true; - _context.Stop(); - _circularBuffer.Clear(); - _context.Dispose(); - } - - public void Play() - { - RequestBuffers(); - _finished = false; - _context.Play(); - } - - public void Pause() - { - _context.Pause(); - } - - public void SequencerFinished() - { - _finished = true; - } - - public void AddSamples(SampleArray f) - { - _circularBuffer.Write(f, 0, f.Length); - } - - public void ResetSamples() - { - _circularBuffer.Clear(); - } - - private void RequestBuffers() - { - // if we fall under the half of buffers - // we request one half - const int count = (BufferCount / 2) * BufferSize; - if (_circularBuffer.Count < count && SampleRequest != null) - { - for (int i = 0; i < BufferCount / 2; i++) - { - SampleRequest(); - } - } - } - - public override int Read(float[] buffer, int offset, int count) - { - if (_circularBuffer.Count < count) - { - if (_finished) - { - Finished(); - } - } - else - { - var read = new SampleArray(count); - _circularBuffer.Read(read, 0, read.Length); - - for (int i = 0; i < count; i++) - { - buffer[offset + i] = read[i]; - } - - var samples = count / 2; - SamplesPlayed(samples); - } - - if (!_finished) - { - RequestBuffers(); - } - - return count; - } - - public event Action Ready; - public event Action SamplesPlayed; - public event Action SampleRequest; - public event Action Finished; - } -} \ No newline at end of file diff --git a/Samples/CSharp/AlphaTab.Samples.Player/Program.cs b/Samples/CSharp/AlphaTab.Samples.Player/Program.cs deleted file mode 100644 index 115f6adf4..000000000 --- a/Samples/CSharp/AlphaTab.Samples.Player/Program.cs +++ /dev/null @@ -1,91 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using System.Collections.Generic; -using System.Drawing; -using System.IO; -using AlphaTab.Audio.Generator; -using AlphaTab.Audio.Synth; -using AlphaTab.Audio.Synth.Midi; -using AlphaTab.Importer; -using AlphaTab.Model; -using AlphaTab.Rendering; -using AlphaTab.Util; -using SkiaSharp; - -namespace AlphaTab.Samples.PngDump -{ - class Program - { - static void Main(string[] args) - { - if (args.Length != 2) - { - Console.WriteLine("Usage AlphaTab.ScoreDump.exe PathToFile PathToSoundFont"); - return; - } - - // load score - var score = ScoreLoader.LoadScoreFromBytes(File.ReadAllBytes(args[0])); - - // generate midi - var midiFile = new MidiFile(); - var handler = new AlphaSynthMidiFileHandler(midiFile); - var generator = new MidiFileGenerator(score, null, handler); - generator.Generate(); - - var player = new AlphaSynth(new NAudioSynthOutput()); - player.MidiLoaded += () => { Console.WriteLine("Midi loaded"); }; - player.SoundFontLoaded += () => { Console.WriteLine("SoundFont loaded"); }; - player.MidiLoadFailed += e => { Console.WriteLine("Midi load failed"); }; - player.SoundFontLoadFailed += e => { Console.WriteLine("SoundFont load failed"); }; - player.Finished += _ => - { - Console.WriteLine("Playback finished"); - ((NAudioSynthOutput)player.Output).Close(); - }; - player.PositionChanged += e => - { - TimeSpan currentTime = TimeSpan.FromMilliseconds(e.CurrentTime); - TimeSpan endTime = TimeSpan.FromMilliseconds(e.EndTime); - - Console.CursorTop--; - Console.Write("".PadLeft(Console.BufferWidth - 1, ' ')); - Console.CursorLeft = 0; - Console.WriteLine("{0:mm\\:ss\\:fff} ({1}) of {2:mm\\:ss\\:fff} ({3})", - currentTime, e.CurrentTick, endTime, e.EndTick); - }; - player.ReadyForPlayback += () => - { - Console.WriteLine("Ready for playback"); - }; - player.LoadSoundFont(File.ReadAllBytes(args[1])); - player.LoadMidi(midiFile); - - Console.WriteLine("Start playing"); - player.Play(); - - Console.WriteLine("Press enter to exit"); - Console.ReadLine(); - - player.Pause(); - - Console.ReadLine(); - } - } -} diff --git a/Samples/CSharp/AlphaTab.Samples.Player/Properties/launchSettings.json b/Samples/CSharp/AlphaTab.Samples.Player/Properties/launchSettings.json deleted file mode 100644 index a8fcc1d57..000000000 --- a/Samples/CSharp/AlphaTab.Samples.Player/Properties/launchSettings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "profiles": { - "AlphaTab.Samples.Player": { - "commandName": "Project", - "commandLineArgs": "$(SolutionDir)\\Samples\\JavaScript\\Files\\NightWish.gp5 $(SolutionDir)\\Build\\JavaScript\\default.sf2" - } - } -} \ No newline at end of file diff --git a/Samples/CSharp/AlphaTab.Samples.PngDump/AlphaTab.Samples.PngDump.csproj b/Samples/CSharp/AlphaTab.Samples.PngDump/AlphaTab.Samples.PngDump.csproj deleted file mode 100644 index 1c24cabd6..000000000 --- a/Samples/CSharp/AlphaTab.Samples.PngDump/AlphaTab.Samples.PngDump.csproj +++ /dev/null @@ -1,14 +0,0 @@ - - - netstandard2.0 - Exe - - - - - - - - - - \ No newline at end of file diff --git a/Samples/CSharp/AlphaTab.Samples.PngDump/Program.cs b/Samples/CSharp/AlphaTab.Samples.PngDump/Program.cs deleted file mode 100644 index e40d73ce1..000000000 --- a/Samples/CSharp/AlphaTab.Samples.PngDump/Program.cs +++ /dev/null @@ -1,95 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using System.Collections.Generic; -using System.Drawing; -using System.IO; -using AlphaTab.Importer; -using AlphaTab.Rendering; -using SkiaSharp; - -namespace AlphaTab.Samples.PngDump -{ - class Program - { - static void Main(string[] args) - { - if (args.Length != 1) - { - Console.WriteLine("Usage AlphaTab.ScoreDump.exe Path"); - return; - } - - // load score - var score = ScoreLoader.LoadScoreFromBytes(File.ReadAllBytes(args[0])); - - // render score with svg engine and desired rendering width - var settings = Settings.Defaults; - settings.Engine = "skia"; - settings.Width = 970; - var renderer = new ScoreRenderer(settings); - - // iterate tracks - for (int i = 0, j = score.Tracks.Count; i < j; i++) - { - var track = score.Tracks[i]; - - // render track - Console.WriteLine("Rendering track {0} - {1}", i + 1, track.Name); - var images = new List(); - var totalWidth = 0; - var totalHeight = 0; - renderer.PartialRenderFinished += r => - { - images.Add((SKImage)r.RenderResult); - }; - renderer.RenderFinished += r => - { - totalWidth = (int)r.TotalWidth; - totalHeight = (int)r.TotalHeight; - }; - renderer.Render(score, new[] { track.Index }); - - // write png - var info = new FileInfo(args[0]); - var path = Path.Combine(info.DirectoryName, Path.GetFileNameWithoutExtension(info.Name) + "-" + i + ".png"); - - using (var full = SKSurface.Create(totalWidth, totalHeight, SKImageInfo.PlatformColorType, SKAlphaType.Premul)) - { - int y = 0; - foreach (var image in images) - { - full.Canvas.DrawImage(image, new SKRect(0, 0, image.Width, image.Height), new SKRect(0, y, image.Width, y + image.Height)); - y += image.Height; - } - - using (var fullImage = full.Snapshot()) - { - using (var data = fullImage.Encode(SKEncodedImageFormat.Png, 100).AsStream(true)) - { - using (var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write)) - { - data.CopyTo(fileStream); - } - } - } - } - } - } - } -} diff --git a/Samples/CSharp/AlphaTab.Samples.ScoreDump/AlphaTab.Samples.ScoreDump.csproj b/Samples/CSharp/AlphaTab.Samples.ScoreDump/AlphaTab.Samples.ScoreDump.csproj deleted file mode 100644 index 1c24cabd6..000000000 --- a/Samples/CSharp/AlphaTab.Samples.ScoreDump/AlphaTab.Samples.ScoreDump.csproj +++ /dev/null @@ -1,14 +0,0 @@ - - - netstandard2.0 - Exe - - - - - - - - - - \ No newline at end of file diff --git a/Samples/CSharp/AlphaTab.Samples.ScoreDump/Program.cs b/Samples/CSharp/AlphaTab.Samples.ScoreDump/Program.cs deleted file mode 100644 index 30b32a15f..000000000 --- a/Samples/CSharp/AlphaTab.Samples.ScoreDump/Program.cs +++ /dev/null @@ -1,55 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using System.IO; -using System.Linq; -using AlphaTab.Importer; -using AlphaTab.Model; - -namespace AlphaTab.Samples.ScoreDump -{ - class Program - { - static void Main(string[] args) - { - if (args.Length != 1) - { - Console.WriteLine("Usage AlphaTab.ScoreDump.exe Path"); - return; - } - - var score = ScoreLoader.LoadScoreFromBytes(File.ReadAllBytes(args[0])); - - // score info - Console.WriteLine("Title: {0}", score.Title); - Console.WriteLine("Subtitle: {0}", score.SubTitle); - Console.WriteLine("Artist: {0}", score.Artist); - Console.WriteLine("Tempo: {0}", score.Tempo); - Console.WriteLine("Bars: {0}", score.MasterBars.Count); - Console.WriteLine("Time Signature: {0}/{1}", score.MasterBars[0].TimeSignatureNumerator, - score.MasterBars[0].TimeSignatureDenominator); - // tracks - Console.WriteLine("Tracks: "); - for (int i = 0; i < score.Tracks.Count; i++) - { - Track track = (Track)score.Tracks[i]; - Console.WriteLine(" {0} - {1} - {2}", i + 1, track.Name, track.Staves.Any(s=>s.StaffKind == StaffKind.Percussion) ? "Percussion" : "Midi Instrument: " + track.PlaybackInfo.Program); - } - } - } -} diff --git a/Samples/CSharp/AlphaTab.Samples.WinForms/AlphaTab.Samples.WinForms.csproj b/Samples/CSharp/AlphaTab.Samples.WinForms/AlphaTab.Samples.WinForms.csproj deleted file mode 100644 index d53afc53f..000000000 --- a/Samples/CSharp/AlphaTab.Samples.WinForms/AlphaTab.Samples.WinForms.csproj +++ /dev/null @@ -1,22 +0,0 @@ - - - net471 - WinExe - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Samples/CSharp/AlphaTab.Samples.WinForms/MainWindow.cs b/Samples/CSharp/AlphaTab.Samples.WinForms/MainWindow.cs deleted file mode 100644 index 29ef54e9b..000000000 --- a/Samples/CSharp/AlphaTab.Samples.WinForms/MainWindow.cs +++ /dev/null @@ -1,168 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using System.IO; -using System.Windows.Forms; -using AlphaTab.Importer; -using AlphaTab.Model; -using Track = AlphaTab.Model.Track; - -namespace AlphaTab.Samples.WinForms -{ - public partial class MainWindow : Form - { - private Score _score; - private int _currentTrackIndex; - - public MainWindow() - { - InitializeComponent(); - cmbRenderEngine.SelectedIndex = 0; - } - - private void openFileButton_Click(object sender, EventArgs e) - { - OpenFile(); - } - - #region Score Data - - public Score Score - { - get { return _score; } - set - { - _score = value; - showScoreInfo.Enabled = value != null; - Text = "AlphaTab - " + (value == null ? "No File Opened" : value.Title); - CurrentTrackIndex = 0; - } - } - - public int CurrentTrackIndex - { - get { return _currentTrackIndex; } - set - { - _currentTrackIndex = value; - UpdateSelectedTrack(); - var track = CurrentTrack; - if (track != null) - { - alphaTabControl1.Tracks = new[] { track }; - } - } - } - - public Track CurrentTrack - { - get - { - if (Score == null || CurrentTrackIndex < 0 || CurrentTrackIndex >= _score.Tracks.Count) return null; - return _score.Tracks[_currentTrackIndex]; - } - } - - #endregion - - #region Score Loading - - private void OpenFile() - { - using (OpenFileDialog dialog = new OpenFileDialog()) - { - dialog.Filter = "Supported Files (*.gp3, *.gp4, *.gp5, *.gpx)|*.gp3;*.gp4;*.gp5;*.gpx"; - if (dialog.ShowDialog(this) == DialogResult.OK) - { - OpenFile(dialog.FileName); - } - } - } - - private void OpenFile(string file) - { - if (!string.IsNullOrWhiteSpace(file) && File.Exists(file)) - { - InternalOpenFile(file); - } - } - - private void InternalOpenFile(string file) - { - try - { - // load the score from the filesystem - Score = ScoreLoader.LoadScoreFromBytes(File.ReadAllBytes(file)); - - trackDetails.Controls.Clear(); - trackBars.Controls.Clear(); - for (int i = Score.Tracks.Count - 1; i >= 0; i--) - { - TrackDetailsControl details = new TrackDetailsControl(Score.Tracks[i]); - details.Dock = DockStyle.Top; - details.Height = 25; - trackDetails.Controls.Add(details); - details.Selected += details_Click; - - TrackBarsControl bars = new TrackBarsControl(Score.Tracks[i]); - bars.Dock = DockStyle.Top; - trackBars.Controls.Add(bars); - } - - UpdateSelectedTrack(); - } - catch (Exception e) - { - MessageBox.Show(this, e.Message, "An error during opening the file occured", MessageBoxButtons.OK, - MessageBoxIcon.Error); - } - } - - private void details_Click(object sender, EventArgs e) - { - TrackDetailsControl details = (TrackDetailsControl)sender; - CurrentTrackIndex = _score.Tracks.IndexOf(details.Track); - } - - private void UpdateSelectedTrack() - { - var currentTrack = CurrentTrack; - foreach (TrackDetailsControl trackViewModel in trackDetails.Controls) - { - trackViewModel.IsSelected = currentTrack == trackViewModel.Track; - } - } - - #endregion - - private void showScoreInfo_Click(object sender, EventArgs e) - { - if (_score == null) return; - using (ScoreInfoWindow window = new ScoreInfoWindow(_score)) - { - window.ShowDialog(this); - } - } - - private void cmbRenderEngine_SelectedIndexChanged(object sender, EventArgs e) - { - alphaTabControl1.RenderEngine = cmbRenderEngine.SelectedItem.ToString(); - } - } -} diff --git a/Samples/CSharp/AlphaTab.Samples.WinForms/Program.cs b/Samples/CSharp/AlphaTab.Samples.WinForms/Program.cs deleted file mode 100644 index aa0f2e989..000000000 --- a/Samples/CSharp/AlphaTab.Samples.WinForms/Program.cs +++ /dev/null @@ -1,37 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using System.Windows.Forms; - -namespace AlphaTab.Samples.WinForms -{ - static class Program - { - /// - /// The main entry point for the application. - /// - [STAThread] - static void Main() - { - Application.EnableVisualStyles(); - Application.SetCompatibleTextRenderingDefault(false); - Application.Run(new MainWindow()); - } - } -} diff --git a/Samples/CSharp/AlphaTab.Samples.WinForms/ScoreInfoWindow.cs b/Samples/CSharp/AlphaTab.Samples.WinForms/ScoreInfoWindow.cs deleted file mode 100644 index 7a0235fb2..000000000 --- a/Samples/CSharp/AlphaTab.Samples.WinForms/ScoreInfoWindow.cs +++ /dev/null @@ -1,45 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System.Windows.Forms; -using AlphaTab.Model; - -namespace AlphaTab.Samples.WinForms -{ - public partial class ScoreInfoWindow : Form - { - public ScoreInfoWindow() - { - InitializeComponent(); - } - - public ScoreInfoWindow(Score score) - { - InitializeComponent(); - txtAlbum.Text = score.Album; - txtArtist.Text = score.Artist; - txtCopyright.Text = score.Copyright; - txtLyrics.Text = score.Words; - txtMusic.Text = score.Music; - txtNotes.Text = score.Notices; - txtSubTitle.Text = score.SubTitle; - txtTab.Text = score.Tab; - txtTitle.Text = score.Title; - } - } -} diff --git a/Samples/CSharp/AlphaTab.Samples.WinForms/TrackBarsControl.cs b/Samples/CSharp/AlphaTab.Samples.WinForms/TrackBarsControl.cs deleted file mode 100644 index 733726345..000000000 --- a/Samples/CSharp/AlphaTab.Samples.WinForms/TrackBarsControl.cs +++ /dev/null @@ -1,108 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System.Drawing; -using System.Drawing.Drawing2D; -using System.Windows.Forms; -using AlphaTab.Model; - -namespace AlphaTab.Samples.WinForms -{ - class TrackBarsControl : Control - { - private static readonly Size BlockSize = new Size(25, 25); - private readonly bool[] _usedBars; - private Color _startColor; - private Color _endColor; - - public TrackBarsControl(Track track) - { - SetStyle(ControlStyles.FixedHeight, true); - SetStyle(ControlStyles.DoubleBuffer, true); - SetStyle(ControlStyles.OptimizedDoubleBuffer, true); - SetStyle(ControlStyles.ResizeRedraw, true); - SetStyle(ControlStyles.UserPaint, true); - base.DoubleBuffered = true; - base.BackColor = Color.FromArgb(93, 95, 94); - - _usedBars = new bool[track.Score.MasterBars.Count]; - for (int s = 0; s < track.Staves.Count; s++) - { - var staff = track.Staves[s]; - for (int barI = 0; barI < staff.Bars.Count; barI++) - { - var bar = staff.Bars[barI]; - _usedBars[barI] = false; - - for (int voiceI = 0; voiceI < bar.Voices.Count && (!_usedBars[barI]); voiceI++) - { - Voice voice = bar.Voices[voiceI]; - for (int i = 0; i < voice.Beats.Count; i++) - { - var b = voice.Beats[i]; - if (!b.IsRest) - { - _usedBars[barI] = true; - } - } - } - } - } - PerformLayout(); - Width = BlockSize.Width * _usedBars.Length; - Height = BlockSize.Height; - MinimumSize = BlockSize; - - SetColor(track.Color); - } - - private void SetColor(Platform.Model.Color color) - { - var baseColor = Color.FromArgb(color.R, color.G, color.B); - double h, s, l; - ColorTools.RGB2HSL(baseColor, out h, out s, out l); - - _startColor = ColorTools.HSL2RGB(h, System.Math.Max(0, System.Math.Min(1, s - 0.2)), - System.Math.Max(0, System.Math.Min(1, l + 0.2))); - _endColor = ColorTools.HSL2RGB(h, System.Math.Max(0, System.Math.Min(1, s - 0.2)), - System.Math.Max(0, System.Math.Min(1, l - 0.2))); - } - - protected override void OnPaint(PaintEventArgs e) - { - base.OnPaint(e); - if (_usedBars == null) return; - - using (LinearGradientBrush brush = new LinearGradientBrush(DisplayRectangle, _startColor, _endColor, LinearGradientMode.Vertical)) - { - e.Graphics.FillRectangle(brush, new Rectangle(0, 0, _usedBars.Length * BlockSize.Width, BlockSize.Height)); - } - - using (Pen pen = new Pen(Color.FromArgb(75,255,255,255))) - { - for (int i = 0; i < _usedBars.Length; i++) - { - e.Graphics.DrawLine(pen, (i + 1) * BlockSize.Width, 0, (i + 1) * BlockSize.Width, BlockSize.Height); - } - pen.Color = Color.FromArgb(51, 51, 51); - e.Graphics.DrawLine(pen, 0, Height - 1, _usedBars.Length * BlockSize.Width, Height - 1); - } - - } - } -} diff --git a/Samples/CSharp/AlphaTab.Samples.WinForms/TrackDetailsControl.cs b/Samples/CSharp/AlphaTab.Samples.WinForms/TrackDetailsControl.cs deleted file mode 100644 index 47dedbe48..000000000 --- a/Samples/CSharp/AlphaTab.Samples.WinForms/TrackDetailsControl.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System; -using System.Drawing; -using System.Windows.Forms; -using AlphaTab.Model; - -namespace AlphaTab.Samples.WinForms -{ - public partial class TrackDetailsControl : UserControl - { - private bool _isSelected; - private Track _track; - - public bool IsSelected - { - get { return _isSelected; } - set - { - _isSelected = value; - BackColor = value ? Color.FromArgb(116, 118, 117) : Color.FromArgb(93, 94, 95); - } - } - - public Track Track - { - get { return _track; } - set - { - _track = value; - TrackName = value.Name; - Volume = value.PlaybackInfo.Volume; - IsSolo = value.PlaybackInfo.IsSolo; - IsMute = value.PlaybackInfo.IsMute; - } - } - - public string TrackName - { - get { return lblName.Text; } - set { lblName.Text = value; } - } - - public int Volume - { - get { return volumeTrack.Value; } - set { volumeTrack.Value = value; } - } - - public bool IsSolo - { - get { return isSoloCheck.Checked; } - set { isSoloCheck.Checked = value; } - } - - public bool IsMute - { - get { return isMuteCheck.Checked; } - set { isMuteCheck.Checked = value; } - } - - public TrackDetailsControl() - { - InitializeComponent(); - } - - public TrackDetailsControl(Track track) - { - InitializeComponent(); - Track = track; - } - - public event EventHandler Selected; - protected virtual void OnSelected() - { - EventHandler handler = Selected; - if (handler != null) handler(this, EventArgs.Empty); - } - - private void lblName_Click(object sender, EventArgs e) - { - OnSelected(); - } - } -} diff --git a/Samples/CSharp/AlphaTab.Samples.WinForms/TrackHeaderControl.cs b/Samples/CSharp/AlphaTab.Samples.WinForms/TrackHeaderControl.cs deleted file mode 100644 index 1bd15b25e..000000000 --- a/Samples/CSharp/AlphaTab.Samples.WinForms/TrackHeaderControl.cs +++ /dev/null @@ -1,30 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System.Windows.Forms; - -namespace AlphaTab.Samples.WinForms -{ - public partial class TrackHeaderControl : UserControl - { - public TrackHeaderControl() - { - InitializeComponent(); - } - } -} diff --git a/Samples/CSharp/AlphaTab.Samples.WinForms/packages.config b/Samples/CSharp/AlphaTab.Samples.WinForms/packages.config deleted file mode 100644 index 593e8d1cd..000000000 --- a/Samples/CSharp/AlphaTab.Samples.WinForms/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/Samples/CSharp/AlphaTab.Samples.Wpf/AlphaTab.Samples.Wpf.csproj b/Samples/CSharp/AlphaTab.Samples.Wpf/AlphaTab.Samples.Wpf.csproj deleted file mode 100644 index 68ba9d43f..000000000 --- a/Samples/CSharp/AlphaTab.Samples.Wpf/AlphaTab.Samples.Wpf.csproj +++ /dev/null @@ -1,27 +0,0 @@ - - - net471 - WinExe - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Samples/CSharp/AlphaTab.Samples.Wpf/App.xaml b/Samples/CSharp/AlphaTab.Samples.Wpf/App.xaml deleted file mode 100644 index b28faeae9..000000000 --- a/Samples/CSharp/AlphaTab.Samples.Wpf/App.xaml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - diff --git a/Samples/CSharp/AlphaTab.Samples.Wpf/App.xaml.cs b/Samples/CSharp/AlphaTab.Samples.Wpf/App.xaml.cs deleted file mode 100644 index 8882d7455..000000000 --- a/Samples/CSharp/AlphaTab.Samples.Wpf/App.xaml.cs +++ /dev/null @@ -1,46 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System.Globalization; -using System.Threading; -using System.Windows; -using System.Windows.Markup; -using AlphaTab.Samples.Wpf.ViewModel; - -namespace AlphaTab.Samples.Wpf -{ - /// - /// Interaction logic for App.xaml - /// - public partial class App - { - public static void InitializeCultures() - { - Thread.CurrentThread.CurrentCulture = new CultureInfo("en-us"); - Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-us"); - - FrameworkElement.LanguageProperty.OverrideMetadata(typeof(FrameworkElement), new FrameworkPropertyMetadata( - XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag))); - } - - protected override void OnStartup(StartupEventArgs e) - { - base.OnStartup(e); - ViewModelBase.Initialize(); - } - } -} diff --git a/Samples/CSharp/AlphaTab.Samples.Wpf/Controls/ToolBarCustom.cs b/Samples/CSharp/AlphaTab.Samples.Wpf/Controls/ToolBarCustom.cs deleted file mode 100644 index 3d9843e84..000000000 --- a/Samples/CSharp/AlphaTab.Samples.Wpf/Controls/ToolBarCustom.cs +++ /dev/null @@ -1,43 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System.Windows; -using System.Windows.Controls; - -namespace AlphaTab.Samples.Wpf.Controls -{ - /// - /// A custom toolbar with hidden overflow button - /// - public class ToolBarCustom : ToolBar - { - public ToolBarCustom() - { - Loaded += OnLoaded; - } - - private void OnLoaded(object sender, RoutedEventArgs e) - { - var overflowGrid = Template.FindName("OverflowGrid", this) as FrameworkElement; - if (overflowGrid != null) - { - overflowGrid.Visibility = Visibility.Collapsed; - } - } - } -} diff --git a/Samples/CSharp/AlphaTab.Samples.Wpf/Converter/BoolToBrushConverter.cs b/Samples/CSharp/AlphaTab.Samples.Wpf/Converter/BoolToBrushConverter.cs deleted file mode 100644 index a677ced56..000000000 --- a/Samples/CSharp/AlphaTab.Samples.Wpf/Converter/BoolToBrushConverter.cs +++ /dev/null @@ -1,44 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using System.Globalization; -using System.Windows.Data; -using System.Windows.Media; - -namespace AlphaTab.Samples.Wpf.Converter -{ - /// - /// A simple ValueConverter which converts a bool to a associated brush. - /// - public class BoolToBrushConverter : IValueConverter - { - public Brush TrueBrush { get; set; } - public Brush FalseBrush { get; set; } - - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - return value is bool && (bool) value ? TrueBrush : FalseBrush; - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - throw new NotImplementedException(); - } - } -} diff --git a/Samples/CSharp/AlphaTab.Samples.Wpf/Data/DialogService.cs b/Samples/CSharp/AlphaTab.Samples.Wpf/Data/DialogService.cs deleted file mode 100644 index 33e1a7c1d..000000000 --- a/Samples/CSharp/AlphaTab.Samples.Wpf/Data/DialogService.cs +++ /dev/null @@ -1,52 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System.Windows; -using AlphaTab.Model; -using AlphaTab.Samples.Wpf.ViewModel; -using Microsoft.Win32; - -namespace AlphaTab.Samples.Wpf.Data -{ - /// - /// This DialogService implementation opens uses WPF dialogs - /// - public class DialogService :IDialogService - { - public string OpenFile() - { - OpenFileDialog dialog = new OpenFileDialog - { - Filter = "Supported Files (*.gp3, *.gp4, *.gp5, *.gpx)|*.gp3;*.gp4;*.gp5;*.gpx" - }; - if (dialog.ShowDialog(Application.Current.MainWindow).GetValueOrDefault()) - { - return dialog.FileName; - } - return null; - } - - public void ShowScoreInfo(Score score) - { - ScoreInfoViewModel viewModel = new ScoreInfoViewModel(score); - ScoreInfoWindow window = new ScoreInfoWindow(); - window.DataContext = viewModel; - window.ShowDialog(); - } - } -} diff --git a/Samples/CSharp/AlphaTab.Samples.Wpf/Data/ErrorService.cs b/Samples/CSharp/AlphaTab.Samples.Wpf/Data/ErrorService.cs deleted file mode 100644 index 8037cacd7..000000000 --- a/Samples/CSharp/AlphaTab.Samples.Wpf/Data/ErrorService.cs +++ /dev/null @@ -1,39 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2017, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using System.Windows; -using AlphaTab.Samples.Wpf.ViewModel; - -namespace AlphaTab.Samples.Wpf.Data -{ - /// - /// This error service implementation shows errors as messageboxes on the main window - /// - public class ErrorService : IErrorService - { - public void OpenFailed(Exception e) - { - ViewModelBase.InvokeOnUi(() => - { - MessageBox.Show(Application.Current.MainWindow, e.Message, "An error during opening the file occured", - MessageBoxButton.OK, MessageBoxImage.Error); - }); - } - } -} \ No newline at end of file diff --git a/Samples/CSharp/AlphaTab.Samples.Wpf/Data/IDialogService.cs b/Samples/CSharp/AlphaTab.Samples.Wpf/Data/IDialogService.cs deleted file mode 100644 index 815e295d2..000000000 --- a/Samples/CSharp/AlphaTab.Samples.Wpf/Data/IDialogService.cs +++ /dev/null @@ -1,37 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Model; - -namespace AlphaTab.Samples.Wpf.Data -{ - public interface IDialogService - { - /// - /// Called when the application wants to open a new file. - /// Implementation should ask the user to select a file from the filesystem. - /// - /// The path to the file which should be opened or null if no file should be opened - string OpenFile(); - - /// - /// Called when the application wants to show a score information. - /// - void ShowScoreInfo(Score score); - } -} diff --git a/Samples/CSharp/AlphaTab.Samples.Wpf/Data/IErrorService.cs b/Samples/CSharp/AlphaTab.Samples.Wpf/Data/IErrorService.cs deleted file mode 100644 index 288b9fb27..000000000 --- a/Samples/CSharp/AlphaTab.Samples.Wpf/Data/IErrorService.cs +++ /dev/null @@ -1,34 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; - -namespace AlphaTab.Samples.Wpf.Data -{ - /// - /// Implement this interface to handle application errors. - /// - public interface IErrorService - { - /// - /// Called when an error during opening a file occurs. - /// - /// The exception occured during opening the file - void OpenFailed(Exception e); - } -} diff --git a/Samples/CSharp/AlphaTab.Samples.Wpf/MainWindow.xaml.cs b/Samples/CSharp/AlphaTab.Samples.Wpf/MainWindow.xaml.cs deleted file mode 100644 index e802613ba..000000000 --- a/Samples/CSharp/AlphaTab.Samples.Wpf/MainWindow.xaml.cs +++ /dev/null @@ -1,43 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System.Windows; -using AlphaTab.Samples.Wpf.Data; -using AlphaTab.Samples.Wpf.ViewModel; - -namespace AlphaTab.Samples.Wpf -{ - /// - /// Interaction logic for MainWindow.xaml - /// - public partial class MainWindow : Window - { - public MainWindow() - { - InitializeComponent(); - - // ensure the UI is using en-us as culture - // we need this culture for correct WPF path string generation - // in a German culture it could happen that we have a , as a decimal separator. - App.InitializeCultures(); - - // create a our viewmodel for databinding - MainViewModel viewModel = new MainViewModel(new DialogService(), new ErrorService()); - DataContext = viewModel; - } - } -} diff --git a/Samples/CSharp/AlphaTab.Samples.Wpf/ScoreInfoWindow.xaml.cs b/Samples/CSharp/AlphaTab.Samples.Wpf/ScoreInfoWindow.xaml.cs deleted file mode 100644 index 0c5294537..000000000 --- a/Samples/CSharp/AlphaTab.Samples.Wpf/ScoreInfoWindow.xaml.cs +++ /dev/null @@ -1,49 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System.Windows; -using System.Windows.Input; - -namespace AlphaTab.Samples.Wpf -{ - /// - /// Interaction logic for ScoreInfoWindow.xaml - /// - public partial class ScoreInfoWindow - { - public ScoreInfoWindow() - { - InitializeComponent(); - } - - private void OnOkClick(object sender, RoutedEventArgs e) - { - DialogResult = true; - } - - protected override void OnPreviewKeyDown(KeyEventArgs e) - { - base.OnPreviewKeyDown(e); - if (e.Key == Key.Escape) - { - DialogResult = false; - e.Handled = true; - } - } - - } -} diff --git a/Samples/CSharp/AlphaTab.Samples.Wpf/Utils/RelayCommand.cs b/Samples/CSharp/AlphaTab.Samples.Wpf/Utils/RelayCommand.cs deleted file mode 100644 index 2d070a862..000000000 --- a/Samples/CSharp/AlphaTab.Samples.Wpf/Utils/RelayCommand.cs +++ /dev/null @@ -1,62 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using System.Windows.Input; -using AlphaTab.Samples.Wpf.ViewModel; - -namespace AlphaTab.Samples.Wpf.Utils -{ - /// - /// This command implementation delegates the execution to a referenced method. - /// - public class RelayCommand : ICommand - { - private readonly Action _action; - private readonly Func _canExecute; - - public RelayCommand(Action action, Func canExecute = null) - { - _action = action; - _canExecute = canExecute; - } - - public bool CanExecute(object parameter) - { - return _canExecute == null || _canExecute(); - } - - public void Execute(object parameter) - { - _action(); - } - - public event EventHandler CanExecuteChanged; - public virtual void RaiseCanExecuteChanged() - { - EventHandler handler = CanExecuteChanged; - if (handler != null) - { - ViewModelBase.InvokeOnUi(() => - { - handler(this, EventArgs.Empty); - }); - } - } - } -} diff --git a/Samples/CSharp/AlphaTab.Samples.Wpf/ViewModel/MainViewModel.cs b/Samples/CSharp/AlphaTab.Samples.Wpf/ViewModel/MainViewModel.cs deleted file mode 100644 index 612b76758..000000000 --- a/Samples/CSharp/AlphaTab.Samples.Wpf/ViewModel/MainViewModel.cs +++ /dev/null @@ -1,263 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using System.Windows.Input; -using AlphaTab.Importer; -using AlphaTab.Model; -using AlphaTab.Samples.Wpf.Data; -using AlphaTab.Samples.Wpf.Utils; - -namespace AlphaTab.Samples.Wpf.ViewModel -{ - /// - /// This viewmodel contains the data and logic for the main application window. - /// - public class MainViewModel : ViewModelBase - { - // references to the services we want to use - private readonly IDialogService _dialogService; - private readonly IErrorService _errorService; - - #region Score Data - - // those properties store the score information - - private Score _score; - private int _currentTrackIndex; - private IEnumerable _trackInfos; - private TrackViewModel _selectedTrackInfo; - private readonly RelayCommand _showScoreInfoCommand; - private string[] _renderEngines; - private string _renderEngine; - - /// - /// A command which raises the method - /// - public ICommand ShowScoreInfoCommand - { - get { return _showScoreInfoCommand; } - } - - public string[] RenderEngines - { - get { return _renderEngines; } - } - - public string RenderEngine - { - get { return _renderEngine; } - set - { - if (value == _renderEngine) return; - _renderEngine = value; - OnPropertyChanged(); - } - } - - /// - /// Gets or sets the currently opened score. - /// If a new score is selected, the first track gets loaded. - /// - public Score Score - { - get { return _score; } - set - { - _score = value; - OnPropertyChanged(); - OnPropertyChanged("ScoreTitle"); - _showScoreInfoCommand.RaiseCanExecuteChanged(); - - // select the first track - CurrentTrackIndex = 0; - } - } - - /// - /// Gets or sets the title of the currently loaded sore. - /// - public string ScoreTitle - { - get - { - return _score == null ? "No File Opened" : _score.Title; - } - } - - /// - /// Gets or sets the index of the track which should be currently displayed. - /// - public int CurrentTrackIndex - { - get { return _currentTrackIndex; } - set - { - _currentTrackIndex = value; - - // update the visual track selection if a new track is selected - UpdateSelectedViewModel(); - - // notify the ui - OnPropertyChanged(); - OnPropertyChanged(nameof(CurrentTracks)); - } - } - - /// - /// Gets the currently selected track. - /// - public IEnumerable CurrentTracks - { - get - { - if (Score == null || CurrentTrackIndex < 0 || CurrentTrackIndex >= _score.Tracks.Count) return null; - return new[] { _score.Tracks[_currentTrackIndex] }; - } - } - - /// - /// Gets the information about the currently loaded tracks - /// - public IEnumerable TrackInfos - { - get { return _trackInfos; } - private set - { - _trackInfos = value; - - // ensure correct selection - UpdateSelectedViewModel(); - OnPropertyChanged(); - } - } - - /// - /// Gets or sets the currently selected track. - /// - public TrackViewModel SelectedTrackInfo - { - get { return _selectedTrackInfo; } - set - { - _selectedTrackInfo = value; - // select the new track - if (_score != null) - { - CurrentTrackIndex = _score.Tracks.IndexOf(_selectedTrackInfo.Track); - } - OnPropertyChanged(); - } - } - - /// - /// Opens a score info dialog for the current score. - /// - public void ShowScoreInfo() - { - if (_score != null) - _dialogService.ShowScoreInfo(_score); - } - - - - #endregion - - #region Score Loading - - /// - /// A command which raises a file opening - /// - public ICommand OpenFileCommand { get; private set; } - - /// - /// Opens a new file by loading the file path using the IO service. - /// - public void OpenFile() - { - OpenFile(_dialogService.OpenFile()); - } - - /// - /// Opens a new file from the specified file path. - /// - /// the path to the file to load - private void OpenFile(string file) - { - if (!string.IsNullOrWhiteSpace(file) && File.Exists(file)) - { - InternalOpenFile(file); - } - } - - private void InternalOpenFile(string file) - { - Task.Factory.StartNew(() => - { - try - { - // load the score from the filesystem - Score = ScoreLoader.LoadScoreFromBytes(File.ReadAllBytes(file)); - - // build the track info objects for the ui - TrackViewModel[] trackInfos = new TrackViewModel[Score.Tracks.Count]; - for (int i = 0; i < trackInfos.Length; i++) - { - trackInfos[i] = new TrackViewModel(Score.Tracks[i]); - } - TrackInfos = trackInfos; - } - catch (Exception e) - { - _errorService.OpenFailed(e); - } - }); - } - - /// - /// Updates the currently selected viewmodel - /// - private void UpdateSelectedViewModel() - { - if (_trackInfos != null) - { - var currentTrackLookup = CurrentTracks.ToLookup(t => t.Index); - foreach (var trackViewModel in _trackInfos) - { - trackViewModel.IsSelected = currentTrackLookup.Contains(trackViewModel.Track.Index); - } - } - } - - #endregion - - public MainViewModel(IDialogService dialogService, IErrorService errorService) - { - _renderEngines = new[] { "gdi", "skia" }; - _renderEngine = _renderEngines[0]; - _dialogService = dialogService; - _errorService = errorService; - OpenFileCommand = new RelayCommand(OpenFile); - _showScoreInfoCommand = new RelayCommand(ShowScoreInfo, () => _score != null); - } - } -} diff --git a/Samples/CSharp/AlphaTab.Samples.Wpf/ViewModel/ScoreInfoViewModel.cs b/Samples/CSharp/AlphaTab.Samples.Wpf/ViewModel/ScoreInfoViewModel.cs deleted file mode 100644 index d2e1343ee..000000000 --- a/Samples/CSharp/AlphaTab.Samples.Wpf/ViewModel/ScoreInfoViewModel.cs +++ /dev/null @@ -1,150 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Model; - -namespace AlphaTab.Samples.Wpf.ViewModel -{ - public class ScoreInfoViewModel : ViewModelBase - { - private string _tab; - private string _words; - private string _title; - private string _subTitle; - private string _notices; - private string _music; - private string _instructions; - private string _copyright; - private string _artist; - private string _album; - - public ScoreInfoViewModel(Score score) - { - _tab = score.Tab; - _words = score.Words; - _title = score.Title; - _subTitle = score.SubTitle; - _notices = score.Notices; - _music = score.Music; - _instructions = score.Instructions; - _copyright = score.Copyright; - _artist = score.Artist; - _album = score.Album; - } - - public string Album - { - get { return _album; } - set - { - _album = value; - OnPropertyChanged(); - } - } - - public string Artist - { - get { return _artist; } - set - { - _artist = value; - OnPropertyChanged(); - } - } - - public string Copyright - { - get { return _copyright; } - set - { - _copyright = value; - OnPropertyChanged(); - } - } - - public string Instructions - { - get { return _instructions; } - set - { - _instructions = value; - OnPropertyChanged(); - } - } - - public string Music - { - get { return _music; } - set - { - _music = value; - OnPropertyChanged(); - } - } - - public string Notices - { - get { return _notices; } - set - { - _notices = value; - OnPropertyChanged(); - } - } - - public string SubTitle - { - get { return _subTitle; } - set - { - _subTitle = value; - OnPropertyChanged(); - } - } - - public string Title - { - get { return _title; } - set - { - _title = value; - OnPropertyChanged(); - } - } - - public string Words - { - get { return _words; } - set - { - _words = value; - OnPropertyChanged(); - } - } - - public string Tab - { - get { return _tab; } - set - { - _tab = value; - OnPropertyChanged(); - } - } - } -} diff --git a/Samples/CSharp/AlphaTab.Samples.Wpf/ViewModel/TrackViewModel.cs b/Samples/CSharp/AlphaTab.Samples.Wpf/ViewModel/TrackViewModel.cs deleted file mode 100644 index 78fb29b7e..000000000 --- a/Samples/CSharp/AlphaTab.Samples.Wpf/ViewModel/TrackViewModel.cs +++ /dev/null @@ -1,175 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System.Linq; -using AlphaTab.Model; - -namespace AlphaTab.Samples.Wpf.ViewModel -{ - /// - /// A viewmodel for displaying track information in the UI - /// - public class TrackViewModel : ViewModelBase - { - private TrackType _trackType; - private bool[] _usedBars; - private bool _isSelected; - private Track _track; - - public bool IsSelected - { - get { return _isSelected; } - set - { - _isSelected = value; - OnPropertyChanged(); - } - } - - public TrackType TrackType - { - get { return _trackType; } - set - { - _trackType = value; - OnPropertyChanged(); - } - } - - public string Name - { - get { return _track.Name; } - set - { - _track.Name = value; - OnPropertyChanged(); - } - } - - public int Volume - { - get { return _track.PlaybackInfo.Volume; } - set - { - _track.PlaybackInfo.Volume = value; - OnPropertyChanged(); - } - } - - public bool IsSolo - { - get { return _track.PlaybackInfo.IsSolo; } - set - { - _track.PlaybackInfo.IsSolo = value; - OnPropertyChanged(); - } - } - - public bool IsMute - { - get { return _track.PlaybackInfo.IsMute; } - set - { - _track.PlaybackInfo.IsMute = value; - OnPropertyChanged(); - } - } - - public bool[] UsedBars - { - get { return _usedBars; } - set - { - _usedBars = value; - OnPropertyChanged(); - } - } - - public Track Track - { - get { return _track; } - set - { - _track = value; - OnPropertyChanged(); - } - } - - public TrackViewModel(Track track) - { - _track = track; - - // general midi Programs - if (track.Staves.Any(s=>s.StaffKind == StaffKind.Percussion)) - { - TrackType = TrackType.Drums; - } - else if (track.PlaybackInfo.Program >= 0 && track.PlaybackInfo.Program <= 6) - { - TrackType = TrackType.Piano; - } - else if (track.PlaybackInfo.Program >= 26 && track.PlaybackInfo.Program <= 31) - { - TrackType = TrackType.ElectricGuitar; - } - else if (track.PlaybackInfo.Program >= 32 && track.PlaybackInfo.Program <= 39) - { - TrackType = TrackType.BassGuitar; - } - else - { - TrackType = TrackType.Default; - } - - // scan all bars if they have any note - _usedBars = new bool[track.Score.MasterBars.Count]; - for (int s = 0; s < track.Staves.Count; s++) - { - var staff = track.Staves[s]; - for (int barI = 0; barI < staff.Bars.Count; barI++) - { - Bar bar = staff.Bars[barI]; - _usedBars[barI] = false; - - for (int voiceI = 0; voiceI < bar.Voices.Count && (!_usedBars[barI]); voiceI++) - { - Voice voice = bar.Voices[voiceI]; - for (int i = 0; i < voice.Beats.Count; i++) - { - var b = voice.Beats[i]; - if (!b.IsRest) - { - _usedBars[barI] = true; - } - } - } - } - } - } - } - - public enum TrackType - { - Default, - ElectricGuitar, - BassGuitar, - Drums, - Piano - } -} diff --git a/Samples/CSharp/AlphaTab.Samples.Wpf/packages.config b/Samples/CSharp/AlphaTab.Samples.Wpf/packages.config deleted file mode 100644 index 9fd7129ad..000000000 --- a/Samples/CSharp/AlphaTab.Samples.Wpf/packages.config +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/AlphaTab.Samples.XamarinForms.Android.csproj b/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/AlphaTab.Samples.XamarinForms.Android.csproj deleted file mode 100644 index c5650ef6f..000000000 --- a/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/AlphaTab.Samples.XamarinForms.Android.csproj +++ /dev/null @@ -1,229 +0,0 @@ - - - - - Debug - AnyCPU - 8.0.30703 - 2.0 - {A5D6838F-254F-480C-A6FB-0FAABCE659BA} - {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - Library - Properties - AlphaTab.Samples.XamarinForms.Android - AlphaTab.Samples.XamarinForms.Android - 512 - true - Resources\Resource.Designer.cs - Off - Properties\AndroidManifest.xml - true - v7.1 - - - - - - - - - True - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - True - None - False - False - False - .Net (Xamarin) - False - False - False - False - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - False - SdkOnly - True - False - False - armeabi;armeabi-v7a;x86;x86_64;arm64-v8a - .Net (Xamarin) - False - False - False - False - False - - - - ..\..\..\packages\Xamarin.Forms.2.3.4.231\lib\MonoAndroid10\FormsViewGroup.dll - - - - - - - - - ..\..\..\packages\Xamarin.Android.Support.Animated.Vector.Drawable.25.1.1\lib\MonoAndroid70\Xamarin.Android.Support.Animated.Vector.Drawable.dll - - - ..\..\..\packages\Xamarin.Android.Support.Compat.25.1.1\lib\MonoAndroid70\Xamarin.Android.Support.Compat.dll - - - ..\..\..\packages\Xamarin.Android.Support.Core.UI.25.1.1\lib\MonoAndroid70\Xamarin.Android.Support.Core.UI.dll - - - ..\..\..\packages\Xamarin.Android.Support.Core.Utils.25.1.1\lib\MonoAndroid70\Xamarin.Android.Support.Core.Utils.dll - - - ..\..\..\packages\Xamarin.Android.Support.Design.25.1.1\lib\MonoAndroid70\Xamarin.Android.Support.Design.dll - - - ..\..\..\packages\Xamarin.Android.Support.Fragment.25.1.1\lib\MonoAndroid70\Xamarin.Android.Support.Fragment.dll - - - ..\..\..\packages\Xamarin.Android.Support.Media.Compat.25.1.1\lib\MonoAndroid70\Xamarin.Android.Support.Media.Compat.dll - - - ..\..\..\packages\Xamarin.Android.Support.Transition.25.1.1\lib\MonoAndroid70\Xamarin.Android.Support.Transition.dll - - - ..\..\..\packages\Xamarin.Android.Support.v4.25.1.1\lib\MonoAndroid70\Xamarin.Android.Support.v4.dll - - - ..\..\..\packages\Xamarin.Android.Support.v7.AppCompat.25.1.1\lib\MonoAndroid70\Xamarin.Android.Support.v7.AppCompat.dll - - - ..\..\..\packages\Xamarin.Android.Support.v7.CardView.25.1.1\lib\MonoAndroid70\Xamarin.Android.Support.v7.CardView.dll - - - ..\..\..\packages\Xamarin.Android.Support.v7.MediaRouter.25.1.1\lib\MonoAndroid70\Xamarin.Android.Support.v7.MediaRouter.dll - - - ..\..\..\packages\Xamarin.Android.Support.v7.Palette.25.1.1\lib\MonoAndroid70\Xamarin.Android.Support.v7.Palette.dll - - - ..\..\..\packages\Xamarin.Android.Support.v7.RecyclerView.25.1.1\lib\MonoAndroid70\Xamarin.Android.Support.v7.RecyclerView.dll - - - ..\..\..\packages\Xamarin.Android.Support.Vector.Drawable.25.1.1\lib\MonoAndroid70\Xamarin.Android.Support.Vector.Drawable.dll - - - ..\..\..\packages\Xamarin.Forms.2.3.4.231\lib\MonoAndroid10\Xamarin.Forms.Core.dll - - - ..\..\..\packages\Xamarin.Forms.2.3.4.231\lib\MonoAndroid10\Xamarin.Forms.Platform.dll - - - ..\..\..\packages\Xamarin.Forms.2.3.4.231\lib\MonoAndroid10\Xamarin.Forms.Platform.Android.dll - - - ..\..\..\packages\Xamarin.Forms.2.3.4.231\lib\MonoAndroid10\Xamarin.Forms.Xaml.dll - - - - - App.xaml - - - - MainPage.xaml - - - - - - - Assets\Canon.gp5 - - - - - - - - - - - - - - - - - - MSBuild:UpdateDesignTimeXaml - - - MSBuild:UpdateDesignTimeXaml - - - - - {bcc950ea-7465-47fc-a7af-e733b55ec91f} - AlphaTab.CSharp - - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/App.xaml b/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/App.xaml deleted file mode 100644 index 26e6736a9..000000000 --- a/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/App.xaml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/App.xaml.cs b/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/App.xaml.cs deleted file mode 100644 index dc78df45e..000000000 --- a/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/App.xaml.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Xamarin.Forms; - -namespace AlphaTab.Samples.XamarinForms.Android -{ - public partial class App : Application - { - public App () - { - InitializeComponent(); - - MainPage = new MainPage(); - } - - protected override void OnStart () - { - // Handle when your app starts - } - - protected override void OnSleep () - { - // Handle when your app sleeps - } - - protected override void OnResume () - { - // Handle when your app resumes - } - } -} diff --git a/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/MainActivity.cs b/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/MainActivity.cs deleted file mode 100644 index 83567312a..000000000 --- a/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/MainActivity.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Android.App; -using Android.Content.PM; -using Android.OS; - -namespace AlphaTab.Samples.XamarinForms.Android -{ - [Activity (Label = "AlphaTab.Samples.XamarinForms", Icon = "@drawable/icon", Theme="@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)] - public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity - { - protected override void OnCreate (Bundle bundle) - { - TabLayoutResource = Resource.Layout.Tabbar; - ToolbarResource = Resource.Layout.Toolbar; - - base.OnCreate (bundle); - - Xamarin.Forms.Forms.Init (this, bundle); - - LoadApplication(new App ()); - } - } -} - diff --git a/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/MainPage.xaml b/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/MainPage.xaml deleted file mode 100644 index 110488598..000000000 --- a/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/MainPage.xaml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/MainPage.xaml.cs b/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/MainPage.xaml.cs deleted file mode 100644 index ea5208fa0..000000000 --- a/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/MainPage.xaml.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.IO; -using AlphaTab.Importer; -using AlphaTab.Model; -using Xamarin.Forms; - -namespace AlphaTab.Samples.XamarinForms.Android -{ - public partial class MainPage - { - private Score _score; - public MainPage() - { - InitializeComponent(); - BindingContext = this; - - byte[] canon; - using (var stream = Forms.Context.Assets.Open("Canon.gp5")) - { - using (var ms = new MemoryStream()) - { - stream.CopyTo(ms); - canon = ms.ToArray(); - } - } - LoadScore(canon); - } - - private void LoadScore(byte[] bytes) - { - _score = ScoreLoader.LoadScoreFromBytes(bytes); - AlphaTabControl.Tracks = new[] - { - _score.Tracks[0] - }; - } - } -} diff --git a/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/Properties/AndroidManifest.xml b/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/Properties/AndroidManifest.xml deleted file mode 100644 index ad00b9383..000000000 --- a/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/Properties/AndroidManifest.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/Properties/AssemblyInfo.cs b/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/Properties/AssemblyInfo.cs deleted file mode 100644 index 145828640..000000000 --- a/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,50 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2017, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System.Reflection; -using System.Runtime.InteropServices; -using Android.App; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("AlphaTab.Samples.XamarinForms.Android")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("AlphaTab")] -[assembly: AssemblyCopyright("Copyright © 2014")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] -[assembly: ComVisible(false)] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] - -// Add some common permissions, these can be removed if not needed -[assembly: UsesPermission(Android.Manifest.Permission.Internet)] -[assembly: UsesPermission(Android.Manifest.Permission.WriteExternalStorage)] diff --git a/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/Readme.md b/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/Readme.md deleted file mode 100644 index 00dece1cc..000000000 --- a/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/Readme.md +++ /dev/null @@ -1,4 +0,0 @@ -# AlphaTab Xamarin Forms Android Sample - - This sample shows how to embedd alphaTab into a Xamarin Forms App targeting Android. The sample loads an embedded Guitar Pro file and displays it using - the Xamarin Forms Control provided by alphaTab. \ No newline at end of file diff --git a/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/Resources/Resource.Designer.cs b/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/Resources/Resource.Designer.cs deleted file mode 100644 index 4d389015e..000000000 --- a/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/Resources/Resource.Designer.cs +++ /dev/null @@ -1,6958 +0,0 @@ -#pragma warning disable 1591 -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -[assembly: global::Android.Runtime.ResourceDesignerAttribute("AlphaTab.Samples.XamarinForms.Android.Resource", IsApplication=true)] - -namespace AlphaTab.Samples.XamarinForms.Android -{ - - - [System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")] - public partial class Resource - { - - static Resource() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - public static void UpdateIdValues() - { - global::Xamarin.Forms.Platform.Android.Resource.Attribute.actionBarSize = global::AlphaTab.Samples.XamarinForms.Android.Resource.Attribute.actionBarSize; - } - - public partial class Animation - { - - // aapt resource value: 0x7f040000 - public const int abc_fade_in = 2130968576; - - // aapt resource value: 0x7f040001 - public const int abc_fade_out = 2130968577; - - // aapt resource value: 0x7f040002 - public const int abc_grow_fade_in_from_bottom = 2130968578; - - // aapt resource value: 0x7f040003 - public const int abc_popup_enter = 2130968579; - - // aapt resource value: 0x7f040004 - public const int abc_popup_exit = 2130968580; - - // aapt resource value: 0x7f040005 - public const int abc_shrink_fade_out_from_bottom = 2130968581; - - // aapt resource value: 0x7f040006 - public const int abc_slide_in_bottom = 2130968582; - - // aapt resource value: 0x7f040007 - public const int abc_slide_in_top = 2130968583; - - // aapt resource value: 0x7f040008 - public const int abc_slide_out_bottom = 2130968584; - - // aapt resource value: 0x7f040009 - public const int abc_slide_out_top = 2130968585; - - // aapt resource value: 0x7f04000a - public const int design_bottom_sheet_slide_in = 2130968586; - - // aapt resource value: 0x7f04000b - public const int design_bottom_sheet_slide_out = 2130968587; - - // aapt resource value: 0x7f04000c - public const int design_fab_in = 2130968588; - - // aapt resource value: 0x7f04000d - public const int design_fab_out = 2130968589; - - // aapt resource value: 0x7f04000e - public const int design_snackbar_in = 2130968590; - - // aapt resource value: 0x7f04000f - public const int design_snackbar_out = 2130968591; - - static Animation() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Animation() - { - } - } - - public partial class Animator - { - - // aapt resource value: 0x7f050000 - public const int design_appbar_state_list_animator = 2131034112; - - static Animator() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Animator() - { - } - } - - public partial class Attribute - { - - // aapt resource value: 0x7f01005c - public const int actionBarDivider = 2130772060; - - // aapt resource value: 0x7f01005d - public const int actionBarItemBackground = 2130772061; - - // aapt resource value: 0x7f010056 - public const int actionBarPopupTheme = 2130772054; - - // aapt resource value: 0x7f01005b - public const int actionBarSize = 2130772059; - - // aapt resource value: 0x7f010058 - public const int actionBarSplitStyle = 2130772056; - - // aapt resource value: 0x7f010057 - public const int actionBarStyle = 2130772055; - - // aapt resource value: 0x7f010052 - public const int actionBarTabBarStyle = 2130772050; - - // aapt resource value: 0x7f010051 - public const int actionBarTabStyle = 2130772049; - - // aapt resource value: 0x7f010053 - public const int actionBarTabTextStyle = 2130772051; - - // aapt resource value: 0x7f010059 - public const int actionBarTheme = 2130772057; - - // aapt resource value: 0x7f01005a - public const int actionBarWidgetTheme = 2130772058; - - // aapt resource value: 0x7f010077 - public const int actionButtonStyle = 2130772087; - - // aapt resource value: 0x7f010073 - public const int actionDropDownStyle = 2130772083; - - // aapt resource value: 0x7f0100c8 - public const int actionLayout = 2130772168; - - // aapt resource value: 0x7f01005e - public const int actionMenuTextAppearance = 2130772062; - - // aapt resource value: 0x7f01005f - public const int actionMenuTextColor = 2130772063; - - // aapt resource value: 0x7f010062 - public const int actionModeBackground = 2130772066; - - // aapt resource value: 0x7f010061 - public const int actionModeCloseButtonStyle = 2130772065; - - // aapt resource value: 0x7f010064 - public const int actionModeCloseDrawable = 2130772068; - - // aapt resource value: 0x7f010066 - public const int actionModeCopyDrawable = 2130772070; - - // aapt resource value: 0x7f010065 - public const int actionModeCutDrawable = 2130772069; - - // aapt resource value: 0x7f01006a - public const int actionModeFindDrawable = 2130772074; - - // aapt resource value: 0x7f010067 - public const int actionModePasteDrawable = 2130772071; - - // aapt resource value: 0x7f01006c - public const int actionModePopupWindowStyle = 2130772076; - - // aapt resource value: 0x7f010068 - public const int actionModeSelectAllDrawable = 2130772072; - - // aapt resource value: 0x7f010069 - public const int actionModeShareDrawable = 2130772073; - - // aapt resource value: 0x7f010063 - public const int actionModeSplitBackground = 2130772067; - - // aapt resource value: 0x7f010060 - public const int actionModeStyle = 2130772064; - - // aapt resource value: 0x7f01006b - public const int actionModeWebSearchDrawable = 2130772075; - - // aapt resource value: 0x7f010054 - public const int actionOverflowButtonStyle = 2130772052; - - // aapt resource value: 0x7f010055 - public const int actionOverflowMenuStyle = 2130772053; - - // aapt resource value: 0x7f0100ca - public const int actionProviderClass = 2130772170; - - // aapt resource value: 0x7f0100c9 - public const int actionViewClass = 2130772169; - - // aapt resource value: 0x7f01007f - public const int activityChooserViewStyle = 2130772095; - - // aapt resource value: 0x7f0100a3 - public const int alertDialogButtonGroupStyle = 2130772131; - - // aapt resource value: 0x7f0100a4 - public const int alertDialogCenterButtons = 2130772132; - - // aapt resource value: 0x7f0100a2 - public const int alertDialogStyle = 2130772130; - - // aapt resource value: 0x7f0100a5 - public const int alertDialogTheme = 2130772133; - - // aapt resource value: 0x7f0100b8 - public const int allowStacking = 2130772152; - - // aapt resource value: 0x7f0100b9 - public const int alpha = 2130772153; - - // aapt resource value: 0x7f0100c0 - public const int arrowHeadLength = 2130772160; - - // aapt resource value: 0x7f0100c1 - public const int arrowShaftLength = 2130772161; - - // aapt resource value: 0x7f0100aa - public const int autoCompleteTextViewStyle = 2130772138; - - // aapt resource value: 0x7f010027 - public const int background = 2130772007; - - // aapt resource value: 0x7f010029 - public const int backgroundSplit = 2130772009; - - // aapt resource value: 0x7f010028 - public const int backgroundStacked = 2130772008; - - // aapt resource value: 0x7f0100fd - public const int backgroundTint = 2130772221; - - // aapt resource value: 0x7f0100fe - public const int backgroundTintMode = 2130772222; - - // aapt resource value: 0x7f0100c2 - public const int barLength = 2130772162; - - // aapt resource value: 0x7f010128 - public const int behavior_autoHide = 2130772264; - - // aapt resource value: 0x7f010105 - public const int behavior_hideable = 2130772229; - - // aapt resource value: 0x7f010131 - public const int behavior_overlapTop = 2130772273; - - // aapt resource value: 0x7f010104 - public const int behavior_peekHeight = 2130772228; - - // aapt resource value: 0x7f010106 - public const int behavior_skipCollapsed = 2130772230; - - // aapt resource value: 0x7f010126 - public const int borderWidth = 2130772262; - - // aapt resource value: 0x7f01007c - public const int borderlessButtonStyle = 2130772092; - - // aapt resource value: 0x7f010120 - public const int bottomSheetDialogTheme = 2130772256; - - // aapt resource value: 0x7f010121 - public const int bottomSheetStyle = 2130772257; - - // aapt resource value: 0x7f010079 - public const int buttonBarButtonStyle = 2130772089; - - // aapt resource value: 0x7f0100a8 - public const int buttonBarNegativeButtonStyle = 2130772136; - - // aapt resource value: 0x7f0100a9 - public const int buttonBarNeutralButtonStyle = 2130772137; - - // aapt resource value: 0x7f0100a7 - public const int buttonBarPositiveButtonStyle = 2130772135; - - // aapt resource value: 0x7f010078 - public const int buttonBarStyle = 2130772088; - - // aapt resource value: 0x7f0100f2 - public const int buttonGravity = 2130772210; - - // aapt resource value: 0x7f01003c - public const int buttonPanelSideLayout = 2130772028; - - // aapt resource value: 0x7f0100ab - public const int buttonStyle = 2130772139; - - // aapt resource value: 0x7f0100ac - public const int buttonStyleSmall = 2130772140; - - // aapt resource value: 0x7f0100ba - public const int buttonTint = 2130772154; - - // aapt resource value: 0x7f0100bb - public const int buttonTintMode = 2130772155; - - // aapt resource value: 0x7f010010 - public const int cardBackgroundColor = 2130771984; - - // aapt resource value: 0x7f010011 - public const int cardCornerRadius = 2130771985; - - // aapt resource value: 0x7f010012 - public const int cardElevation = 2130771986; - - // aapt resource value: 0x7f010013 - public const int cardMaxElevation = 2130771987; - - // aapt resource value: 0x7f010015 - public const int cardPreventCornerOverlap = 2130771989; - - // aapt resource value: 0x7f010014 - public const int cardUseCompatPadding = 2130771988; - - // aapt resource value: 0x7f0100ad - public const int checkboxStyle = 2130772141; - - // aapt resource value: 0x7f0100ae - public const int checkedTextViewStyle = 2130772142; - - // aapt resource value: 0x7f0100d5 - public const int closeIcon = 2130772181; - - // aapt resource value: 0x7f010039 - public const int closeItemLayout = 2130772025; - - // aapt resource value: 0x7f0100f4 - public const int collapseContentDescription = 2130772212; - - // aapt resource value: 0x7f0100f3 - public const int collapseIcon = 2130772211; - - // aapt resource value: 0x7f010113 - public const int collapsedTitleGravity = 2130772243; - - // aapt resource value: 0x7f01010d - public const int collapsedTitleTextAppearance = 2130772237; - - // aapt resource value: 0x7f0100bc - public const int color = 2130772156; - - // aapt resource value: 0x7f01009a - public const int colorAccent = 2130772122; - - // aapt resource value: 0x7f0100a1 - public const int colorBackgroundFloating = 2130772129; - - // aapt resource value: 0x7f01009e - public const int colorButtonNormal = 2130772126; - - // aapt resource value: 0x7f01009c - public const int colorControlActivated = 2130772124; - - // aapt resource value: 0x7f01009d - public const int colorControlHighlight = 2130772125; - - // aapt resource value: 0x7f01009b - public const int colorControlNormal = 2130772123; - - // aapt resource value: 0x7f010098 - public const int colorPrimary = 2130772120; - - // aapt resource value: 0x7f010099 - public const int colorPrimaryDark = 2130772121; - - // aapt resource value: 0x7f01009f - public const int colorSwitchThumbNormal = 2130772127; - - // aapt resource value: 0x7f0100da - public const int commitIcon = 2130772186; - - // aapt resource value: 0x7f010032 - public const int contentInsetEnd = 2130772018; - - // aapt resource value: 0x7f010036 - public const int contentInsetEndWithActions = 2130772022; - - // aapt resource value: 0x7f010033 - public const int contentInsetLeft = 2130772019; - - // aapt resource value: 0x7f010034 - public const int contentInsetRight = 2130772020; - - // aapt resource value: 0x7f010031 - public const int contentInsetStart = 2130772017; - - // aapt resource value: 0x7f010035 - public const int contentInsetStartWithNavigation = 2130772021; - - // aapt resource value: 0x7f010016 - public const int contentPadding = 2130771990; - - // aapt resource value: 0x7f01001a - public const int contentPaddingBottom = 2130771994; - - // aapt resource value: 0x7f010017 - public const int contentPaddingLeft = 2130771991; - - // aapt resource value: 0x7f010018 - public const int contentPaddingRight = 2130771992; - - // aapt resource value: 0x7f010019 - public const int contentPaddingTop = 2130771993; - - // aapt resource value: 0x7f01010e - public const int contentScrim = 2130772238; - - // aapt resource value: 0x7f0100a0 - public const int controlBackground = 2130772128; - - // aapt resource value: 0x7f010147 - public const int counterEnabled = 2130772295; - - // aapt resource value: 0x7f010148 - public const int counterMaxLength = 2130772296; - - // aapt resource value: 0x7f01014a - public const int counterOverflowTextAppearance = 2130772298; - - // aapt resource value: 0x7f010149 - public const int counterTextAppearance = 2130772297; - - // aapt resource value: 0x7f01002a - public const int customNavigationLayout = 2130772010; - - // aapt resource value: 0x7f0100d4 - public const int defaultQueryHint = 2130772180; - - // aapt resource value: 0x7f010071 - public const int dialogPreferredPadding = 2130772081; - - // aapt resource value: 0x7f010070 - public const int dialogTheme = 2130772080; - - // aapt resource value: 0x7f010020 - public const int displayOptions = 2130772000; - - // aapt resource value: 0x7f010026 - public const int divider = 2130772006; - - // aapt resource value: 0x7f01007e - public const int dividerHorizontal = 2130772094; - - // aapt resource value: 0x7f0100c6 - public const int dividerPadding = 2130772166; - - // aapt resource value: 0x7f01007d - public const int dividerVertical = 2130772093; - - // aapt resource value: 0x7f0100be - public const int drawableSize = 2130772158; - - // aapt resource value: 0x7f01001b - public const int drawerArrowStyle = 2130771995; - - // aapt resource value: 0x7f010090 - public const int dropDownListViewStyle = 2130772112; - - // aapt resource value: 0x7f010074 - public const int dropdownListPreferredItemHeight = 2130772084; - - // aapt resource value: 0x7f010085 - public const int editTextBackground = 2130772101; - - // aapt resource value: 0x7f010084 - public const int editTextColor = 2130772100; - - // aapt resource value: 0x7f0100af - public const int editTextStyle = 2130772143; - - // aapt resource value: 0x7f010037 - public const int elevation = 2130772023; - - // aapt resource value: 0x7f010145 - public const int errorEnabled = 2130772293; - - // aapt resource value: 0x7f010146 - public const int errorTextAppearance = 2130772294; - - // aapt resource value: 0x7f01003b - public const int expandActivityOverflowButtonDrawable = 2130772027; - - // aapt resource value: 0x7f0100ff - public const int expanded = 2130772223; - - // aapt resource value: 0x7f010114 - public const int expandedTitleGravity = 2130772244; - - // aapt resource value: 0x7f010107 - public const int expandedTitleMargin = 2130772231; - - // aapt resource value: 0x7f01010b - public const int expandedTitleMarginBottom = 2130772235; - - // aapt resource value: 0x7f01010a - public const int expandedTitleMarginEnd = 2130772234; - - // aapt resource value: 0x7f010108 - public const int expandedTitleMarginStart = 2130772232; - - // aapt resource value: 0x7f010109 - public const int expandedTitleMarginTop = 2130772233; - - // aapt resource value: 0x7f01010c - public const int expandedTitleTextAppearance = 2130772236; - - // aapt resource value: 0x7f01000f - public const int externalRouteEnabledDrawable = 2130771983; - - // aapt resource value: 0x7f010124 - public const int fabSize = 2130772260; - - // aapt resource value: 0x7f010129 - public const int foregroundInsidePadding = 2130772265; - - // aapt resource value: 0x7f0100bf - public const int gapBetweenBars = 2130772159; - - // aapt resource value: 0x7f0100d6 - public const int goIcon = 2130772182; - - // aapt resource value: 0x7f01012f - public const int headerLayout = 2130772271; - - // aapt resource value: 0x7f01001c - public const int height = 2130771996; - - // aapt resource value: 0x7f010030 - public const int hideOnContentScroll = 2130772016; - - // aapt resource value: 0x7f01014b - public const int hintAnimationEnabled = 2130772299; - - // aapt resource value: 0x7f010144 - public const int hintEnabled = 2130772292; - - // aapt resource value: 0x7f010143 - public const int hintTextAppearance = 2130772291; - - // aapt resource value: 0x7f010076 - public const int homeAsUpIndicator = 2130772086; - - // aapt resource value: 0x7f01002b - public const int homeLayout = 2130772011; - - // aapt resource value: 0x7f010024 - public const int icon = 2130772004; - - // aapt resource value: 0x7f0100d2 - public const int iconifiedByDefault = 2130772178; - - // aapt resource value: 0x7f010086 - public const int imageButtonStyle = 2130772102; - - // aapt resource value: 0x7f01002d - public const int indeterminateProgressStyle = 2130772013; - - // aapt resource value: 0x7f01003a - public const int initialActivityCount = 2130772026; - - // aapt resource value: 0x7f010130 - public const int insetForeground = 2130772272; - - // aapt resource value: 0x7f01001d - public const int isLightTheme = 2130771997; - - // aapt resource value: 0x7f01012d - public const int itemBackground = 2130772269; - - // aapt resource value: 0x7f01012b - public const int itemIconTint = 2130772267; - - // aapt resource value: 0x7f01002f - public const int itemPadding = 2130772015; - - // aapt resource value: 0x7f01012e - public const int itemTextAppearance = 2130772270; - - // aapt resource value: 0x7f01012c - public const int itemTextColor = 2130772268; - - // aapt resource value: 0x7f010118 - public const int keylines = 2130772248; - - // aapt resource value: 0x7f0100d1 - public const int layout = 2130772177; - - // aapt resource value: 0x7f010000 - public const int layoutManager = 2130771968; - - // aapt resource value: 0x7f01011b - public const int layout_anchor = 2130772251; - - // aapt resource value: 0x7f01011d - public const int layout_anchorGravity = 2130772253; - - // aapt resource value: 0x7f01011a - public const int layout_behavior = 2130772250; - - // aapt resource value: 0x7f010116 - public const int layout_collapseMode = 2130772246; - - // aapt resource value: 0x7f010117 - public const int layout_collapseParallaxMultiplier = 2130772247; - - // aapt resource value: 0x7f01011f - public const int layout_dodgeInsetEdges = 2130772255; - - // aapt resource value: 0x7f01011e - public const int layout_insetEdge = 2130772254; - - // aapt resource value: 0x7f01011c - public const int layout_keyline = 2130772252; - - // aapt resource value: 0x7f010102 - public const int layout_scrollFlags = 2130772226; - - // aapt resource value: 0x7f010103 - public const int layout_scrollInterpolator = 2130772227; - - // aapt resource value: 0x7f010097 - public const int listChoiceBackgroundIndicator = 2130772119; - - // aapt resource value: 0x7f010072 - public const int listDividerAlertDialog = 2130772082; - - // aapt resource value: 0x7f010040 - public const int listItemLayout = 2130772032; - - // aapt resource value: 0x7f01003d - public const int listLayout = 2130772029; - - // aapt resource value: 0x7f0100b7 - public const int listMenuViewStyle = 2130772151; - - // aapt resource value: 0x7f010091 - public const int listPopupWindowStyle = 2130772113; - - // aapt resource value: 0x7f01008b - public const int listPreferredItemHeight = 2130772107; - - // aapt resource value: 0x7f01008d - public const int listPreferredItemHeightLarge = 2130772109; - - // aapt resource value: 0x7f01008c - public const int listPreferredItemHeightSmall = 2130772108; - - // aapt resource value: 0x7f01008e - public const int listPreferredItemPaddingLeft = 2130772110; - - // aapt resource value: 0x7f01008f - public const int listPreferredItemPaddingRight = 2130772111; - - // aapt resource value: 0x7f010025 - public const int logo = 2130772005; - - // aapt resource value: 0x7f0100f7 - public const int logoDescription = 2130772215; - - // aapt resource value: 0x7f010132 - public const int maxActionInlineWidth = 2130772274; - - // aapt resource value: 0x7f0100f1 - public const int maxButtonHeight = 2130772209; - - // aapt resource value: 0x7f0100c4 - public const int measureWithLargestChild = 2130772164; - - // aapt resource value: 0x7f010004 - public const int mediaRouteAudioTrackDrawable = 2130771972; - - // aapt resource value: 0x7f010005 - public const int mediaRouteButtonStyle = 2130771973; - - // aapt resource value: 0x7f010006 - public const int mediaRouteCloseDrawable = 2130771974; - - // aapt resource value: 0x7f010007 - public const int mediaRouteControlPanelThemeOverlay = 2130771975; - - // aapt resource value: 0x7f010008 - public const int mediaRouteDefaultIconDrawable = 2130771976; - - // aapt resource value: 0x7f010009 - public const int mediaRoutePauseDrawable = 2130771977; - - // aapt resource value: 0x7f01000a - public const int mediaRoutePlayDrawable = 2130771978; - - // aapt resource value: 0x7f01000b - public const int mediaRouteSpeakerGroupIconDrawable = 2130771979; - - // aapt resource value: 0x7f01000c - public const int mediaRouteSpeakerIconDrawable = 2130771980; - - // aapt resource value: 0x7f01000d - public const int mediaRouteTheme = 2130771981; - - // aapt resource value: 0x7f01000e - public const int mediaRouteTvIconDrawable = 2130771982; - - // aapt resource value: 0x7f01012a - public const int menu = 2130772266; - - // aapt resource value: 0x7f01003e - public const int multiChoiceItemLayout = 2130772030; - - // aapt resource value: 0x7f0100f6 - public const int navigationContentDescription = 2130772214; - - // aapt resource value: 0x7f0100f5 - public const int navigationIcon = 2130772213; - - // aapt resource value: 0x7f01001f - public const int navigationMode = 2130771999; - - // aapt resource value: 0x7f0100cd - public const int overlapAnchor = 2130772173; - - // aapt resource value: 0x7f0100cf - public const int paddingBottomNoButtons = 2130772175; - - // aapt resource value: 0x7f0100fb - public const int paddingEnd = 2130772219; - - // aapt resource value: 0x7f0100fa - public const int paddingStart = 2130772218; - - // aapt resource value: 0x7f0100d0 - public const int paddingTopNoTitle = 2130772176; - - // aapt resource value: 0x7f010094 - public const int panelBackground = 2130772116; - - // aapt resource value: 0x7f010096 - public const int panelMenuListTheme = 2130772118; - - // aapt resource value: 0x7f010095 - public const int panelMenuListWidth = 2130772117; - - // aapt resource value: 0x7f01014e - public const int passwordToggleContentDescription = 2130772302; - - // aapt resource value: 0x7f01014d - public const int passwordToggleDrawable = 2130772301; - - // aapt resource value: 0x7f01014c - public const int passwordToggleEnabled = 2130772300; - - // aapt resource value: 0x7f01014f - public const int passwordToggleTint = 2130772303; - - // aapt resource value: 0x7f010150 - public const int passwordToggleTintMode = 2130772304; - - // aapt resource value: 0x7f010082 - public const int popupMenuStyle = 2130772098; - - // aapt resource value: 0x7f010038 - public const int popupTheme = 2130772024; - - // aapt resource value: 0x7f010083 - public const int popupWindowStyle = 2130772099; - - // aapt resource value: 0x7f0100cb - public const int preserveIconSpacing = 2130772171; - - // aapt resource value: 0x7f010125 - public const int pressedTranslationZ = 2130772261; - - // aapt resource value: 0x7f01002e - public const int progressBarPadding = 2130772014; - - // aapt resource value: 0x7f01002c - public const int progressBarStyle = 2130772012; - - // aapt resource value: 0x7f0100dc - public const int queryBackground = 2130772188; - - // aapt resource value: 0x7f0100d3 - public const int queryHint = 2130772179; - - // aapt resource value: 0x7f0100b0 - public const int radioButtonStyle = 2130772144; - - // aapt resource value: 0x7f0100b1 - public const int ratingBarStyle = 2130772145; - - // aapt resource value: 0x7f0100b2 - public const int ratingBarStyleIndicator = 2130772146; - - // aapt resource value: 0x7f0100b3 - public const int ratingBarStyleSmall = 2130772147; - - // aapt resource value: 0x7f010002 - public const int reverseLayout = 2130771970; - - // aapt resource value: 0x7f010123 - public const int rippleColor = 2130772259; - - // aapt resource value: 0x7f010112 - public const int scrimAnimationDuration = 2130772242; - - // aapt resource value: 0x7f010111 - public const int scrimVisibleHeightTrigger = 2130772241; - - // aapt resource value: 0x7f0100d8 - public const int searchHintIcon = 2130772184; - - // aapt resource value: 0x7f0100d7 - public const int searchIcon = 2130772183; - - // aapt resource value: 0x7f01008a - public const int searchViewStyle = 2130772106; - - // aapt resource value: 0x7f0100b4 - public const int seekBarStyle = 2130772148; - - // aapt resource value: 0x7f01007a - public const int selectableItemBackground = 2130772090; - - // aapt resource value: 0x7f01007b - public const int selectableItemBackgroundBorderless = 2130772091; - - // aapt resource value: 0x7f0100c7 - public const int showAsAction = 2130772167; - - // aapt resource value: 0x7f0100c5 - public const int showDividers = 2130772165; - - // aapt resource value: 0x7f0100e8 - public const int showText = 2130772200; - - // aapt resource value: 0x7f010041 - public const int showTitle = 2130772033; - - // aapt resource value: 0x7f01003f - public const int singleChoiceItemLayout = 2130772031; - - // aapt resource value: 0x7f010001 - public const int spanCount = 2130771969; - - // aapt resource value: 0x7f0100bd - public const int spinBars = 2130772157; - - // aapt resource value: 0x7f010075 - public const int spinnerDropDownItemStyle = 2130772085; - - // aapt resource value: 0x7f0100b5 - public const int spinnerStyle = 2130772149; - - // aapt resource value: 0x7f0100e7 - public const int splitTrack = 2130772199; - - // aapt resource value: 0x7f010042 - public const int srcCompat = 2130772034; - - // aapt resource value: 0x7f010003 - public const int stackFromEnd = 2130771971; - - // aapt resource value: 0x7f0100ce - public const int state_above_anchor = 2130772174; - - // aapt resource value: 0x7f010100 - public const int state_collapsed = 2130772224; - - // aapt resource value: 0x7f010101 - public const int state_collapsible = 2130772225; - - // aapt resource value: 0x7f010119 - public const int statusBarBackground = 2130772249; - - // aapt resource value: 0x7f01010f - public const int statusBarScrim = 2130772239; - - // aapt resource value: 0x7f0100cc - public const int subMenuArrow = 2130772172; - - // aapt resource value: 0x7f0100dd - public const int submitBackground = 2130772189; - - // aapt resource value: 0x7f010021 - public const int subtitle = 2130772001; - - // aapt resource value: 0x7f0100ea - public const int subtitleTextAppearance = 2130772202; - - // aapt resource value: 0x7f0100f9 - public const int subtitleTextColor = 2130772217; - - // aapt resource value: 0x7f010023 - public const int subtitleTextStyle = 2130772003; - - // aapt resource value: 0x7f0100db - public const int suggestionRowLayout = 2130772187; - - // aapt resource value: 0x7f0100e5 - public const int switchMinWidth = 2130772197; - - // aapt resource value: 0x7f0100e6 - public const int switchPadding = 2130772198; - - // aapt resource value: 0x7f0100b6 - public const int switchStyle = 2130772150; - - // aapt resource value: 0x7f0100e4 - public const int switchTextAppearance = 2130772196; - - // aapt resource value: 0x7f010136 - public const int tabBackground = 2130772278; - - // aapt resource value: 0x7f010135 - public const int tabContentStart = 2130772277; - - // aapt resource value: 0x7f010138 - public const int tabGravity = 2130772280; - - // aapt resource value: 0x7f010133 - public const int tabIndicatorColor = 2130772275; - - // aapt resource value: 0x7f010134 - public const int tabIndicatorHeight = 2130772276; - - // aapt resource value: 0x7f01013a - public const int tabMaxWidth = 2130772282; - - // aapt resource value: 0x7f010139 - public const int tabMinWidth = 2130772281; - - // aapt resource value: 0x7f010137 - public const int tabMode = 2130772279; - - // aapt resource value: 0x7f010142 - public const int tabPadding = 2130772290; - - // aapt resource value: 0x7f010141 - public const int tabPaddingBottom = 2130772289; - - // aapt resource value: 0x7f010140 - public const int tabPaddingEnd = 2130772288; - - // aapt resource value: 0x7f01013e - public const int tabPaddingStart = 2130772286; - - // aapt resource value: 0x7f01013f - public const int tabPaddingTop = 2130772287; - - // aapt resource value: 0x7f01013d - public const int tabSelectedTextColor = 2130772285; - - // aapt resource value: 0x7f01013b - public const int tabTextAppearance = 2130772283; - - // aapt resource value: 0x7f01013c - public const int tabTextColor = 2130772284; - - // aapt resource value: 0x7f010046 - public const int textAllCaps = 2130772038; - - // aapt resource value: 0x7f01006d - public const int textAppearanceLargePopupMenu = 2130772077; - - // aapt resource value: 0x7f010092 - public const int textAppearanceListItem = 2130772114; - - // aapt resource value: 0x7f010093 - public const int textAppearanceListItemSmall = 2130772115; - - // aapt resource value: 0x7f01006f - public const int textAppearancePopupMenuHeader = 2130772079; - - // aapt resource value: 0x7f010088 - public const int textAppearanceSearchResultSubtitle = 2130772104; - - // aapt resource value: 0x7f010087 - public const int textAppearanceSearchResultTitle = 2130772103; - - // aapt resource value: 0x7f01006e - public const int textAppearanceSmallPopupMenu = 2130772078; - - // aapt resource value: 0x7f0100a6 - public const int textColorAlertDialogListItem = 2130772134; - - // aapt resource value: 0x7f010122 - public const int textColorError = 2130772258; - - // aapt resource value: 0x7f010089 - public const int textColorSearchUrl = 2130772105; - - // aapt resource value: 0x7f0100fc - public const int theme = 2130772220; - - // aapt resource value: 0x7f0100c3 - public const int thickness = 2130772163; - - // aapt resource value: 0x7f0100e3 - public const int thumbTextPadding = 2130772195; - - // aapt resource value: 0x7f0100de - public const int thumbTint = 2130772190; - - // aapt resource value: 0x7f0100df - public const int thumbTintMode = 2130772191; - - // aapt resource value: 0x7f010043 - public const int tickMark = 2130772035; - - // aapt resource value: 0x7f010044 - public const int tickMarkTint = 2130772036; - - // aapt resource value: 0x7f010045 - public const int tickMarkTintMode = 2130772037; - - // aapt resource value: 0x7f01001e - public const int title = 2130771998; - - // aapt resource value: 0x7f010115 - public const int titleEnabled = 2130772245; - - // aapt resource value: 0x7f0100eb - public const int titleMargin = 2130772203; - - // aapt resource value: 0x7f0100ef - public const int titleMarginBottom = 2130772207; - - // aapt resource value: 0x7f0100ed - public const int titleMarginEnd = 2130772205; - - // aapt resource value: 0x7f0100ec - public const int titleMarginStart = 2130772204; - - // aapt resource value: 0x7f0100ee - public const int titleMarginTop = 2130772206; - - // aapt resource value: 0x7f0100f0 - public const int titleMargins = 2130772208; - - // aapt resource value: 0x7f0100e9 - public const int titleTextAppearance = 2130772201; - - // aapt resource value: 0x7f0100f8 - public const int titleTextColor = 2130772216; - - // aapt resource value: 0x7f010022 - public const int titleTextStyle = 2130772002; - - // aapt resource value: 0x7f010110 - public const int toolbarId = 2130772240; - - // aapt resource value: 0x7f010081 - public const int toolbarNavigationButtonStyle = 2130772097; - - // aapt resource value: 0x7f010080 - public const int toolbarStyle = 2130772096; - - // aapt resource value: 0x7f0100e0 - public const int track = 2130772192; - - // aapt resource value: 0x7f0100e1 - public const int trackTint = 2130772193; - - // aapt resource value: 0x7f0100e2 - public const int trackTintMode = 2130772194; - - // aapt resource value: 0x7f010127 - public const int useCompatPadding = 2130772263; - - // aapt resource value: 0x7f0100d9 - public const int voiceIcon = 2130772185; - - // aapt resource value: 0x7f010047 - public const int windowActionBar = 2130772039; - - // aapt resource value: 0x7f010049 - public const int windowActionBarOverlay = 2130772041; - - // aapt resource value: 0x7f01004a - public const int windowActionModeOverlay = 2130772042; - - // aapt resource value: 0x7f01004e - public const int windowFixedHeightMajor = 2130772046; - - // aapt resource value: 0x7f01004c - public const int windowFixedHeightMinor = 2130772044; - - // aapt resource value: 0x7f01004b - public const int windowFixedWidthMajor = 2130772043; - - // aapt resource value: 0x7f01004d - public const int windowFixedWidthMinor = 2130772045; - - // aapt resource value: 0x7f01004f - public const int windowMinWidthMajor = 2130772047; - - // aapt resource value: 0x7f010050 - public const int windowMinWidthMinor = 2130772048; - - // aapt resource value: 0x7f010048 - public const int windowNoTitle = 2130772040; - - static Attribute() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Attribute() - { - } - } - - public partial class Boolean - { - - // aapt resource value: 0x7f0d0000 - public const int abc_action_bar_embed_tabs = 2131558400; - - // aapt resource value: 0x7f0d0001 - public const int abc_allow_stacked_button_bar = 2131558401; - - // aapt resource value: 0x7f0d0002 - public const int abc_config_actionMenuItemAllCaps = 2131558402; - - // aapt resource value: 0x7f0d0003 - public const int abc_config_closeDialogWhenTouchOutside = 2131558403; - - // aapt resource value: 0x7f0d0004 - public const int abc_config_showMenuShortcutsWhenKeyboardPresent = 2131558404; - - static Boolean() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Boolean() - { - } - } - - public partial class Color - { - - // aapt resource value: 0x7f0c004a - public const int abc_background_cache_hint_selector_material_dark = 2131492938; - - // aapt resource value: 0x7f0c004b - public const int abc_background_cache_hint_selector_material_light = 2131492939; - - // aapt resource value: 0x7f0c004c - public const int abc_btn_colored_borderless_text_material = 2131492940; - - // aapt resource value: 0x7f0c004d - public const int abc_btn_colored_text_material = 2131492941; - - // aapt resource value: 0x7f0c004e - public const int abc_color_highlight_material = 2131492942; - - // aapt resource value: 0x7f0c004f - public const int abc_hint_foreground_material_dark = 2131492943; - - // aapt resource value: 0x7f0c0050 - public const int abc_hint_foreground_material_light = 2131492944; - - // aapt resource value: 0x7f0c0005 - public const int abc_input_method_navigation_guard = 2131492869; - - // aapt resource value: 0x7f0c0051 - public const int abc_primary_text_disable_only_material_dark = 2131492945; - - // aapt resource value: 0x7f0c0052 - public const int abc_primary_text_disable_only_material_light = 2131492946; - - // aapt resource value: 0x7f0c0053 - public const int abc_primary_text_material_dark = 2131492947; - - // aapt resource value: 0x7f0c0054 - public const int abc_primary_text_material_light = 2131492948; - - // aapt resource value: 0x7f0c0055 - public const int abc_search_url_text = 2131492949; - - // aapt resource value: 0x7f0c0006 - public const int abc_search_url_text_normal = 2131492870; - - // aapt resource value: 0x7f0c0007 - public const int abc_search_url_text_pressed = 2131492871; - - // aapt resource value: 0x7f0c0008 - public const int abc_search_url_text_selected = 2131492872; - - // aapt resource value: 0x7f0c0056 - public const int abc_secondary_text_material_dark = 2131492950; - - // aapt resource value: 0x7f0c0057 - public const int abc_secondary_text_material_light = 2131492951; - - // aapt resource value: 0x7f0c0058 - public const int abc_tint_btn_checkable = 2131492952; - - // aapt resource value: 0x7f0c0059 - public const int abc_tint_default = 2131492953; - - // aapt resource value: 0x7f0c005a - public const int abc_tint_edittext = 2131492954; - - // aapt resource value: 0x7f0c005b - public const int abc_tint_seek_thumb = 2131492955; - - // aapt resource value: 0x7f0c005c - public const int abc_tint_spinner = 2131492956; - - // aapt resource value: 0x7f0c005d - public const int abc_tint_switch_thumb = 2131492957; - - // aapt resource value: 0x7f0c005e - public const int abc_tint_switch_track = 2131492958; - - // aapt resource value: 0x7f0c0009 - public const int accent_material_dark = 2131492873; - - // aapt resource value: 0x7f0c000a - public const int accent_material_light = 2131492874; - - // aapt resource value: 0x7f0c000b - public const int background_floating_material_dark = 2131492875; - - // aapt resource value: 0x7f0c000c - public const int background_floating_material_light = 2131492876; - - // aapt resource value: 0x7f0c000d - public const int background_material_dark = 2131492877; - - // aapt resource value: 0x7f0c000e - public const int background_material_light = 2131492878; - - // aapt resource value: 0x7f0c000f - public const int bright_foreground_disabled_material_dark = 2131492879; - - // aapt resource value: 0x7f0c0010 - public const int bright_foreground_disabled_material_light = 2131492880; - - // aapt resource value: 0x7f0c0011 - public const int bright_foreground_inverse_material_dark = 2131492881; - - // aapt resource value: 0x7f0c0012 - public const int bright_foreground_inverse_material_light = 2131492882; - - // aapt resource value: 0x7f0c0013 - public const int bright_foreground_material_dark = 2131492883; - - // aapt resource value: 0x7f0c0014 - public const int bright_foreground_material_light = 2131492884; - - // aapt resource value: 0x7f0c0015 - public const int button_material_dark = 2131492885; - - // aapt resource value: 0x7f0c0016 - public const int button_material_light = 2131492886; - - // aapt resource value: 0x7f0c0000 - public const int cardview_dark_background = 2131492864; - - // aapt resource value: 0x7f0c0001 - public const int cardview_light_background = 2131492865; - - // aapt resource value: 0x7f0c0002 - public const int cardview_shadow_end_color = 2131492866; - - // aapt resource value: 0x7f0c0003 - public const int cardview_shadow_start_color = 2131492867; - - // aapt resource value: 0x7f0c003f - public const int design_bottom_navigation_shadow_color = 2131492927; - - // aapt resource value: 0x7f0c005f - public const int design_error = 2131492959; - - // aapt resource value: 0x7f0c0040 - public const int design_fab_shadow_end_color = 2131492928; - - // aapt resource value: 0x7f0c0041 - public const int design_fab_shadow_mid_color = 2131492929; - - // aapt resource value: 0x7f0c0042 - public const int design_fab_shadow_start_color = 2131492930; - - // aapt resource value: 0x7f0c0043 - public const int design_fab_stroke_end_inner_color = 2131492931; - - // aapt resource value: 0x7f0c0044 - public const int design_fab_stroke_end_outer_color = 2131492932; - - // aapt resource value: 0x7f0c0045 - public const int design_fab_stroke_top_inner_color = 2131492933; - - // aapt resource value: 0x7f0c0046 - public const int design_fab_stroke_top_outer_color = 2131492934; - - // aapt resource value: 0x7f0c0047 - public const int design_snackbar_background_color = 2131492935; - - // aapt resource value: 0x7f0c0048 - public const int design_textinput_error_color_dark = 2131492936; - - // aapt resource value: 0x7f0c0049 - public const int design_textinput_error_color_light = 2131492937; - - // aapt resource value: 0x7f0c0060 - public const int design_tint_password_toggle = 2131492960; - - // aapt resource value: 0x7f0c0017 - public const int dim_foreground_disabled_material_dark = 2131492887; - - // aapt resource value: 0x7f0c0018 - public const int dim_foreground_disabled_material_light = 2131492888; - - // aapt resource value: 0x7f0c0019 - public const int dim_foreground_material_dark = 2131492889; - - // aapt resource value: 0x7f0c001a - public const int dim_foreground_material_light = 2131492890; - - // aapt resource value: 0x7f0c001b - public const int foreground_material_dark = 2131492891; - - // aapt resource value: 0x7f0c001c - public const int foreground_material_light = 2131492892; - - // aapt resource value: 0x7f0c001d - public const int highlighted_text_material_dark = 2131492893; - - // aapt resource value: 0x7f0c001e - public const int highlighted_text_material_light = 2131492894; - - // aapt resource value: 0x7f0c001f - public const int material_blue_grey_800 = 2131492895; - - // aapt resource value: 0x7f0c0020 - public const int material_blue_grey_900 = 2131492896; - - // aapt resource value: 0x7f0c0021 - public const int material_blue_grey_950 = 2131492897; - - // aapt resource value: 0x7f0c0022 - public const int material_deep_teal_200 = 2131492898; - - // aapt resource value: 0x7f0c0023 - public const int material_deep_teal_500 = 2131492899; - - // aapt resource value: 0x7f0c0024 - public const int material_grey_100 = 2131492900; - - // aapt resource value: 0x7f0c0025 - public const int material_grey_300 = 2131492901; - - // aapt resource value: 0x7f0c0026 - public const int material_grey_50 = 2131492902; - - // aapt resource value: 0x7f0c0027 - public const int material_grey_600 = 2131492903; - - // aapt resource value: 0x7f0c0028 - public const int material_grey_800 = 2131492904; - - // aapt resource value: 0x7f0c0029 - public const int material_grey_850 = 2131492905; - - // aapt resource value: 0x7f0c002a - public const int material_grey_900 = 2131492906; - - // aapt resource value: 0x7f0c0004 - public const int notification_action_color_filter = 2131492868; - - // aapt resource value: 0x7f0c002b - public const int notification_icon_bg_color = 2131492907; - - // aapt resource value: 0x7f0c002c - public const int notification_material_background_media_default_color = 2131492908; - - // aapt resource value: 0x7f0c002d - public const int primary_dark_material_dark = 2131492909; - - // aapt resource value: 0x7f0c002e - public const int primary_dark_material_light = 2131492910; - - // aapt resource value: 0x7f0c002f - public const int primary_material_dark = 2131492911; - - // aapt resource value: 0x7f0c0030 - public const int primary_material_light = 2131492912; - - // aapt resource value: 0x7f0c0031 - public const int primary_text_default_material_dark = 2131492913; - - // aapt resource value: 0x7f0c0032 - public const int primary_text_default_material_light = 2131492914; - - // aapt resource value: 0x7f0c0033 - public const int primary_text_disabled_material_dark = 2131492915; - - // aapt resource value: 0x7f0c0034 - public const int primary_text_disabled_material_light = 2131492916; - - // aapt resource value: 0x7f0c0035 - public const int ripple_material_dark = 2131492917; - - // aapt resource value: 0x7f0c0036 - public const int ripple_material_light = 2131492918; - - // aapt resource value: 0x7f0c0037 - public const int secondary_text_default_material_dark = 2131492919; - - // aapt resource value: 0x7f0c0038 - public const int secondary_text_default_material_light = 2131492920; - - // aapt resource value: 0x7f0c0039 - public const int secondary_text_disabled_material_dark = 2131492921; - - // aapt resource value: 0x7f0c003a - public const int secondary_text_disabled_material_light = 2131492922; - - // aapt resource value: 0x7f0c003b - public const int switch_thumb_disabled_material_dark = 2131492923; - - // aapt resource value: 0x7f0c003c - public const int switch_thumb_disabled_material_light = 2131492924; - - // aapt resource value: 0x7f0c0061 - public const int switch_thumb_material_dark = 2131492961; - - // aapt resource value: 0x7f0c0062 - public const int switch_thumb_material_light = 2131492962; - - // aapt resource value: 0x7f0c003d - public const int switch_thumb_normal_material_dark = 2131492925; - - // aapt resource value: 0x7f0c003e - public const int switch_thumb_normal_material_light = 2131492926; - - static Color() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Color() - { - } - } - - public partial class Dimension - { - - // aapt resource value: 0x7f070018 - public const int abc_action_bar_content_inset_material = 2131165208; - - // aapt resource value: 0x7f070019 - public const int abc_action_bar_content_inset_with_nav = 2131165209; - - // aapt resource value: 0x7f07000d - public const int abc_action_bar_default_height_material = 2131165197; - - // aapt resource value: 0x7f07001a - public const int abc_action_bar_default_padding_end_material = 2131165210; - - // aapt resource value: 0x7f07001b - public const int abc_action_bar_default_padding_start_material = 2131165211; - - // aapt resource value: 0x7f070021 - public const int abc_action_bar_elevation_material = 2131165217; - - // aapt resource value: 0x7f070022 - public const int abc_action_bar_icon_vertical_padding_material = 2131165218; - - // aapt resource value: 0x7f070023 - public const int abc_action_bar_overflow_padding_end_material = 2131165219; - - // aapt resource value: 0x7f070024 - public const int abc_action_bar_overflow_padding_start_material = 2131165220; - - // aapt resource value: 0x7f07000e - public const int abc_action_bar_progress_bar_size = 2131165198; - - // aapt resource value: 0x7f070025 - public const int abc_action_bar_stacked_max_height = 2131165221; - - // aapt resource value: 0x7f070026 - public const int abc_action_bar_stacked_tab_max_width = 2131165222; - - // aapt resource value: 0x7f070027 - public const int abc_action_bar_subtitle_bottom_margin_material = 2131165223; - - // aapt resource value: 0x7f070028 - public const int abc_action_bar_subtitle_top_margin_material = 2131165224; - - // aapt resource value: 0x7f070029 - public const int abc_action_button_min_height_material = 2131165225; - - // aapt resource value: 0x7f07002a - public const int abc_action_button_min_width_material = 2131165226; - - // aapt resource value: 0x7f07002b - public const int abc_action_button_min_width_overflow_material = 2131165227; - - // aapt resource value: 0x7f07000c - public const int abc_alert_dialog_button_bar_height = 2131165196; - - // aapt resource value: 0x7f07002c - public const int abc_button_inset_horizontal_material = 2131165228; - - // aapt resource value: 0x7f07002d - public const int abc_button_inset_vertical_material = 2131165229; - - // aapt resource value: 0x7f07002e - public const int abc_button_padding_horizontal_material = 2131165230; - - // aapt resource value: 0x7f07002f - public const int abc_button_padding_vertical_material = 2131165231; - - // aapt resource value: 0x7f070030 - public const int abc_cascading_menus_min_smallest_width = 2131165232; - - // aapt resource value: 0x7f070011 - public const int abc_config_prefDialogWidth = 2131165201; - - // aapt resource value: 0x7f070031 - public const int abc_control_corner_material = 2131165233; - - // aapt resource value: 0x7f070032 - public const int abc_control_inset_material = 2131165234; - - // aapt resource value: 0x7f070033 - public const int abc_control_padding_material = 2131165235; - - // aapt resource value: 0x7f070012 - public const int abc_dialog_fixed_height_major = 2131165202; - - // aapt resource value: 0x7f070013 - public const int abc_dialog_fixed_height_minor = 2131165203; - - // aapt resource value: 0x7f070014 - public const int abc_dialog_fixed_width_major = 2131165204; - - // aapt resource value: 0x7f070015 - public const int abc_dialog_fixed_width_minor = 2131165205; - - // aapt resource value: 0x7f070034 - public const int abc_dialog_list_padding_bottom_no_buttons = 2131165236; - - // aapt resource value: 0x7f070035 - public const int abc_dialog_list_padding_top_no_title = 2131165237; - - // aapt resource value: 0x7f070016 - public const int abc_dialog_min_width_major = 2131165206; - - // aapt resource value: 0x7f070017 - public const int abc_dialog_min_width_minor = 2131165207; - - // aapt resource value: 0x7f070036 - public const int abc_dialog_padding_material = 2131165238; - - // aapt resource value: 0x7f070037 - public const int abc_dialog_padding_top_material = 2131165239; - - // aapt resource value: 0x7f070038 - public const int abc_dialog_title_divider_material = 2131165240; - - // aapt resource value: 0x7f070039 - public const int abc_disabled_alpha_material_dark = 2131165241; - - // aapt resource value: 0x7f07003a - public const int abc_disabled_alpha_material_light = 2131165242; - - // aapt resource value: 0x7f07003b - public const int abc_dropdownitem_icon_width = 2131165243; - - // aapt resource value: 0x7f07003c - public const int abc_dropdownitem_text_padding_left = 2131165244; - - // aapt resource value: 0x7f07003d - public const int abc_dropdownitem_text_padding_right = 2131165245; - - // aapt resource value: 0x7f07003e - public const int abc_edit_text_inset_bottom_material = 2131165246; - - // aapt resource value: 0x7f07003f - public const int abc_edit_text_inset_horizontal_material = 2131165247; - - // aapt resource value: 0x7f070040 - public const int abc_edit_text_inset_top_material = 2131165248; - - // aapt resource value: 0x7f070041 - public const int abc_floating_window_z = 2131165249; - - // aapt resource value: 0x7f070042 - public const int abc_list_item_padding_horizontal_material = 2131165250; - - // aapt resource value: 0x7f070043 - public const int abc_panel_menu_list_width = 2131165251; - - // aapt resource value: 0x7f070044 - public const int abc_progress_bar_height_material = 2131165252; - - // aapt resource value: 0x7f070045 - public const int abc_search_view_preferred_height = 2131165253; - - // aapt resource value: 0x7f070046 - public const int abc_search_view_preferred_width = 2131165254; - - // aapt resource value: 0x7f070047 - public const int abc_seekbar_track_background_height_material = 2131165255; - - // aapt resource value: 0x7f070048 - public const int abc_seekbar_track_progress_height_material = 2131165256; - - // aapt resource value: 0x7f070049 - public const int abc_select_dialog_padding_start_material = 2131165257; - - // aapt resource value: 0x7f07001d - public const int abc_switch_padding = 2131165213; - - // aapt resource value: 0x7f07004a - public const int abc_text_size_body_1_material = 2131165258; - - // aapt resource value: 0x7f07004b - public const int abc_text_size_body_2_material = 2131165259; - - // aapt resource value: 0x7f07004c - public const int abc_text_size_button_material = 2131165260; - - // aapt resource value: 0x7f07004d - public const int abc_text_size_caption_material = 2131165261; - - // aapt resource value: 0x7f07004e - public const int abc_text_size_display_1_material = 2131165262; - - // aapt resource value: 0x7f07004f - public const int abc_text_size_display_2_material = 2131165263; - - // aapt resource value: 0x7f070050 - public const int abc_text_size_display_3_material = 2131165264; - - // aapt resource value: 0x7f070051 - public const int abc_text_size_display_4_material = 2131165265; - - // aapt resource value: 0x7f070052 - public const int abc_text_size_headline_material = 2131165266; - - // aapt resource value: 0x7f070053 - public const int abc_text_size_large_material = 2131165267; - - // aapt resource value: 0x7f070054 - public const int abc_text_size_medium_material = 2131165268; - - // aapt resource value: 0x7f070055 - public const int abc_text_size_menu_header_material = 2131165269; - - // aapt resource value: 0x7f070056 - public const int abc_text_size_menu_material = 2131165270; - - // aapt resource value: 0x7f070057 - public const int abc_text_size_small_material = 2131165271; - - // aapt resource value: 0x7f070058 - public const int abc_text_size_subhead_material = 2131165272; - - // aapt resource value: 0x7f07000f - public const int abc_text_size_subtitle_material_toolbar = 2131165199; - - // aapt resource value: 0x7f070059 - public const int abc_text_size_title_material = 2131165273; - - // aapt resource value: 0x7f070010 - public const int abc_text_size_title_material_toolbar = 2131165200; - - // aapt resource value: 0x7f070009 - public const int cardview_compat_inset_shadow = 2131165193; - - // aapt resource value: 0x7f07000a - public const int cardview_default_elevation = 2131165194; - - // aapt resource value: 0x7f07000b - public const int cardview_default_radius = 2131165195; - - // aapt resource value: 0x7f070076 - public const int design_appbar_elevation = 2131165302; - - // aapt resource value: 0x7f070077 - public const int design_bottom_navigation_active_item_max_width = 2131165303; - - // aapt resource value: 0x7f070078 - public const int design_bottom_navigation_active_text_size = 2131165304; - - // aapt resource value: 0x7f070079 - public const int design_bottom_navigation_elevation = 2131165305; - - // aapt resource value: 0x7f07007a - public const int design_bottom_navigation_height = 2131165306; - - // aapt resource value: 0x7f07007b - public const int design_bottom_navigation_item_max_width = 2131165307; - - // aapt resource value: 0x7f07007c - public const int design_bottom_navigation_item_min_width = 2131165308; - - // aapt resource value: 0x7f07007d - public const int design_bottom_navigation_margin = 2131165309; - - // aapt resource value: 0x7f07007e - public const int design_bottom_navigation_shadow_height = 2131165310; - - // aapt resource value: 0x7f07007f - public const int design_bottom_navigation_text_size = 2131165311; - - // aapt resource value: 0x7f070080 - public const int design_bottom_sheet_modal_elevation = 2131165312; - - // aapt resource value: 0x7f070081 - public const int design_bottom_sheet_peek_height_min = 2131165313; - - // aapt resource value: 0x7f070082 - public const int design_fab_border_width = 2131165314; - - // aapt resource value: 0x7f070083 - public const int design_fab_elevation = 2131165315; - - // aapt resource value: 0x7f070084 - public const int design_fab_image_size = 2131165316; - - // aapt resource value: 0x7f070085 - public const int design_fab_size_mini = 2131165317; - - // aapt resource value: 0x7f070086 - public const int design_fab_size_normal = 2131165318; - - // aapt resource value: 0x7f070087 - public const int design_fab_translation_z_pressed = 2131165319; - - // aapt resource value: 0x7f070088 - public const int design_navigation_elevation = 2131165320; - - // aapt resource value: 0x7f070089 - public const int design_navigation_icon_padding = 2131165321; - - // aapt resource value: 0x7f07008a - public const int design_navigation_icon_size = 2131165322; - - // aapt resource value: 0x7f07006e - public const int design_navigation_max_width = 2131165294; - - // aapt resource value: 0x7f07008b - public const int design_navigation_padding_bottom = 2131165323; - - // aapt resource value: 0x7f07008c - public const int design_navigation_separator_vertical_padding = 2131165324; - - // aapt resource value: 0x7f07006f - public const int design_snackbar_action_inline_max_width = 2131165295; - - // aapt resource value: 0x7f070070 - public const int design_snackbar_background_corner_radius = 2131165296; - - // aapt resource value: 0x7f07008d - public const int design_snackbar_elevation = 2131165325; - - // aapt resource value: 0x7f070071 - public const int design_snackbar_extra_spacing_horizontal = 2131165297; - - // aapt resource value: 0x7f070072 - public const int design_snackbar_max_width = 2131165298; - - // aapt resource value: 0x7f070073 - public const int design_snackbar_min_width = 2131165299; - - // aapt resource value: 0x7f07008e - public const int design_snackbar_padding_horizontal = 2131165326; - - // aapt resource value: 0x7f07008f - public const int design_snackbar_padding_vertical = 2131165327; - - // aapt resource value: 0x7f070074 - public const int design_snackbar_padding_vertical_2lines = 2131165300; - - // aapt resource value: 0x7f070090 - public const int design_snackbar_text_size = 2131165328; - - // aapt resource value: 0x7f070091 - public const int design_tab_max_width = 2131165329; - - // aapt resource value: 0x7f070075 - public const int design_tab_scrollable_min_width = 2131165301; - - // aapt resource value: 0x7f070092 - public const int design_tab_text_size = 2131165330; - - // aapt resource value: 0x7f070093 - public const int design_tab_text_size_2line = 2131165331; - - // aapt resource value: 0x7f07005a - public const int disabled_alpha_material_dark = 2131165274; - - // aapt resource value: 0x7f07005b - public const int disabled_alpha_material_light = 2131165275; - - // aapt resource value: 0x7f07005c - public const int highlight_alpha_material_colored = 2131165276; - - // aapt resource value: 0x7f07005d - public const int highlight_alpha_material_dark = 2131165277; - - // aapt resource value: 0x7f07005e - public const int highlight_alpha_material_light = 2131165278; - - // aapt resource value: 0x7f07005f - public const int hint_alpha_material_dark = 2131165279; - - // aapt resource value: 0x7f070060 - public const int hint_alpha_material_light = 2131165280; - - // aapt resource value: 0x7f070061 - public const int hint_pressed_alpha_material_dark = 2131165281; - - // aapt resource value: 0x7f070062 - public const int hint_pressed_alpha_material_light = 2131165282; - - // aapt resource value: 0x7f070000 - public const int item_touch_helper_max_drag_scroll_per_frame = 2131165184; - - // aapt resource value: 0x7f070001 - public const int item_touch_helper_swipe_escape_max_velocity = 2131165185; - - // aapt resource value: 0x7f070002 - public const int item_touch_helper_swipe_escape_velocity = 2131165186; - - // aapt resource value: 0x7f070003 - public const int mr_controller_volume_group_list_item_height = 2131165187; - - // aapt resource value: 0x7f070004 - public const int mr_controller_volume_group_list_item_icon_size = 2131165188; - - // aapt resource value: 0x7f070005 - public const int mr_controller_volume_group_list_max_height = 2131165189; - - // aapt resource value: 0x7f070008 - public const int mr_controller_volume_group_list_padding_top = 2131165192; - - // aapt resource value: 0x7f070006 - public const int mr_dialog_fixed_width_major = 2131165190; - - // aapt resource value: 0x7f070007 - public const int mr_dialog_fixed_width_minor = 2131165191; - - // aapt resource value: 0x7f070063 - public const int notification_action_icon_size = 2131165283; - - // aapt resource value: 0x7f070064 - public const int notification_action_text_size = 2131165284; - - // aapt resource value: 0x7f070065 - public const int notification_big_circle_margin = 2131165285; - - // aapt resource value: 0x7f07001e - public const int notification_content_margin_start = 2131165214; - - // aapt resource value: 0x7f070066 - public const int notification_large_icon_height = 2131165286; - - // aapt resource value: 0x7f070067 - public const int notification_large_icon_width = 2131165287; - - // aapt resource value: 0x7f07001f - public const int notification_main_column_padding_top = 2131165215; - - // aapt resource value: 0x7f070020 - public const int notification_media_narrow_margin = 2131165216; - - // aapt resource value: 0x7f070068 - public const int notification_right_icon_size = 2131165288; - - // aapt resource value: 0x7f07001c - public const int notification_right_side_padding_top = 2131165212; - - // aapt resource value: 0x7f070069 - public const int notification_small_icon_background_padding = 2131165289; - - // aapt resource value: 0x7f07006a - public const int notification_small_icon_size_as_large = 2131165290; - - // aapt resource value: 0x7f07006b - public const int notification_subtext_size = 2131165291; - - // aapt resource value: 0x7f07006c - public const int notification_top_pad = 2131165292; - - // aapt resource value: 0x7f07006d - public const int notification_top_pad_large_text = 2131165293; - - static Dimension() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Dimension() - { - } - } - - public partial class Drawable - { - - // aapt resource value: 0x7f020000 - public const int abc_ab_share_pack_mtrl_alpha = 2130837504; - - // aapt resource value: 0x7f020001 - public const int abc_action_bar_item_background_material = 2130837505; - - // aapt resource value: 0x7f020002 - public const int abc_btn_borderless_material = 2130837506; - - // aapt resource value: 0x7f020003 - public const int abc_btn_check_material = 2130837507; - - // aapt resource value: 0x7f020004 - public const int abc_btn_check_to_on_mtrl_000 = 2130837508; - - // aapt resource value: 0x7f020005 - public const int abc_btn_check_to_on_mtrl_015 = 2130837509; - - // aapt resource value: 0x7f020006 - public const int abc_btn_colored_material = 2130837510; - - // aapt resource value: 0x7f020007 - public const int abc_btn_default_mtrl_shape = 2130837511; - - // aapt resource value: 0x7f020008 - public const int abc_btn_radio_material = 2130837512; - - // aapt resource value: 0x7f020009 - public const int abc_btn_radio_to_on_mtrl_000 = 2130837513; - - // aapt resource value: 0x7f02000a - public const int abc_btn_radio_to_on_mtrl_015 = 2130837514; - - // aapt resource value: 0x7f02000b - public const int abc_btn_switch_to_on_mtrl_00001 = 2130837515; - - // aapt resource value: 0x7f02000c - public const int abc_btn_switch_to_on_mtrl_00012 = 2130837516; - - // aapt resource value: 0x7f02000d - public const int abc_cab_background_internal_bg = 2130837517; - - // aapt resource value: 0x7f02000e - public const int abc_cab_background_top_material = 2130837518; - - // aapt resource value: 0x7f02000f - public const int abc_cab_background_top_mtrl_alpha = 2130837519; - - // aapt resource value: 0x7f020010 - public const int abc_control_background_material = 2130837520; - - // aapt resource value: 0x7f020011 - public const int abc_dialog_material_background = 2130837521; - - // aapt resource value: 0x7f020012 - public const int abc_edit_text_material = 2130837522; - - // aapt resource value: 0x7f020013 - public const int abc_ic_ab_back_material = 2130837523; - - // aapt resource value: 0x7f020014 - public const int abc_ic_arrow_drop_right_black_24dp = 2130837524; - - // aapt resource value: 0x7f020015 - public const int abc_ic_clear_material = 2130837525; - - // aapt resource value: 0x7f020016 - public const int abc_ic_commit_search_api_mtrl_alpha = 2130837526; - - // aapt resource value: 0x7f020017 - public const int abc_ic_go_search_api_material = 2130837527; - - // aapt resource value: 0x7f020018 - public const int abc_ic_menu_copy_mtrl_am_alpha = 2130837528; - - // aapt resource value: 0x7f020019 - public const int abc_ic_menu_cut_mtrl_alpha = 2130837529; - - // aapt resource value: 0x7f02001a - public const int abc_ic_menu_overflow_material = 2130837530; - - // aapt resource value: 0x7f02001b - public const int abc_ic_menu_paste_mtrl_am_alpha = 2130837531; - - // aapt resource value: 0x7f02001c - public const int abc_ic_menu_selectall_mtrl_alpha = 2130837532; - - // aapt resource value: 0x7f02001d - public const int abc_ic_menu_share_mtrl_alpha = 2130837533; - - // aapt resource value: 0x7f02001e - public const int abc_ic_search_api_material = 2130837534; - - // aapt resource value: 0x7f02001f - public const int abc_ic_star_black_16dp = 2130837535; - - // aapt resource value: 0x7f020020 - public const int abc_ic_star_black_36dp = 2130837536; - - // aapt resource value: 0x7f020021 - public const int abc_ic_star_black_48dp = 2130837537; - - // aapt resource value: 0x7f020022 - public const int abc_ic_star_half_black_16dp = 2130837538; - - // aapt resource value: 0x7f020023 - public const int abc_ic_star_half_black_36dp = 2130837539; - - // aapt resource value: 0x7f020024 - public const int abc_ic_star_half_black_48dp = 2130837540; - - // aapt resource value: 0x7f020025 - public const int abc_ic_voice_search_api_material = 2130837541; - - // aapt resource value: 0x7f020026 - public const int abc_item_background_holo_dark = 2130837542; - - // aapt resource value: 0x7f020027 - public const int abc_item_background_holo_light = 2130837543; - - // aapt resource value: 0x7f020028 - public const int abc_list_divider_mtrl_alpha = 2130837544; - - // aapt resource value: 0x7f020029 - public const int abc_list_focused_holo = 2130837545; - - // aapt resource value: 0x7f02002a - public const int abc_list_longpressed_holo = 2130837546; - - // aapt resource value: 0x7f02002b - public const int abc_list_pressed_holo_dark = 2130837547; - - // aapt resource value: 0x7f02002c - public const int abc_list_pressed_holo_light = 2130837548; - - // aapt resource value: 0x7f02002d - public const int abc_list_selector_background_transition_holo_dark = 2130837549; - - // aapt resource value: 0x7f02002e - public const int abc_list_selector_background_transition_holo_light = 2130837550; - - // aapt resource value: 0x7f02002f - public const int abc_list_selector_disabled_holo_dark = 2130837551; - - // aapt resource value: 0x7f020030 - public const int abc_list_selector_disabled_holo_light = 2130837552; - - // aapt resource value: 0x7f020031 - public const int abc_list_selector_holo_dark = 2130837553; - - // aapt resource value: 0x7f020032 - public const int abc_list_selector_holo_light = 2130837554; - - // aapt resource value: 0x7f020033 - public const int abc_menu_hardkey_panel_mtrl_mult = 2130837555; - - // aapt resource value: 0x7f020034 - public const int abc_popup_background_mtrl_mult = 2130837556; - - // aapt resource value: 0x7f020035 - public const int abc_ratingbar_indicator_material = 2130837557; - - // aapt resource value: 0x7f020036 - public const int abc_ratingbar_material = 2130837558; - - // aapt resource value: 0x7f020037 - public const int abc_ratingbar_small_material = 2130837559; - - // aapt resource value: 0x7f020038 - public const int abc_scrubber_control_off_mtrl_alpha = 2130837560; - - // aapt resource value: 0x7f020039 - public const int abc_scrubber_control_to_pressed_mtrl_000 = 2130837561; - - // aapt resource value: 0x7f02003a - public const int abc_scrubber_control_to_pressed_mtrl_005 = 2130837562; - - // aapt resource value: 0x7f02003b - public const int abc_scrubber_primary_mtrl_alpha = 2130837563; - - // aapt resource value: 0x7f02003c - public const int abc_scrubber_track_mtrl_alpha = 2130837564; - - // aapt resource value: 0x7f02003d - public const int abc_seekbar_thumb_material = 2130837565; - - // aapt resource value: 0x7f02003e - public const int abc_seekbar_tick_mark_material = 2130837566; - - // aapt resource value: 0x7f02003f - public const int abc_seekbar_track_material = 2130837567; - - // aapt resource value: 0x7f020040 - public const int abc_spinner_mtrl_am_alpha = 2130837568; - - // aapt resource value: 0x7f020041 - public const int abc_spinner_textfield_background_material = 2130837569; - - // aapt resource value: 0x7f020042 - public const int abc_switch_thumb_material = 2130837570; - - // aapt resource value: 0x7f020043 - public const int abc_switch_track_mtrl_alpha = 2130837571; - - // aapt resource value: 0x7f020044 - public const int abc_tab_indicator_material = 2130837572; - - // aapt resource value: 0x7f020045 - public const int abc_tab_indicator_mtrl_alpha = 2130837573; - - // aapt resource value: 0x7f020046 - public const int abc_text_cursor_material = 2130837574; - - // aapt resource value: 0x7f020047 - public const int abc_text_select_handle_left_mtrl_dark = 2130837575; - - // aapt resource value: 0x7f020048 - public const int abc_text_select_handle_left_mtrl_light = 2130837576; - - // aapt resource value: 0x7f020049 - public const int abc_text_select_handle_middle_mtrl_dark = 2130837577; - - // aapt resource value: 0x7f02004a - public const int abc_text_select_handle_middle_mtrl_light = 2130837578; - - // aapt resource value: 0x7f02004b - public const int abc_text_select_handle_right_mtrl_dark = 2130837579; - - // aapt resource value: 0x7f02004c - public const int abc_text_select_handle_right_mtrl_light = 2130837580; - - // aapt resource value: 0x7f02004d - public const int abc_textfield_activated_mtrl_alpha = 2130837581; - - // aapt resource value: 0x7f02004e - public const int abc_textfield_default_mtrl_alpha = 2130837582; - - // aapt resource value: 0x7f02004f - public const int abc_textfield_search_activated_mtrl_alpha = 2130837583; - - // aapt resource value: 0x7f020050 - public const int abc_textfield_search_default_mtrl_alpha = 2130837584; - - // aapt resource value: 0x7f020051 - public const int abc_textfield_search_material = 2130837585; - - // aapt resource value: 0x7f020052 - public const int abc_vector_test = 2130837586; - - // aapt resource value: 0x7f020053 - public const int avd_hide_password = 2130837587; - - // aapt resource value: 0x7f02010a - public const int avd_hide_password_1 = 2130837770; - - // aapt resource value: 0x7f02010b - public const int avd_hide_password_2 = 2130837771; - - // aapt resource value: 0x7f02010c - public const int avd_hide_password_3 = 2130837772; - - // aapt resource value: 0x7f020054 - public const int avd_show_password = 2130837588; - - // aapt resource value: 0x7f02010d - public const int avd_show_password_1 = 2130837773; - - // aapt resource value: 0x7f02010e - public const int avd_show_password_2 = 2130837774; - - // aapt resource value: 0x7f02010f - public const int avd_show_password_3 = 2130837775; - - // aapt resource value: 0x7f020055 - public const int design_bottom_navigation_item_background = 2130837589; - - // aapt resource value: 0x7f020056 - public const int design_fab_background = 2130837590; - - // aapt resource value: 0x7f020057 - public const int design_ic_visibility = 2130837591; - - // aapt resource value: 0x7f020058 - public const int design_ic_visibility_off = 2130837592; - - // aapt resource value: 0x7f020059 - public const int design_password_eye = 2130837593; - - // aapt resource value: 0x7f02005a - public const int design_snackbar_background = 2130837594; - - // aapt resource value: 0x7f02005b - public const int ic_audiotrack_dark = 2130837595; - - // aapt resource value: 0x7f02005c - public const int ic_audiotrack_light = 2130837596; - - // aapt resource value: 0x7f02005d - public const int ic_dialog_close_dark = 2130837597; - - // aapt resource value: 0x7f02005e - public const int ic_dialog_close_light = 2130837598; - - // aapt resource value: 0x7f02005f - public const int ic_group_collapse_00 = 2130837599; - - // aapt resource value: 0x7f020060 - public const int ic_group_collapse_01 = 2130837600; - - // aapt resource value: 0x7f020061 - public const int ic_group_collapse_02 = 2130837601; - - // aapt resource value: 0x7f020062 - public const int ic_group_collapse_03 = 2130837602; - - // aapt resource value: 0x7f020063 - public const int ic_group_collapse_04 = 2130837603; - - // aapt resource value: 0x7f020064 - public const int ic_group_collapse_05 = 2130837604; - - // aapt resource value: 0x7f020065 - public const int ic_group_collapse_06 = 2130837605; - - // aapt resource value: 0x7f020066 - public const int ic_group_collapse_07 = 2130837606; - - // aapt resource value: 0x7f020067 - public const int ic_group_collapse_08 = 2130837607; - - // aapt resource value: 0x7f020068 - public const int ic_group_collapse_09 = 2130837608; - - // aapt resource value: 0x7f020069 - public const int ic_group_collapse_10 = 2130837609; - - // aapt resource value: 0x7f02006a - public const int ic_group_collapse_11 = 2130837610; - - // aapt resource value: 0x7f02006b - public const int ic_group_collapse_12 = 2130837611; - - // aapt resource value: 0x7f02006c - public const int ic_group_collapse_13 = 2130837612; - - // aapt resource value: 0x7f02006d - public const int ic_group_collapse_14 = 2130837613; - - // aapt resource value: 0x7f02006e - public const int ic_group_collapse_15 = 2130837614; - - // aapt resource value: 0x7f02006f - public const int ic_group_expand_00 = 2130837615; - - // aapt resource value: 0x7f020070 - public const int ic_group_expand_01 = 2130837616; - - // aapt resource value: 0x7f020071 - public const int ic_group_expand_02 = 2130837617; - - // aapt resource value: 0x7f020072 - public const int ic_group_expand_03 = 2130837618; - - // aapt resource value: 0x7f020073 - public const int ic_group_expand_04 = 2130837619; - - // aapt resource value: 0x7f020074 - public const int ic_group_expand_05 = 2130837620; - - // aapt resource value: 0x7f020075 - public const int ic_group_expand_06 = 2130837621; - - // aapt resource value: 0x7f020076 - public const int ic_group_expand_07 = 2130837622; - - // aapt resource value: 0x7f020077 - public const int ic_group_expand_08 = 2130837623; - - // aapt resource value: 0x7f020078 - public const int ic_group_expand_09 = 2130837624; - - // aapt resource value: 0x7f020079 - public const int ic_group_expand_10 = 2130837625; - - // aapt resource value: 0x7f02007a - public const int ic_group_expand_11 = 2130837626; - - // aapt resource value: 0x7f02007b - public const int ic_group_expand_12 = 2130837627; - - // aapt resource value: 0x7f02007c - public const int ic_group_expand_13 = 2130837628; - - // aapt resource value: 0x7f02007d - public const int ic_group_expand_14 = 2130837629; - - // aapt resource value: 0x7f02007e - public const int ic_group_expand_15 = 2130837630; - - // aapt resource value: 0x7f02007f - public const int ic_media_pause_dark = 2130837631; - - // aapt resource value: 0x7f020080 - public const int ic_media_pause_light = 2130837632; - - // aapt resource value: 0x7f020081 - public const int ic_media_play_dark = 2130837633; - - // aapt resource value: 0x7f020082 - public const int ic_media_play_light = 2130837634; - - // aapt resource value: 0x7f020083 - public const int ic_mr_button_connected_00_dark = 2130837635; - - // aapt resource value: 0x7f020084 - public const int ic_mr_button_connected_00_light = 2130837636; - - // aapt resource value: 0x7f020085 - public const int ic_mr_button_connected_01_dark = 2130837637; - - // aapt resource value: 0x7f020086 - public const int ic_mr_button_connected_01_light = 2130837638; - - // aapt resource value: 0x7f020087 - public const int ic_mr_button_connected_02_dark = 2130837639; - - // aapt resource value: 0x7f020088 - public const int ic_mr_button_connected_02_light = 2130837640; - - // aapt resource value: 0x7f020089 - public const int ic_mr_button_connected_03_dark = 2130837641; - - // aapt resource value: 0x7f02008a - public const int ic_mr_button_connected_03_light = 2130837642; - - // aapt resource value: 0x7f02008b - public const int ic_mr_button_connected_04_dark = 2130837643; - - // aapt resource value: 0x7f02008c - public const int ic_mr_button_connected_04_light = 2130837644; - - // aapt resource value: 0x7f02008d - public const int ic_mr_button_connected_05_dark = 2130837645; - - // aapt resource value: 0x7f02008e - public const int ic_mr_button_connected_05_light = 2130837646; - - // aapt resource value: 0x7f02008f - public const int ic_mr_button_connected_06_dark = 2130837647; - - // aapt resource value: 0x7f020090 - public const int ic_mr_button_connected_06_light = 2130837648; - - // aapt resource value: 0x7f020091 - public const int ic_mr_button_connected_07_dark = 2130837649; - - // aapt resource value: 0x7f020092 - public const int ic_mr_button_connected_07_light = 2130837650; - - // aapt resource value: 0x7f020093 - public const int ic_mr_button_connected_08_dark = 2130837651; - - // aapt resource value: 0x7f020094 - public const int ic_mr_button_connected_08_light = 2130837652; - - // aapt resource value: 0x7f020095 - public const int ic_mr_button_connected_09_dark = 2130837653; - - // aapt resource value: 0x7f020096 - public const int ic_mr_button_connected_09_light = 2130837654; - - // aapt resource value: 0x7f020097 - public const int ic_mr_button_connected_10_dark = 2130837655; - - // aapt resource value: 0x7f020098 - public const int ic_mr_button_connected_10_light = 2130837656; - - // aapt resource value: 0x7f020099 - public const int ic_mr_button_connected_11_dark = 2130837657; - - // aapt resource value: 0x7f02009a - public const int ic_mr_button_connected_11_light = 2130837658; - - // aapt resource value: 0x7f02009b - public const int ic_mr_button_connected_12_dark = 2130837659; - - // aapt resource value: 0x7f02009c - public const int ic_mr_button_connected_12_light = 2130837660; - - // aapt resource value: 0x7f02009d - public const int ic_mr_button_connected_13_dark = 2130837661; - - // aapt resource value: 0x7f02009e - public const int ic_mr_button_connected_13_light = 2130837662; - - // aapt resource value: 0x7f02009f - public const int ic_mr_button_connected_14_dark = 2130837663; - - // aapt resource value: 0x7f0200a0 - public const int ic_mr_button_connected_14_light = 2130837664; - - // aapt resource value: 0x7f0200a1 - public const int ic_mr_button_connected_15_dark = 2130837665; - - // aapt resource value: 0x7f0200a2 - public const int ic_mr_button_connected_15_light = 2130837666; - - // aapt resource value: 0x7f0200a3 - public const int ic_mr_button_connected_16_dark = 2130837667; - - // aapt resource value: 0x7f0200a4 - public const int ic_mr_button_connected_16_light = 2130837668; - - // aapt resource value: 0x7f0200a5 - public const int ic_mr_button_connected_17_dark = 2130837669; - - // aapt resource value: 0x7f0200a6 - public const int ic_mr_button_connected_17_light = 2130837670; - - // aapt resource value: 0x7f0200a7 - public const int ic_mr_button_connected_18_dark = 2130837671; - - // aapt resource value: 0x7f0200a8 - public const int ic_mr_button_connected_18_light = 2130837672; - - // aapt resource value: 0x7f0200a9 - public const int ic_mr_button_connected_19_dark = 2130837673; - - // aapt resource value: 0x7f0200aa - public const int ic_mr_button_connected_19_light = 2130837674; - - // aapt resource value: 0x7f0200ab - public const int ic_mr_button_connected_20_dark = 2130837675; - - // aapt resource value: 0x7f0200ac - public const int ic_mr_button_connected_20_light = 2130837676; - - // aapt resource value: 0x7f0200ad - public const int ic_mr_button_connected_21_dark = 2130837677; - - // aapt resource value: 0x7f0200ae - public const int ic_mr_button_connected_21_light = 2130837678; - - // aapt resource value: 0x7f0200af - public const int ic_mr_button_connected_22_dark = 2130837679; - - // aapt resource value: 0x7f0200b0 - public const int ic_mr_button_connected_22_light = 2130837680; - - // aapt resource value: 0x7f0200b1 - public const int ic_mr_button_connecting_00_dark = 2130837681; - - // aapt resource value: 0x7f0200b2 - public const int ic_mr_button_connecting_00_light = 2130837682; - - // aapt resource value: 0x7f0200b3 - public const int ic_mr_button_connecting_01_dark = 2130837683; - - // aapt resource value: 0x7f0200b4 - public const int ic_mr_button_connecting_01_light = 2130837684; - - // aapt resource value: 0x7f0200b5 - public const int ic_mr_button_connecting_02_dark = 2130837685; - - // aapt resource value: 0x7f0200b6 - public const int ic_mr_button_connecting_02_light = 2130837686; - - // aapt resource value: 0x7f0200b7 - public const int ic_mr_button_connecting_03_dark = 2130837687; - - // aapt resource value: 0x7f0200b8 - public const int ic_mr_button_connecting_03_light = 2130837688; - - // aapt resource value: 0x7f0200b9 - public const int ic_mr_button_connecting_04_dark = 2130837689; - - // aapt resource value: 0x7f0200ba - public const int ic_mr_button_connecting_04_light = 2130837690; - - // aapt resource value: 0x7f0200bb - public const int ic_mr_button_connecting_05_dark = 2130837691; - - // aapt resource value: 0x7f0200bc - public const int ic_mr_button_connecting_05_light = 2130837692; - - // aapt resource value: 0x7f0200bd - public const int ic_mr_button_connecting_06_dark = 2130837693; - - // aapt resource value: 0x7f0200be - public const int ic_mr_button_connecting_06_light = 2130837694; - - // aapt resource value: 0x7f0200bf - public const int ic_mr_button_connecting_07_dark = 2130837695; - - // aapt resource value: 0x7f0200c0 - public const int ic_mr_button_connecting_07_light = 2130837696; - - // aapt resource value: 0x7f0200c1 - public const int ic_mr_button_connecting_08_dark = 2130837697; - - // aapt resource value: 0x7f0200c2 - public const int ic_mr_button_connecting_08_light = 2130837698; - - // aapt resource value: 0x7f0200c3 - public const int ic_mr_button_connecting_09_dark = 2130837699; - - // aapt resource value: 0x7f0200c4 - public const int ic_mr_button_connecting_09_light = 2130837700; - - // aapt resource value: 0x7f0200c5 - public const int ic_mr_button_connecting_10_dark = 2130837701; - - // aapt resource value: 0x7f0200c6 - public const int ic_mr_button_connecting_10_light = 2130837702; - - // aapt resource value: 0x7f0200c7 - public const int ic_mr_button_connecting_11_dark = 2130837703; - - // aapt resource value: 0x7f0200c8 - public const int ic_mr_button_connecting_11_light = 2130837704; - - // aapt resource value: 0x7f0200c9 - public const int ic_mr_button_connecting_12_dark = 2130837705; - - // aapt resource value: 0x7f0200ca - public const int ic_mr_button_connecting_12_light = 2130837706; - - // aapt resource value: 0x7f0200cb - public const int ic_mr_button_connecting_13_dark = 2130837707; - - // aapt resource value: 0x7f0200cc - public const int ic_mr_button_connecting_13_light = 2130837708; - - // aapt resource value: 0x7f0200cd - public const int ic_mr_button_connecting_14_dark = 2130837709; - - // aapt resource value: 0x7f0200ce - public const int ic_mr_button_connecting_14_light = 2130837710; - - // aapt resource value: 0x7f0200cf - public const int ic_mr_button_connecting_15_dark = 2130837711; - - // aapt resource value: 0x7f0200d0 - public const int ic_mr_button_connecting_15_light = 2130837712; - - // aapt resource value: 0x7f0200d1 - public const int ic_mr_button_connecting_16_dark = 2130837713; - - // aapt resource value: 0x7f0200d2 - public const int ic_mr_button_connecting_16_light = 2130837714; - - // aapt resource value: 0x7f0200d3 - public const int ic_mr_button_connecting_17_dark = 2130837715; - - // aapt resource value: 0x7f0200d4 - public const int ic_mr_button_connecting_17_light = 2130837716; - - // aapt resource value: 0x7f0200d5 - public const int ic_mr_button_connecting_18_dark = 2130837717; - - // aapt resource value: 0x7f0200d6 - public const int ic_mr_button_connecting_18_light = 2130837718; - - // aapt resource value: 0x7f0200d7 - public const int ic_mr_button_connecting_19_dark = 2130837719; - - // aapt resource value: 0x7f0200d8 - public const int ic_mr_button_connecting_19_light = 2130837720; - - // aapt resource value: 0x7f0200d9 - public const int ic_mr_button_connecting_20_dark = 2130837721; - - // aapt resource value: 0x7f0200da - public const int ic_mr_button_connecting_20_light = 2130837722; - - // aapt resource value: 0x7f0200db - public const int ic_mr_button_connecting_21_dark = 2130837723; - - // aapt resource value: 0x7f0200dc - public const int ic_mr_button_connecting_21_light = 2130837724; - - // aapt resource value: 0x7f0200dd - public const int ic_mr_button_connecting_22_dark = 2130837725; - - // aapt resource value: 0x7f0200de - public const int ic_mr_button_connecting_22_light = 2130837726; - - // aapt resource value: 0x7f0200df - public const int ic_mr_button_disabled_dark = 2130837727; - - // aapt resource value: 0x7f0200e0 - public const int ic_mr_button_disabled_light = 2130837728; - - // aapt resource value: 0x7f0200e1 - public const int ic_mr_button_disconnected_dark = 2130837729; - - // aapt resource value: 0x7f0200e2 - public const int ic_mr_button_disconnected_light = 2130837730; - - // aapt resource value: 0x7f0200e3 - public const int ic_mr_button_grey = 2130837731; - - // aapt resource value: 0x7f0200e4 - public const int ic_vol_type_speaker_dark = 2130837732; - - // aapt resource value: 0x7f0200e5 - public const int ic_vol_type_speaker_group_dark = 2130837733; - - // aapt resource value: 0x7f0200e6 - public const int ic_vol_type_speaker_group_light = 2130837734; - - // aapt resource value: 0x7f0200e7 - public const int ic_vol_type_speaker_light = 2130837735; - - // aapt resource value: 0x7f0200e8 - public const int ic_vol_type_tv_dark = 2130837736; - - // aapt resource value: 0x7f0200e9 - public const int ic_vol_type_tv_light = 2130837737; - - // aapt resource value: 0x7f0200ea - public const int Icon = 2130837738; - - // aapt resource value: 0x7f0200eb - public const int mr_button_connected_dark = 2130837739; - - // aapt resource value: 0x7f0200ec - public const int mr_button_connected_light = 2130837740; - - // aapt resource value: 0x7f0200ed - public const int mr_button_connecting_dark = 2130837741; - - // aapt resource value: 0x7f0200ee - public const int mr_button_connecting_light = 2130837742; - - // aapt resource value: 0x7f0200ef - public const int mr_button_dark = 2130837743; - - // aapt resource value: 0x7f0200f0 - public const int mr_button_light = 2130837744; - - // aapt resource value: 0x7f0200f1 - public const int mr_dialog_close_dark = 2130837745; - - // aapt resource value: 0x7f0200f2 - public const int mr_dialog_close_light = 2130837746; - - // aapt resource value: 0x7f0200f3 - public const int mr_dialog_material_background_dark = 2130837747; - - // aapt resource value: 0x7f0200f4 - public const int mr_dialog_material_background_light = 2130837748; - - // aapt resource value: 0x7f0200f5 - public const int mr_group_collapse = 2130837749; - - // aapt resource value: 0x7f0200f6 - public const int mr_group_expand = 2130837750; - - // aapt resource value: 0x7f0200f7 - public const int mr_media_pause_dark = 2130837751; - - // aapt resource value: 0x7f0200f8 - public const int mr_media_pause_light = 2130837752; - - // aapt resource value: 0x7f0200f9 - public const int mr_media_play_dark = 2130837753; - - // aapt resource value: 0x7f0200fa - public const int mr_media_play_light = 2130837754; - - // aapt resource value: 0x7f0200fb - public const int mr_vol_type_audiotrack_dark = 2130837755; - - // aapt resource value: 0x7f0200fc - public const int mr_vol_type_audiotrack_light = 2130837756; - - // aapt resource value: 0x7f0200fd - public const int navigation_empty_icon = 2130837757; - - // aapt resource value: 0x7f0200fe - public const int notification_action_background = 2130837758; - - // aapt resource value: 0x7f0200ff - public const int notification_bg = 2130837759; - - // aapt resource value: 0x7f020100 - public const int notification_bg_low = 2130837760; - - // aapt resource value: 0x7f020101 - public const int notification_bg_low_normal = 2130837761; - - // aapt resource value: 0x7f020102 - public const int notification_bg_low_pressed = 2130837762; - - // aapt resource value: 0x7f020103 - public const int notification_bg_normal = 2130837763; - - // aapt resource value: 0x7f020104 - public const int notification_bg_normal_pressed = 2130837764; - - // aapt resource value: 0x7f020105 - public const int notification_icon_background = 2130837765; - - // aapt resource value: 0x7f020108 - public const int notification_template_icon_bg = 2130837768; - - // aapt resource value: 0x7f020109 - public const int notification_template_icon_low_bg = 2130837769; - - // aapt resource value: 0x7f020106 - public const int notification_tile_bg = 2130837766; - - // aapt resource value: 0x7f020107 - public const int notify_panel_notification_icon_bg = 2130837767; - - static Drawable() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Drawable() - { - } - } - - public partial class Id - { - - // aapt resource value: 0x7f08009c - public const int action0 = 2131230876; - - // aapt resource value: 0x7f080064 - public const int action_bar = 2131230820; - - // aapt resource value: 0x7f080001 - public const int action_bar_activity_content = 2131230721; - - // aapt resource value: 0x7f080063 - public const int action_bar_container = 2131230819; - - // aapt resource value: 0x7f08005f - public const int action_bar_root = 2131230815; - - // aapt resource value: 0x7f080002 - public const int action_bar_spinner = 2131230722; - - // aapt resource value: 0x7f080042 - public const int action_bar_subtitle = 2131230786; - - // aapt resource value: 0x7f080041 - public const int action_bar_title = 2131230785; - - // aapt resource value: 0x7f080099 - public const int action_container = 2131230873; - - // aapt resource value: 0x7f080065 - public const int action_context_bar = 2131230821; - - // aapt resource value: 0x7f0800a0 - public const int action_divider = 2131230880; - - // aapt resource value: 0x7f08009a - public const int action_image = 2131230874; - - // aapt resource value: 0x7f080003 - public const int action_menu_divider = 2131230723; - - // aapt resource value: 0x7f080004 - public const int action_menu_presenter = 2131230724; - - // aapt resource value: 0x7f080061 - public const int action_mode_bar = 2131230817; - - // aapt resource value: 0x7f080060 - public const int action_mode_bar_stub = 2131230816; - - // aapt resource value: 0x7f080043 - public const int action_mode_close_button = 2131230787; - - // aapt resource value: 0x7f08009b - public const int action_text = 2131230875; - - // aapt resource value: 0x7f0800a9 - public const int actions = 2131230889; - - // aapt resource value: 0x7f080044 - public const int activity_chooser_view_content = 2131230788; - - // aapt resource value: 0x7f080019 - public const int add = 2131230745; - - // aapt resource value: 0x7f080058 - public const int alertTitle = 2131230808; - - // aapt resource value: 0x7f08003d - public const int all = 2131230781; - - // aapt resource value: 0x7f080023 - public const int always = 2131230755; - - // aapt resource value: 0x7f08002f - public const int auto = 2131230767; - - // aapt resource value: 0x7f080020 - public const int beginning = 2131230752; - - // aapt resource value: 0x7f080028 - public const int bottom = 2131230760; - - // aapt resource value: 0x7f08004b - public const int buttonPanel = 2131230795; - - // aapt resource value: 0x7f08009d - public const int cancel_action = 2131230877; - - // aapt resource value: 0x7f080030 - public const int center = 2131230768; - - // aapt resource value: 0x7f080031 - public const int center_horizontal = 2131230769; - - // aapt resource value: 0x7f080032 - public const int center_vertical = 2131230770; - - // aapt resource value: 0x7f08005b - public const int checkbox = 2131230811; - - // aapt resource value: 0x7f0800a5 - public const int chronometer = 2131230885; - - // aapt resource value: 0x7f080039 - public const int clip_horizontal = 2131230777; - - // aapt resource value: 0x7f08003a - public const int clip_vertical = 2131230778; - - // aapt resource value: 0x7f080024 - public const int collapseActionView = 2131230756; - - // aapt resource value: 0x7f08004e - public const int contentPanel = 2131230798; - - // aapt resource value: 0x7f080055 - public const int custom = 2131230805; - - // aapt resource value: 0x7f080054 - public const int customPanel = 2131230804; - - // aapt resource value: 0x7f080062 - public const int decor_content_parent = 2131230818; - - // aapt resource value: 0x7f080047 - public const int default_activity_button = 2131230791; - - // aapt resource value: 0x7f080076 - public const int design_bottom_sheet = 2131230838; - - // aapt resource value: 0x7f08007d - public const int design_menu_item_action_area = 2131230845; - - // aapt resource value: 0x7f08007c - public const int design_menu_item_action_area_stub = 2131230844; - - // aapt resource value: 0x7f08007b - public const int design_menu_item_text = 2131230843; - - // aapt resource value: 0x7f08007a - public const int design_navigation_view = 2131230842; - - // aapt resource value: 0x7f080012 - public const int disableHome = 2131230738; - - // aapt resource value: 0x7f080066 - public const int edit_query = 2131230822; - - // aapt resource value: 0x7f080021 - public const int end = 2131230753; - - // aapt resource value: 0x7f0800af - public const int end_padder = 2131230895; - - // aapt resource value: 0x7f08002a - public const int enterAlways = 2131230762; - - // aapt resource value: 0x7f08002b - public const int enterAlwaysCollapsed = 2131230763; - - // aapt resource value: 0x7f08002c - public const int exitUntilCollapsed = 2131230764; - - // aapt resource value: 0x7f080045 - public const int expand_activities_button = 2131230789; - - // aapt resource value: 0x7f08005a - public const int expanded_menu = 2131230810; - - // aapt resource value: 0x7f08003b - public const int fill = 2131230779; - - // aapt resource value: 0x7f08003c - public const int fill_horizontal = 2131230780; - - // aapt resource value: 0x7f080033 - public const int fill_vertical = 2131230771; - - // aapt resource value: 0x7f08003f - public const int @fixed = 2131230783; - - // aapt resource value: 0x7f080005 - public const int home = 2131230725; - - // aapt resource value: 0x7f080013 - public const int homeAsUp = 2131230739; - - // aapt resource value: 0x7f080049 - public const int icon = 2131230793; - - // aapt resource value: 0x7f0800aa - public const int icon_group = 2131230890; - - // aapt resource value: 0x7f080025 - public const int ifRoom = 2131230757; - - // aapt resource value: 0x7f080046 - public const int image = 2131230790; - - // aapt resource value: 0x7f0800a6 - public const int info = 2131230886; - - // aapt resource value: 0x7f080000 - public const int item_touch_helper_previous_elevation = 2131230720; - - // aapt resource value: 0x7f080074 - public const int largeLabel = 2131230836; - - // aapt resource value: 0x7f080034 - public const int left = 2131230772; - - // aapt resource value: 0x7f0800ab - public const int line1 = 2131230891; - - // aapt resource value: 0x7f0800ad - public const int line3 = 2131230893; - - // aapt resource value: 0x7f08000f - public const int listMode = 2131230735; - - // aapt resource value: 0x7f080048 - public const int list_item = 2131230792; - - // aapt resource value: 0x7f0800b3 - public const int masked = 2131230899; - - // aapt resource value: 0x7f08009f - public const int media_actions = 2131230879; - - // aapt resource value: 0x7f080022 - public const int middle = 2131230754; - - // aapt resource value: 0x7f08003e - public const int mini = 2131230782; - - // aapt resource value: 0x7f08008b - public const int mr_art = 2131230859; - - // aapt resource value: 0x7f080080 - public const int mr_chooser_list = 2131230848; - - // aapt resource value: 0x7f080083 - public const int mr_chooser_route_desc = 2131230851; - - // aapt resource value: 0x7f080081 - public const int mr_chooser_route_icon = 2131230849; - - // aapt resource value: 0x7f080082 - public const int mr_chooser_route_name = 2131230850; - - // aapt resource value: 0x7f08007f - public const int mr_chooser_title = 2131230847; - - // aapt resource value: 0x7f080088 - public const int mr_close = 2131230856; - - // aapt resource value: 0x7f08008e - public const int mr_control_divider = 2131230862; - - // aapt resource value: 0x7f080094 - public const int mr_control_play_pause = 2131230868; - - // aapt resource value: 0x7f080097 - public const int mr_control_subtitle = 2131230871; - - // aapt resource value: 0x7f080096 - public const int mr_control_title = 2131230870; - - // aapt resource value: 0x7f080095 - public const int mr_control_title_container = 2131230869; - - // aapt resource value: 0x7f080089 - public const int mr_custom_control = 2131230857; - - // aapt resource value: 0x7f08008a - public const int mr_default_control = 2131230858; - - // aapt resource value: 0x7f080085 - public const int mr_dialog_area = 2131230853; - - // aapt resource value: 0x7f080084 - public const int mr_expandable_area = 2131230852; - - // aapt resource value: 0x7f080098 - public const int mr_group_expand_collapse = 2131230872; - - // aapt resource value: 0x7f08008c - public const int mr_media_main_control = 2131230860; - - // aapt resource value: 0x7f080087 - public const int mr_name = 2131230855; - - // aapt resource value: 0x7f08008d - public const int mr_playback_control = 2131230861; - - // aapt resource value: 0x7f080086 - public const int mr_title_bar = 2131230854; - - // aapt resource value: 0x7f08008f - public const int mr_volume_control = 2131230863; - - // aapt resource value: 0x7f080090 - public const int mr_volume_group_list = 2131230864; - - // aapt resource value: 0x7f080092 - public const int mr_volume_item_icon = 2131230866; - - // aapt resource value: 0x7f080093 - public const int mr_volume_slider = 2131230867; - - // aapt resource value: 0x7f08001a - public const int multiply = 2131230746; - - // aapt resource value: 0x7f080079 - public const int navigation_header_container = 2131230841; - - // aapt resource value: 0x7f080026 - public const int never = 2131230758; - - // aapt resource value: 0x7f080014 - public const int none = 2131230740; - - // aapt resource value: 0x7f080010 - public const int normal = 2131230736; - - // aapt resource value: 0x7f0800a8 - public const int notification_background = 2131230888; - - // aapt resource value: 0x7f0800a2 - public const int notification_main_column = 2131230882; - - // aapt resource value: 0x7f0800a1 - public const int notification_main_column_container = 2131230881; - - // aapt resource value: 0x7f080037 - public const int parallax = 2131230775; - - // aapt resource value: 0x7f08004d - public const int parentPanel = 2131230797; - - // aapt resource value: 0x7f080038 - public const int pin = 2131230776; - - // aapt resource value: 0x7f080006 - public const int progress_circular = 2131230726; - - // aapt resource value: 0x7f080007 - public const int progress_horizontal = 2131230727; - - // aapt resource value: 0x7f08005d - public const int radio = 2131230813; - - // aapt resource value: 0x7f080035 - public const int right = 2131230773; - - // aapt resource value: 0x7f0800a7 - public const int right_icon = 2131230887; - - // aapt resource value: 0x7f0800a3 - public const int right_side = 2131230883; - - // aapt resource value: 0x7f08001b - public const int screen = 2131230747; - - // aapt resource value: 0x7f08002d - public const int scroll = 2131230765; - - // aapt resource value: 0x7f080053 - public const int scrollIndicatorDown = 2131230803; - - // aapt resource value: 0x7f08004f - public const int scrollIndicatorUp = 2131230799; - - // aapt resource value: 0x7f080050 - public const int scrollView = 2131230800; - - // aapt resource value: 0x7f080040 - public const int scrollable = 2131230784; - - // aapt resource value: 0x7f080068 - public const int search_badge = 2131230824; - - // aapt resource value: 0x7f080067 - public const int search_bar = 2131230823; - - // aapt resource value: 0x7f080069 - public const int search_button = 2131230825; - - // aapt resource value: 0x7f08006e - public const int search_close_btn = 2131230830; - - // aapt resource value: 0x7f08006a - public const int search_edit_frame = 2131230826; - - // aapt resource value: 0x7f080070 - public const int search_go_btn = 2131230832; - - // aapt resource value: 0x7f08006b - public const int search_mag_icon = 2131230827; - - // aapt resource value: 0x7f08006c - public const int search_plate = 2131230828; - - // aapt resource value: 0x7f08006d - public const int search_src_text = 2131230829; - - // aapt resource value: 0x7f080071 - public const int search_voice_btn = 2131230833; - - // aapt resource value: 0x7f080072 - public const int select_dialog_listview = 2131230834; - - // aapt resource value: 0x7f08005c - public const int shortcut = 2131230812; - - // aapt resource value: 0x7f080015 - public const int showCustom = 2131230741; - - // aapt resource value: 0x7f080016 - public const int showHome = 2131230742; - - // aapt resource value: 0x7f080017 - public const int showTitle = 2131230743; - - // aapt resource value: 0x7f0800b0 - public const int sliding_tabs = 2131230896; - - // aapt resource value: 0x7f080073 - public const int smallLabel = 2131230835; - - // aapt resource value: 0x7f080078 - public const int snackbar_action = 2131230840; - - // aapt resource value: 0x7f080077 - public const int snackbar_text = 2131230839; - - // aapt resource value: 0x7f08002e - public const int snap = 2131230766; - - // aapt resource value: 0x7f08004c - public const int spacer = 2131230796; - - // aapt resource value: 0x7f080008 - public const int split_action_bar = 2131230728; - - // aapt resource value: 0x7f08001c - public const int src_atop = 2131230748; - - // aapt resource value: 0x7f08001d - public const int src_in = 2131230749; - - // aapt resource value: 0x7f08001e - public const int src_over = 2131230750; - - // aapt resource value: 0x7f080036 - public const int start = 2131230774; - - // aapt resource value: 0x7f08009e - public const int status_bar_latest_event_content = 2131230878; - - // aapt resource value: 0x7f08005e - public const int submenuarrow = 2131230814; - - // aapt resource value: 0x7f08006f - public const int submit_area = 2131230831; - - // aapt resource value: 0x7f080011 - public const int tabMode = 2131230737; - - // aapt resource value: 0x7f0800ae - public const int text = 2131230894; - - // aapt resource value: 0x7f0800ac - public const int text2 = 2131230892; - - // aapt resource value: 0x7f080052 - public const int textSpacerNoButtons = 2131230802; - - // aapt resource value: 0x7f080051 - public const int textSpacerNoTitle = 2131230801; - - // aapt resource value: 0x7f08007e - public const int text_input_password_toggle = 2131230846; - - // aapt resource value: 0x7f08000c - public const int textinput_counter = 2131230732; - - // aapt resource value: 0x7f08000d - public const int textinput_error = 2131230733; - - // aapt resource value: 0x7f0800a4 - public const int time = 2131230884; - - // aapt resource value: 0x7f08004a - public const int title = 2131230794; - - // aapt resource value: 0x7f080059 - public const int titleDividerNoCustom = 2131230809; - - // aapt resource value: 0x7f080057 - public const int title_template = 2131230807; - - // aapt resource value: 0x7f0800b1 - public const int toolbar = 2131230897; - - // aapt resource value: 0x7f080029 - public const int top = 2131230761; - - // aapt resource value: 0x7f080056 - public const int topPanel = 2131230806; - - // aapt resource value: 0x7f080075 - public const int touch_outside = 2131230837; - - // aapt resource value: 0x7f08000a - public const int transition_current_scene = 2131230730; - - // aapt resource value: 0x7f08000b - public const int transition_scene_layoutid_cache = 2131230731; - - // aapt resource value: 0x7f080009 - public const int up = 2131230729; - - // aapt resource value: 0x7f080018 - public const int useLogo = 2131230744; - - // aapt resource value: 0x7f08000e - public const int view_offset_helper = 2131230734; - - // aapt resource value: 0x7f0800b2 - public const int visible = 2131230898; - - // aapt resource value: 0x7f080091 - public const int volume_item_container = 2131230865; - - // aapt resource value: 0x7f080027 - public const int withText = 2131230759; - - // aapt resource value: 0x7f08001f - public const int wrap_content = 2131230751; - - static Id() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Id() - { - } - } - - public partial class Integer - { - - // aapt resource value: 0x7f0a0003 - public const int abc_config_activityDefaultDur = 2131361795; - - // aapt resource value: 0x7f0a0004 - public const int abc_config_activityShortDur = 2131361796; - - // aapt resource value: 0x7f0a0008 - public const int app_bar_elevation_anim_duration = 2131361800; - - // aapt resource value: 0x7f0a0009 - public const int bottom_sheet_slide_duration = 2131361801; - - // aapt resource value: 0x7f0a0005 - public const int cancel_button_image_alpha = 2131361797; - - // aapt resource value: 0x7f0a0007 - public const int design_snackbar_text_max_lines = 2131361799; - - // aapt resource value: 0x7f0a000a - public const int hide_password_duration = 2131361802; - - // aapt resource value: 0x7f0a0000 - public const int mr_controller_volume_group_list_animation_duration_ms = 2131361792; - - // aapt resource value: 0x7f0a0001 - public const int mr_controller_volume_group_list_fade_in_duration_ms = 2131361793; - - // aapt resource value: 0x7f0a0002 - public const int mr_controller_volume_group_list_fade_out_duration_ms = 2131361794; - - // aapt resource value: 0x7f0a000b - public const int show_password_duration = 2131361803; - - // aapt resource value: 0x7f0a0006 - public const int status_bar_notification_info_maxnum = 2131361798; - - static Integer() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Integer() - { - } - } - - public partial class Interpolator - { - - // aapt resource value: 0x7f060000 - public const int mr_fast_out_slow_in = 2131099648; - - // aapt resource value: 0x7f060001 - public const int mr_linear_out_slow_in = 2131099649; - - static Interpolator() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Interpolator() - { - } - } - - public partial class Layout - { - - // aapt resource value: 0x7f030000 - public const int abc_action_bar_title_item = 2130903040; - - // aapt resource value: 0x7f030001 - public const int abc_action_bar_up_container = 2130903041; - - // aapt resource value: 0x7f030002 - public const int abc_action_bar_view_list_nav_layout = 2130903042; - - // aapt resource value: 0x7f030003 - public const int abc_action_menu_item_layout = 2130903043; - - // aapt resource value: 0x7f030004 - public const int abc_action_menu_layout = 2130903044; - - // aapt resource value: 0x7f030005 - public const int abc_action_mode_bar = 2130903045; - - // aapt resource value: 0x7f030006 - public const int abc_action_mode_close_item_material = 2130903046; - - // aapt resource value: 0x7f030007 - public const int abc_activity_chooser_view = 2130903047; - - // aapt resource value: 0x7f030008 - public const int abc_activity_chooser_view_list_item = 2130903048; - - // aapt resource value: 0x7f030009 - public const int abc_alert_dialog_button_bar_material = 2130903049; - - // aapt resource value: 0x7f03000a - public const int abc_alert_dialog_material = 2130903050; - - // aapt resource value: 0x7f03000b - public const int abc_alert_dialog_title_material = 2130903051; - - // aapt resource value: 0x7f03000c - public const int abc_dialog_title_material = 2130903052; - - // aapt resource value: 0x7f03000d - public const int abc_expanded_menu_layout = 2130903053; - - // aapt resource value: 0x7f03000e - public const int abc_list_menu_item_checkbox = 2130903054; - - // aapt resource value: 0x7f03000f - public const int abc_list_menu_item_icon = 2130903055; - - // aapt resource value: 0x7f030010 - public const int abc_list_menu_item_layout = 2130903056; - - // aapt resource value: 0x7f030011 - public const int abc_list_menu_item_radio = 2130903057; - - // aapt resource value: 0x7f030012 - public const int abc_popup_menu_header_item_layout = 2130903058; - - // aapt resource value: 0x7f030013 - public const int abc_popup_menu_item_layout = 2130903059; - - // aapt resource value: 0x7f030014 - public const int abc_screen_content_include = 2130903060; - - // aapt resource value: 0x7f030015 - public const int abc_screen_simple = 2130903061; - - // aapt resource value: 0x7f030016 - public const int abc_screen_simple_overlay_action_mode = 2130903062; - - // aapt resource value: 0x7f030017 - public const int abc_screen_toolbar = 2130903063; - - // aapt resource value: 0x7f030018 - public const int abc_search_dropdown_item_icons_2line = 2130903064; - - // aapt resource value: 0x7f030019 - public const int abc_search_view = 2130903065; - - // aapt resource value: 0x7f03001a - public const int abc_select_dialog_material = 2130903066; - - // aapt resource value: 0x7f03001b - public const int design_bottom_navigation_item = 2130903067; - - // aapt resource value: 0x7f03001c - public const int design_bottom_sheet_dialog = 2130903068; - - // aapt resource value: 0x7f03001d - public const int design_layout_snackbar = 2130903069; - - // aapt resource value: 0x7f03001e - public const int design_layout_snackbar_include = 2130903070; - - // aapt resource value: 0x7f03001f - public const int design_layout_tab_icon = 2130903071; - - // aapt resource value: 0x7f030020 - public const int design_layout_tab_text = 2130903072; - - // aapt resource value: 0x7f030021 - public const int design_menu_item_action_area = 2130903073; - - // aapt resource value: 0x7f030022 - public const int design_navigation_item = 2130903074; - - // aapt resource value: 0x7f030023 - public const int design_navigation_item_header = 2130903075; - - // aapt resource value: 0x7f030024 - public const int design_navigation_item_separator = 2130903076; - - // aapt resource value: 0x7f030025 - public const int design_navigation_item_subheader = 2130903077; - - // aapt resource value: 0x7f030026 - public const int design_navigation_menu = 2130903078; - - // aapt resource value: 0x7f030027 - public const int design_navigation_menu_item = 2130903079; - - // aapt resource value: 0x7f030028 - public const int design_text_input_password_icon = 2130903080; - - // aapt resource value: 0x7f030029 - public const int mr_chooser_dialog = 2130903081; - - // aapt resource value: 0x7f03002a - public const int mr_chooser_list_item = 2130903082; - - // aapt resource value: 0x7f03002b - public const int mr_controller_material_dialog_b = 2130903083; - - // aapt resource value: 0x7f03002c - public const int mr_controller_volume_item = 2130903084; - - // aapt resource value: 0x7f03002d - public const int mr_playback_control = 2130903085; - - // aapt resource value: 0x7f03002e - public const int mr_volume_control = 2130903086; - - // aapt resource value: 0x7f03002f - public const int notification_action = 2130903087; - - // aapt resource value: 0x7f030030 - public const int notification_action_tombstone = 2130903088; - - // aapt resource value: 0x7f030031 - public const int notification_media_action = 2130903089; - - // aapt resource value: 0x7f030032 - public const int notification_media_cancel_action = 2130903090; - - // aapt resource value: 0x7f030033 - public const int notification_template_big_media = 2130903091; - - // aapt resource value: 0x7f030034 - public const int notification_template_big_media_custom = 2130903092; - - // aapt resource value: 0x7f030035 - public const int notification_template_big_media_narrow = 2130903093; - - // aapt resource value: 0x7f030036 - public const int notification_template_big_media_narrow_custom = 2130903094; - - // aapt resource value: 0x7f030037 - public const int notification_template_custom_big = 2130903095; - - // aapt resource value: 0x7f030038 - public const int notification_template_icon_group = 2130903096; - - // aapt resource value: 0x7f030039 - public const int notification_template_lines_media = 2130903097; - - // aapt resource value: 0x7f03003a - public const int notification_template_media = 2130903098; - - // aapt resource value: 0x7f03003b - public const int notification_template_media_custom = 2130903099; - - // aapt resource value: 0x7f03003c - public const int notification_template_part_chronometer = 2130903100; - - // aapt resource value: 0x7f03003d - public const int notification_template_part_time = 2130903101; - - // aapt resource value: 0x7f03003e - public const int select_dialog_item_material = 2130903102; - - // aapt resource value: 0x7f03003f - public const int select_dialog_multichoice_material = 2130903103; - - // aapt resource value: 0x7f030040 - public const int select_dialog_singlechoice_material = 2130903104; - - // aapt resource value: 0x7f030041 - public const int support_simple_spinner_dropdown_item = 2130903105; - - // aapt resource value: 0x7f030042 - public const int Tabbar = 2130903106; - - // aapt resource value: 0x7f030043 - public const int Toolbar = 2130903107; - - static Layout() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Layout() - { - } - } - - public partial class String - { - - // aapt resource value: 0x7f090014 - public const int abc_action_bar_home_description = 2131296276; - - // aapt resource value: 0x7f090015 - public const int abc_action_bar_home_description_format = 2131296277; - - // aapt resource value: 0x7f090016 - public const int abc_action_bar_home_subtitle_description_format = 2131296278; - - // aapt resource value: 0x7f090017 - public const int abc_action_bar_up_description = 2131296279; - - // aapt resource value: 0x7f090018 - public const int abc_action_menu_overflow_description = 2131296280; - - // aapt resource value: 0x7f090019 - public const int abc_action_mode_done = 2131296281; - - // aapt resource value: 0x7f09001a - public const int abc_activity_chooser_view_see_all = 2131296282; - - // aapt resource value: 0x7f09001b - public const int abc_activitychooserview_choose_application = 2131296283; - - // aapt resource value: 0x7f09001c - public const int abc_capital_off = 2131296284; - - // aapt resource value: 0x7f09001d - public const int abc_capital_on = 2131296285; - - // aapt resource value: 0x7f090029 - public const int abc_font_family_body_1_material = 2131296297; - - // aapt resource value: 0x7f09002a - public const int abc_font_family_body_2_material = 2131296298; - - // aapt resource value: 0x7f09002b - public const int abc_font_family_button_material = 2131296299; - - // aapt resource value: 0x7f09002c - public const int abc_font_family_caption_material = 2131296300; - - // aapt resource value: 0x7f09002d - public const int abc_font_family_display_1_material = 2131296301; - - // aapt resource value: 0x7f09002e - public const int abc_font_family_display_2_material = 2131296302; - - // aapt resource value: 0x7f09002f - public const int abc_font_family_display_3_material = 2131296303; - - // aapt resource value: 0x7f090030 - public const int abc_font_family_display_4_material = 2131296304; - - // aapt resource value: 0x7f090031 - public const int abc_font_family_headline_material = 2131296305; - - // aapt resource value: 0x7f090032 - public const int abc_font_family_menu_material = 2131296306; - - // aapt resource value: 0x7f090033 - public const int abc_font_family_subhead_material = 2131296307; - - // aapt resource value: 0x7f090034 - public const int abc_font_family_title_material = 2131296308; - - // aapt resource value: 0x7f09001e - public const int abc_search_hint = 2131296286; - - // aapt resource value: 0x7f09001f - public const int abc_searchview_description_clear = 2131296287; - - // aapt resource value: 0x7f090020 - public const int abc_searchview_description_query = 2131296288; - - // aapt resource value: 0x7f090021 - public const int abc_searchview_description_search = 2131296289; - - // aapt resource value: 0x7f090022 - public const int abc_searchview_description_submit = 2131296290; - - // aapt resource value: 0x7f090023 - public const int abc_searchview_description_voice = 2131296291; - - // aapt resource value: 0x7f090024 - public const int abc_shareactionprovider_share_with = 2131296292; - - // aapt resource value: 0x7f090025 - public const int abc_shareactionprovider_share_with_application = 2131296293; - - // aapt resource value: 0x7f090026 - public const int abc_toolbar_collapse_description = 2131296294; - - // aapt resource value: 0x7f090035 - public const int appbar_scrolling_view_behavior = 2131296309; - - // aapt resource value: 0x7f090036 - public const int bottom_sheet_behavior = 2131296310; - - // aapt resource value: 0x7f090037 - public const int character_counter_pattern = 2131296311; - - // aapt resource value: 0x7f090000 - public const int mr_button_content_description = 2131296256; - - // aapt resource value: 0x7f090001 - public const int mr_cast_button_connected = 2131296257; - - // aapt resource value: 0x7f090002 - public const int mr_cast_button_connecting = 2131296258; - - // aapt resource value: 0x7f090003 - public const int mr_cast_button_disconnected = 2131296259; - - // aapt resource value: 0x7f090004 - public const int mr_chooser_searching = 2131296260; - - // aapt resource value: 0x7f090005 - public const int mr_chooser_title = 2131296261; - - // aapt resource value: 0x7f090006 - public const int mr_controller_album_art = 2131296262; - - // aapt resource value: 0x7f090007 - public const int mr_controller_casting_screen = 2131296263; - - // aapt resource value: 0x7f090008 - public const int mr_controller_close_description = 2131296264; - - // aapt resource value: 0x7f090009 - public const int mr_controller_collapse_group = 2131296265; - - // aapt resource value: 0x7f09000a - public const int mr_controller_disconnect = 2131296266; - - // aapt resource value: 0x7f09000b - public const int mr_controller_expand_group = 2131296267; - - // aapt resource value: 0x7f09000c - public const int mr_controller_no_info_available = 2131296268; - - // aapt resource value: 0x7f09000d - public const int mr_controller_no_media_selected = 2131296269; - - // aapt resource value: 0x7f09000e - public const int mr_controller_pause = 2131296270; - - // aapt resource value: 0x7f09000f - public const int mr_controller_play = 2131296271; - - // aapt resource value: 0x7f090010 - public const int mr_controller_stop = 2131296272; - - // aapt resource value: 0x7f090011 - public const int mr_controller_volume_slider = 2131296273; - - // aapt resource value: 0x7f090012 - public const int mr_system_route_name = 2131296274; - - // aapt resource value: 0x7f090013 - public const int mr_user_route_category_name = 2131296275; - - // aapt resource value: 0x7f090038 - public const int password_toggle_content_description = 2131296312; - - // aapt resource value: 0x7f090039 - public const int path_password_eye = 2131296313; - - // aapt resource value: 0x7f09003a - public const int path_password_eye_mask_strike_through = 2131296314; - - // aapt resource value: 0x7f09003b - public const int path_password_eye_mask_visible = 2131296315; - - // aapt resource value: 0x7f09003c - public const int path_password_strike_through = 2131296316; - - // aapt resource value: 0x7f090027 - public const int search_menu_title = 2131296295; - - // aapt resource value: 0x7f090028 - public const int status_bar_notification_info_overflow = 2131296296; - - static String() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private String() - { - } - } - - public partial class Style - { - - // aapt resource value: 0x7f0b00ae - public const int AlertDialog_AppCompat = 2131427502; - - // aapt resource value: 0x7f0b00af - public const int AlertDialog_AppCompat_Light = 2131427503; - - // aapt resource value: 0x7f0b00b0 - public const int Animation_AppCompat_Dialog = 2131427504; - - // aapt resource value: 0x7f0b00b1 - public const int Animation_AppCompat_DropDownUp = 2131427505; - - // aapt resource value: 0x7f0b0170 - public const int Animation_Design_BottomSheetDialog = 2131427696; - - // aapt resource value: 0x7f0b018b - public const int AppCompatDialogStyle = 2131427723; - - // aapt resource value: 0x7f0b00b2 - public const int Base_AlertDialog_AppCompat = 2131427506; - - // aapt resource value: 0x7f0b00b3 - public const int Base_AlertDialog_AppCompat_Light = 2131427507; - - // aapt resource value: 0x7f0b00b4 - public const int Base_Animation_AppCompat_Dialog = 2131427508; - - // aapt resource value: 0x7f0b00b5 - public const int Base_Animation_AppCompat_DropDownUp = 2131427509; - - // aapt resource value: 0x7f0b000c - public const int Base_CardView = 2131427340; - - // aapt resource value: 0x7f0b00b6 - public const int Base_DialogWindowTitle_AppCompat = 2131427510; - - // aapt resource value: 0x7f0b00b7 - public const int Base_DialogWindowTitleBackground_AppCompat = 2131427511; - - // aapt resource value: 0x7f0b004e - public const int Base_TextAppearance_AppCompat = 2131427406; - - // aapt resource value: 0x7f0b004f - public const int Base_TextAppearance_AppCompat_Body1 = 2131427407; - - // aapt resource value: 0x7f0b0050 - public const int Base_TextAppearance_AppCompat_Body2 = 2131427408; - - // aapt resource value: 0x7f0b0036 - public const int Base_TextAppearance_AppCompat_Button = 2131427382; - - // aapt resource value: 0x7f0b0051 - public const int Base_TextAppearance_AppCompat_Caption = 2131427409; - - // aapt resource value: 0x7f0b0052 - public const int Base_TextAppearance_AppCompat_Display1 = 2131427410; - - // aapt resource value: 0x7f0b0053 - public const int Base_TextAppearance_AppCompat_Display2 = 2131427411; - - // aapt resource value: 0x7f0b0054 - public const int Base_TextAppearance_AppCompat_Display3 = 2131427412; - - // aapt resource value: 0x7f0b0055 - public const int Base_TextAppearance_AppCompat_Display4 = 2131427413; - - // aapt resource value: 0x7f0b0056 - public const int Base_TextAppearance_AppCompat_Headline = 2131427414; - - // aapt resource value: 0x7f0b001a - public const int Base_TextAppearance_AppCompat_Inverse = 2131427354; - - // aapt resource value: 0x7f0b0057 - public const int Base_TextAppearance_AppCompat_Large = 2131427415; - - // aapt resource value: 0x7f0b001b - public const int Base_TextAppearance_AppCompat_Large_Inverse = 2131427355; - - // aapt resource value: 0x7f0b0058 - public const int Base_TextAppearance_AppCompat_Light_Widget_PopupMenu_Large = 2131427416; - - // aapt resource value: 0x7f0b0059 - public const int Base_TextAppearance_AppCompat_Light_Widget_PopupMenu_Small = 2131427417; - - // aapt resource value: 0x7f0b005a - public const int Base_TextAppearance_AppCompat_Medium = 2131427418; - - // aapt resource value: 0x7f0b001c - public const int Base_TextAppearance_AppCompat_Medium_Inverse = 2131427356; - - // aapt resource value: 0x7f0b005b - public const int Base_TextAppearance_AppCompat_Menu = 2131427419; - - // aapt resource value: 0x7f0b00b8 - public const int Base_TextAppearance_AppCompat_SearchResult = 2131427512; - - // aapt resource value: 0x7f0b005c - public const int Base_TextAppearance_AppCompat_SearchResult_Subtitle = 2131427420; - - // aapt resource value: 0x7f0b005d - public const int Base_TextAppearance_AppCompat_SearchResult_Title = 2131427421; - - // aapt resource value: 0x7f0b005e - public const int Base_TextAppearance_AppCompat_Small = 2131427422; - - // aapt resource value: 0x7f0b001d - public const int Base_TextAppearance_AppCompat_Small_Inverse = 2131427357; - - // aapt resource value: 0x7f0b005f - public const int Base_TextAppearance_AppCompat_Subhead = 2131427423; - - // aapt resource value: 0x7f0b001e - public const int Base_TextAppearance_AppCompat_Subhead_Inverse = 2131427358; - - // aapt resource value: 0x7f0b0060 - public const int Base_TextAppearance_AppCompat_Title = 2131427424; - - // aapt resource value: 0x7f0b001f - public const int Base_TextAppearance_AppCompat_Title_Inverse = 2131427359; - - // aapt resource value: 0x7f0b00a3 - public const int Base_TextAppearance_AppCompat_Widget_ActionBar_Menu = 2131427491; - - // aapt resource value: 0x7f0b0061 - public const int Base_TextAppearance_AppCompat_Widget_ActionBar_Subtitle = 2131427425; - - // aapt resource value: 0x7f0b0062 - public const int Base_TextAppearance_AppCompat_Widget_ActionBar_Subtitle_Inverse = 2131427426; - - // aapt resource value: 0x7f0b0063 - public const int Base_TextAppearance_AppCompat_Widget_ActionBar_Title = 2131427427; - - // aapt resource value: 0x7f0b0064 - public const int Base_TextAppearance_AppCompat_Widget_ActionBar_Title_Inverse = 2131427428; - - // aapt resource value: 0x7f0b0065 - public const int Base_TextAppearance_AppCompat_Widget_ActionMode_Subtitle = 2131427429; - - // aapt resource value: 0x7f0b0066 - public const int Base_TextAppearance_AppCompat_Widget_ActionMode_Title = 2131427430; - - // aapt resource value: 0x7f0b0067 - public const int Base_TextAppearance_AppCompat_Widget_Button = 2131427431; - - // aapt resource value: 0x7f0b00aa - public const int Base_TextAppearance_AppCompat_Widget_Button_Borderless_Colored = 2131427498; - - // aapt resource value: 0x7f0b00ab - public const int Base_TextAppearance_AppCompat_Widget_Button_Colored = 2131427499; - - // aapt resource value: 0x7f0b00a4 - public const int Base_TextAppearance_AppCompat_Widget_Button_Inverse = 2131427492; - - // aapt resource value: 0x7f0b00b9 - public const int Base_TextAppearance_AppCompat_Widget_DropDownItem = 2131427513; - - // aapt resource value: 0x7f0b0068 - public const int Base_TextAppearance_AppCompat_Widget_PopupMenu_Header = 2131427432; - - // aapt resource value: 0x7f0b0069 - public const int Base_TextAppearance_AppCompat_Widget_PopupMenu_Large = 2131427433; - - // aapt resource value: 0x7f0b006a - public const int Base_TextAppearance_AppCompat_Widget_PopupMenu_Small = 2131427434; - - // aapt resource value: 0x7f0b006b - public const int Base_TextAppearance_AppCompat_Widget_Switch = 2131427435; - - // aapt resource value: 0x7f0b006c - public const int Base_TextAppearance_AppCompat_Widget_TextView_SpinnerItem = 2131427436; - - // aapt resource value: 0x7f0b00ba - public const int Base_TextAppearance_Widget_AppCompat_ExpandedMenu_Item = 2131427514; - - // aapt resource value: 0x7f0b006d - public const int Base_TextAppearance_Widget_AppCompat_Toolbar_Subtitle = 2131427437; - - // aapt resource value: 0x7f0b006e - public const int Base_TextAppearance_Widget_AppCompat_Toolbar_Title = 2131427438; - - // aapt resource value: 0x7f0b006f - public const int Base_Theme_AppCompat = 2131427439; - - // aapt resource value: 0x7f0b00bb - public const int Base_Theme_AppCompat_CompactMenu = 2131427515; - - // aapt resource value: 0x7f0b0020 - public const int Base_Theme_AppCompat_Dialog = 2131427360; - - // aapt resource value: 0x7f0b0021 - public const int Base_Theme_AppCompat_Dialog_Alert = 2131427361; - - // aapt resource value: 0x7f0b00bc - public const int Base_Theme_AppCompat_Dialog_FixedSize = 2131427516; - - // aapt resource value: 0x7f0b0022 - public const int Base_Theme_AppCompat_Dialog_MinWidth = 2131427362; - - // aapt resource value: 0x7f0b0010 - public const int Base_Theme_AppCompat_DialogWhenLarge = 2131427344; - - // aapt resource value: 0x7f0b0070 - public const int Base_Theme_AppCompat_Light = 2131427440; - - // aapt resource value: 0x7f0b00bd - public const int Base_Theme_AppCompat_Light_DarkActionBar = 2131427517; - - // aapt resource value: 0x7f0b0023 - public const int Base_Theme_AppCompat_Light_Dialog = 2131427363; - - // aapt resource value: 0x7f0b0024 - public const int Base_Theme_AppCompat_Light_Dialog_Alert = 2131427364; - - // aapt resource value: 0x7f0b00be - public const int Base_Theme_AppCompat_Light_Dialog_FixedSize = 2131427518; - - // aapt resource value: 0x7f0b0025 - public const int Base_Theme_AppCompat_Light_Dialog_MinWidth = 2131427365; - - // aapt resource value: 0x7f0b0011 - public const int Base_Theme_AppCompat_Light_DialogWhenLarge = 2131427345; - - // aapt resource value: 0x7f0b00bf - public const int Base_ThemeOverlay_AppCompat = 2131427519; - - // aapt resource value: 0x7f0b00c0 - public const int Base_ThemeOverlay_AppCompat_ActionBar = 2131427520; - - // aapt resource value: 0x7f0b00c1 - public const int Base_ThemeOverlay_AppCompat_Dark = 2131427521; - - // aapt resource value: 0x7f0b00c2 - public const int Base_ThemeOverlay_AppCompat_Dark_ActionBar = 2131427522; - - // aapt resource value: 0x7f0b0026 - public const int Base_ThemeOverlay_AppCompat_Dialog = 2131427366; - - // aapt resource value: 0x7f0b0027 - public const int Base_ThemeOverlay_AppCompat_Dialog_Alert = 2131427367; - - // aapt resource value: 0x7f0b00c3 - public const int Base_ThemeOverlay_AppCompat_Light = 2131427523; - - // aapt resource value: 0x7f0b0028 - public const int Base_V11_Theme_AppCompat_Dialog = 2131427368; - - // aapt resource value: 0x7f0b0029 - public const int Base_V11_Theme_AppCompat_Light_Dialog = 2131427369; - - // aapt resource value: 0x7f0b002a - public const int Base_V11_ThemeOverlay_AppCompat_Dialog = 2131427370; - - // aapt resource value: 0x7f0b0032 - public const int Base_V12_Widget_AppCompat_AutoCompleteTextView = 2131427378; - - // aapt resource value: 0x7f0b0033 - public const int Base_V12_Widget_AppCompat_EditText = 2131427379; - - // aapt resource value: 0x7f0b0071 - public const int Base_V21_Theme_AppCompat = 2131427441; - - // aapt resource value: 0x7f0b0072 - public const int Base_V21_Theme_AppCompat_Dialog = 2131427442; - - // aapt resource value: 0x7f0b0073 - public const int Base_V21_Theme_AppCompat_Light = 2131427443; - - // aapt resource value: 0x7f0b0074 - public const int Base_V21_Theme_AppCompat_Light_Dialog = 2131427444; - - // aapt resource value: 0x7f0b0075 - public const int Base_V21_ThemeOverlay_AppCompat_Dialog = 2131427445; - - // aapt resource value: 0x7f0b00a1 - public const int Base_V22_Theme_AppCompat = 2131427489; - - // aapt resource value: 0x7f0b00a2 - public const int Base_V22_Theme_AppCompat_Light = 2131427490; - - // aapt resource value: 0x7f0b00a5 - public const int Base_V23_Theme_AppCompat = 2131427493; - - // aapt resource value: 0x7f0b00a6 - public const int Base_V23_Theme_AppCompat_Light = 2131427494; - - // aapt resource value: 0x7f0b00c4 - public const int Base_V7_Theme_AppCompat = 2131427524; - - // aapt resource value: 0x7f0b00c5 - public const int Base_V7_Theme_AppCompat_Dialog = 2131427525; - - // aapt resource value: 0x7f0b00c6 - public const int Base_V7_Theme_AppCompat_Light = 2131427526; - - // aapt resource value: 0x7f0b00c7 - public const int Base_V7_Theme_AppCompat_Light_Dialog = 2131427527; - - // aapt resource value: 0x7f0b00c8 - public const int Base_V7_ThemeOverlay_AppCompat_Dialog = 2131427528; - - // aapt resource value: 0x7f0b00c9 - public const int Base_V7_Widget_AppCompat_AutoCompleteTextView = 2131427529; - - // aapt resource value: 0x7f0b00ca - public const int Base_V7_Widget_AppCompat_EditText = 2131427530; - - // aapt resource value: 0x7f0b00cb - public const int Base_Widget_AppCompat_ActionBar = 2131427531; - - // aapt resource value: 0x7f0b00cc - public const int Base_Widget_AppCompat_ActionBar_Solid = 2131427532; - - // aapt resource value: 0x7f0b00cd - public const int Base_Widget_AppCompat_ActionBar_TabBar = 2131427533; - - // aapt resource value: 0x7f0b0076 - public const int Base_Widget_AppCompat_ActionBar_TabText = 2131427446; - - // aapt resource value: 0x7f0b0077 - public const int Base_Widget_AppCompat_ActionBar_TabView = 2131427447; - - // aapt resource value: 0x7f0b0078 - public const int Base_Widget_AppCompat_ActionButton = 2131427448; - - // aapt resource value: 0x7f0b0079 - public const int Base_Widget_AppCompat_ActionButton_CloseMode = 2131427449; - - // aapt resource value: 0x7f0b007a - public const int Base_Widget_AppCompat_ActionButton_Overflow = 2131427450; - - // aapt resource value: 0x7f0b00ce - public const int Base_Widget_AppCompat_ActionMode = 2131427534; - - // aapt resource value: 0x7f0b00cf - public const int Base_Widget_AppCompat_ActivityChooserView = 2131427535; - - // aapt resource value: 0x7f0b0034 - public const int Base_Widget_AppCompat_AutoCompleteTextView = 2131427380; - - // aapt resource value: 0x7f0b007b - public const int Base_Widget_AppCompat_Button = 2131427451; - - // aapt resource value: 0x7f0b007c - public const int Base_Widget_AppCompat_Button_Borderless = 2131427452; - - // aapt resource value: 0x7f0b007d - public const int Base_Widget_AppCompat_Button_Borderless_Colored = 2131427453; - - // aapt resource value: 0x7f0b00d0 - public const int Base_Widget_AppCompat_Button_ButtonBar_AlertDialog = 2131427536; - - // aapt resource value: 0x7f0b00a7 - public const int Base_Widget_AppCompat_Button_Colored = 2131427495; - - // aapt resource value: 0x7f0b007e - public const int Base_Widget_AppCompat_Button_Small = 2131427454; - - // aapt resource value: 0x7f0b007f - public const int Base_Widget_AppCompat_ButtonBar = 2131427455; - - // aapt resource value: 0x7f0b00d1 - public const int Base_Widget_AppCompat_ButtonBar_AlertDialog = 2131427537; - - // aapt resource value: 0x7f0b0080 - public const int Base_Widget_AppCompat_CompoundButton_CheckBox = 2131427456; - - // aapt resource value: 0x7f0b0081 - public const int Base_Widget_AppCompat_CompoundButton_RadioButton = 2131427457; - - // aapt resource value: 0x7f0b00d2 - public const int Base_Widget_AppCompat_CompoundButton_Switch = 2131427538; - - // aapt resource value: 0x7f0b000f - public const int Base_Widget_AppCompat_DrawerArrowToggle = 2131427343; - - // aapt resource value: 0x7f0b00d3 - public const int Base_Widget_AppCompat_DrawerArrowToggle_Common = 2131427539; - - // aapt resource value: 0x7f0b0082 - public const int Base_Widget_AppCompat_DropDownItem_Spinner = 2131427458; - - // aapt resource value: 0x7f0b0035 - public const int Base_Widget_AppCompat_EditText = 2131427381; - - // aapt resource value: 0x7f0b0083 - public const int Base_Widget_AppCompat_ImageButton = 2131427459; - - // aapt resource value: 0x7f0b00d4 - public const int Base_Widget_AppCompat_Light_ActionBar = 2131427540; - - // aapt resource value: 0x7f0b00d5 - public const int Base_Widget_AppCompat_Light_ActionBar_Solid = 2131427541; - - // aapt resource value: 0x7f0b00d6 - public const int Base_Widget_AppCompat_Light_ActionBar_TabBar = 2131427542; - - // aapt resource value: 0x7f0b0084 - public const int Base_Widget_AppCompat_Light_ActionBar_TabText = 2131427460; - - // aapt resource value: 0x7f0b0085 - public const int Base_Widget_AppCompat_Light_ActionBar_TabText_Inverse = 2131427461; - - // aapt resource value: 0x7f0b0086 - public const int Base_Widget_AppCompat_Light_ActionBar_TabView = 2131427462; - - // aapt resource value: 0x7f0b0087 - public const int Base_Widget_AppCompat_Light_PopupMenu = 2131427463; - - // aapt resource value: 0x7f0b0088 - public const int Base_Widget_AppCompat_Light_PopupMenu_Overflow = 2131427464; - - // aapt resource value: 0x7f0b00d7 - public const int Base_Widget_AppCompat_ListMenuView = 2131427543; - - // aapt resource value: 0x7f0b0089 - public const int Base_Widget_AppCompat_ListPopupWindow = 2131427465; - - // aapt resource value: 0x7f0b008a - public const int Base_Widget_AppCompat_ListView = 2131427466; - - // aapt resource value: 0x7f0b008b - public const int Base_Widget_AppCompat_ListView_DropDown = 2131427467; - - // aapt resource value: 0x7f0b008c - public const int Base_Widget_AppCompat_ListView_Menu = 2131427468; - - // aapt resource value: 0x7f0b008d - public const int Base_Widget_AppCompat_PopupMenu = 2131427469; - - // aapt resource value: 0x7f0b008e - public const int Base_Widget_AppCompat_PopupMenu_Overflow = 2131427470; - - // aapt resource value: 0x7f0b00d8 - public const int Base_Widget_AppCompat_PopupWindow = 2131427544; - - // aapt resource value: 0x7f0b002b - public const int Base_Widget_AppCompat_ProgressBar = 2131427371; - - // aapt resource value: 0x7f0b002c - public const int Base_Widget_AppCompat_ProgressBar_Horizontal = 2131427372; - - // aapt resource value: 0x7f0b008f - public const int Base_Widget_AppCompat_RatingBar = 2131427471; - - // aapt resource value: 0x7f0b00a8 - public const int Base_Widget_AppCompat_RatingBar_Indicator = 2131427496; - - // aapt resource value: 0x7f0b00a9 - public const int Base_Widget_AppCompat_RatingBar_Small = 2131427497; - - // aapt resource value: 0x7f0b00d9 - public const int Base_Widget_AppCompat_SearchView = 2131427545; - - // aapt resource value: 0x7f0b00da - public const int Base_Widget_AppCompat_SearchView_ActionBar = 2131427546; - - // aapt resource value: 0x7f0b0090 - public const int Base_Widget_AppCompat_SeekBar = 2131427472; - - // aapt resource value: 0x7f0b00db - public const int Base_Widget_AppCompat_SeekBar_Discrete = 2131427547; - - // aapt resource value: 0x7f0b0091 - public const int Base_Widget_AppCompat_Spinner = 2131427473; - - // aapt resource value: 0x7f0b0012 - public const int Base_Widget_AppCompat_Spinner_Underlined = 2131427346; - - // aapt resource value: 0x7f0b0092 - public const int Base_Widget_AppCompat_TextView_SpinnerItem = 2131427474; - - // aapt resource value: 0x7f0b00dc - public const int Base_Widget_AppCompat_Toolbar = 2131427548; - - // aapt resource value: 0x7f0b0093 - public const int Base_Widget_AppCompat_Toolbar_Button_Navigation = 2131427475; - - // aapt resource value: 0x7f0b0171 - public const int Base_Widget_Design_AppBarLayout = 2131427697; - - // aapt resource value: 0x7f0b0172 - public const int Base_Widget_Design_TabLayout = 2131427698; - - // aapt resource value: 0x7f0b000b - public const int CardView = 2131427339; - - // aapt resource value: 0x7f0b000d - public const int CardView_Dark = 2131427341; - - // aapt resource value: 0x7f0b000e - public const int CardView_Light = 2131427342; - - // aapt resource value: 0x7f0b0189 - public const int MainTheme = 2131427721; - - // aapt resource value: 0x7f0b018a - public const int MainTheme_Base = 2131427722; - - // aapt resource value: 0x7f0b002d - public const int Platform_AppCompat = 2131427373; - - // aapt resource value: 0x7f0b002e - public const int Platform_AppCompat_Light = 2131427374; - - // aapt resource value: 0x7f0b0094 - public const int Platform_ThemeOverlay_AppCompat = 2131427476; - - // aapt resource value: 0x7f0b0095 - public const int Platform_ThemeOverlay_AppCompat_Dark = 2131427477; - - // aapt resource value: 0x7f0b0096 - public const int Platform_ThemeOverlay_AppCompat_Light = 2131427478; - - // aapt resource value: 0x7f0b002f - public const int Platform_V11_AppCompat = 2131427375; - - // aapt resource value: 0x7f0b0030 - public const int Platform_V11_AppCompat_Light = 2131427376; - - // aapt resource value: 0x7f0b0037 - public const int Platform_V14_AppCompat = 2131427383; - - // aapt resource value: 0x7f0b0038 - public const int Platform_V14_AppCompat_Light = 2131427384; - - // aapt resource value: 0x7f0b0097 - public const int Platform_V21_AppCompat = 2131427479; - - // aapt resource value: 0x7f0b0098 - public const int Platform_V21_AppCompat_Light = 2131427480; - - // aapt resource value: 0x7f0b00ac - public const int Platform_V25_AppCompat = 2131427500; - - // aapt resource value: 0x7f0b00ad - public const int Platform_V25_AppCompat_Light = 2131427501; - - // aapt resource value: 0x7f0b0031 - public const int Platform_Widget_AppCompat_Spinner = 2131427377; - - // aapt resource value: 0x7f0b0040 - public const int RtlOverlay_DialogWindowTitle_AppCompat = 2131427392; - - // aapt resource value: 0x7f0b0041 - public const int RtlOverlay_Widget_AppCompat_ActionBar_TitleItem = 2131427393; - - // aapt resource value: 0x7f0b0042 - public const int RtlOverlay_Widget_AppCompat_DialogTitle_Icon = 2131427394; - - // aapt resource value: 0x7f0b0043 - public const int RtlOverlay_Widget_AppCompat_PopupMenuItem = 2131427395; - - // aapt resource value: 0x7f0b0044 - public const int RtlOverlay_Widget_AppCompat_PopupMenuItem_InternalGroup = 2131427396; - - // aapt resource value: 0x7f0b0045 - public const int RtlOverlay_Widget_AppCompat_PopupMenuItem_Text = 2131427397; - - // aapt resource value: 0x7f0b0046 - public const int RtlOverlay_Widget_AppCompat_Search_DropDown = 2131427398; - - // aapt resource value: 0x7f0b0047 - public const int RtlOverlay_Widget_AppCompat_Search_DropDown_Icon1 = 2131427399; - - // aapt resource value: 0x7f0b0048 - public const int RtlOverlay_Widget_AppCompat_Search_DropDown_Icon2 = 2131427400; - - // aapt resource value: 0x7f0b0049 - public const int RtlOverlay_Widget_AppCompat_Search_DropDown_Query = 2131427401; - - // aapt resource value: 0x7f0b004a - public const int RtlOverlay_Widget_AppCompat_Search_DropDown_Text = 2131427402; - - // aapt resource value: 0x7f0b004b - public const int RtlOverlay_Widget_AppCompat_SearchView_MagIcon = 2131427403; - - // aapt resource value: 0x7f0b004c - public const int RtlUnderlay_Widget_AppCompat_ActionButton = 2131427404; - - // aapt resource value: 0x7f0b004d - public const int RtlUnderlay_Widget_AppCompat_ActionButton_Overflow = 2131427405; - - // aapt resource value: 0x7f0b00dd - public const int TextAppearance_AppCompat = 2131427549; - - // aapt resource value: 0x7f0b00de - public const int TextAppearance_AppCompat_Body1 = 2131427550; - - // aapt resource value: 0x7f0b00df - public const int TextAppearance_AppCompat_Body2 = 2131427551; - - // aapt resource value: 0x7f0b00e0 - public const int TextAppearance_AppCompat_Button = 2131427552; - - // aapt resource value: 0x7f0b00e1 - public const int TextAppearance_AppCompat_Caption = 2131427553; - - // aapt resource value: 0x7f0b00e2 - public const int TextAppearance_AppCompat_Display1 = 2131427554; - - // aapt resource value: 0x7f0b00e3 - public const int TextAppearance_AppCompat_Display2 = 2131427555; - - // aapt resource value: 0x7f0b00e4 - public const int TextAppearance_AppCompat_Display3 = 2131427556; - - // aapt resource value: 0x7f0b00e5 - public const int TextAppearance_AppCompat_Display4 = 2131427557; - - // aapt resource value: 0x7f0b00e6 - public const int TextAppearance_AppCompat_Headline = 2131427558; - - // aapt resource value: 0x7f0b00e7 - public const int TextAppearance_AppCompat_Inverse = 2131427559; - - // aapt resource value: 0x7f0b00e8 - public const int TextAppearance_AppCompat_Large = 2131427560; - - // aapt resource value: 0x7f0b00e9 - public const int TextAppearance_AppCompat_Large_Inverse = 2131427561; - - // aapt resource value: 0x7f0b00ea - public const int TextAppearance_AppCompat_Light_SearchResult_Subtitle = 2131427562; - - // aapt resource value: 0x7f0b00eb - public const int TextAppearance_AppCompat_Light_SearchResult_Title = 2131427563; - - // aapt resource value: 0x7f0b00ec - public const int TextAppearance_AppCompat_Light_Widget_PopupMenu_Large = 2131427564; - - // aapt resource value: 0x7f0b00ed - public const int TextAppearance_AppCompat_Light_Widget_PopupMenu_Small = 2131427565; - - // aapt resource value: 0x7f0b00ee - public const int TextAppearance_AppCompat_Medium = 2131427566; - - // aapt resource value: 0x7f0b00ef - public const int TextAppearance_AppCompat_Medium_Inverse = 2131427567; - - // aapt resource value: 0x7f0b00f0 - public const int TextAppearance_AppCompat_Menu = 2131427568; - - // aapt resource value: 0x7f0b0039 - public const int TextAppearance_AppCompat_Notification = 2131427385; - - // aapt resource value: 0x7f0b0099 - public const int TextAppearance_AppCompat_Notification_Info = 2131427481; - - // aapt resource value: 0x7f0b009a - public const int TextAppearance_AppCompat_Notification_Info_Media = 2131427482; - - // aapt resource value: 0x7f0b00f1 - public const int TextAppearance_AppCompat_Notification_Line2 = 2131427569; - - // aapt resource value: 0x7f0b00f2 - public const int TextAppearance_AppCompat_Notification_Line2_Media = 2131427570; - - // aapt resource value: 0x7f0b009b - public const int TextAppearance_AppCompat_Notification_Media = 2131427483; - - // aapt resource value: 0x7f0b009c - public const int TextAppearance_AppCompat_Notification_Time = 2131427484; - - // aapt resource value: 0x7f0b009d - public const int TextAppearance_AppCompat_Notification_Time_Media = 2131427485; - - // aapt resource value: 0x7f0b003a - public const int TextAppearance_AppCompat_Notification_Title = 2131427386; - - // aapt resource value: 0x7f0b009e - public const int TextAppearance_AppCompat_Notification_Title_Media = 2131427486; - - // aapt resource value: 0x7f0b00f3 - public const int TextAppearance_AppCompat_SearchResult_Subtitle = 2131427571; - - // aapt resource value: 0x7f0b00f4 - public const int TextAppearance_AppCompat_SearchResult_Title = 2131427572; - - // aapt resource value: 0x7f0b00f5 - public const int TextAppearance_AppCompat_Small = 2131427573; - - // aapt resource value: 0x7f0b00f6 - public const int TextAppearance_AppCompat_Small_Inverse = 2131427574; - - // aapt resource value: 0x7f0b00f7 - public const int TextAppearance_AppCompat_Subhead = 2131427575; - - // aapt resource value: 0x7f0b00f8 - public const int TextAppearance_AppCompat_Subhead_Inverse = 2131427576; - - // aapt resource value: 0x7f0b00f9 - public const int TextAppearance_AppCompat_Title = 2131427577; - - // aapt resource value: 0x7f0b00fa - public const int TextAppearance_AppCompat_Title_Inverse = 2131427578; - - // aapt resource value: 0x7f0b00fb - public const int TextAppearance_AppCompat_Widget_ActionBar_Menu = 2131427579; - - // aapt resource value: 0x7f0b00fc - public const int TextAppearance_AppCompat_Widget_ActionBar_Subtitle = 2131427580; - - // aapt resource value: 0x7f0b00fd - public const int TextAppearance_AppCompat_Widget_ActionBar_Subtitle_Inverse = 2131427581; - - // aapt resource value: 0x7f0b00fe - public const int TextAppearance_AppCompat_Widget_ActionBar_Title = 2131427582; - - // aapt resource value: 0x7f0b00ff - public const int TextAppearance_AppCompat_Widget_ActionBar_Title_Inverse = 2131427583; - - // aapt resource value: 0x7f0b0100 - public const int TextAppearance_AppCompat_Widget_ActionMode_Subtitle = 2131427584; - - // aapt resource value: 0x7f0b0101 - public const int TextAppearance_AppCompat_Widget_ActionMode_Subtitle_Inverse = 2131427585; - - // aapt resource value: 0x7f0b0102 - public const int TextAppearance_AppCompat_Widget_ActionMode_Title = 2131427586; - - // aapt resource value: 0x7f0b0103 - public const int TextAppearance_AppCompat_Widget_ActionMode_Title_Inverse = 2131427587; - - // aapt resource value: 0x7f0b0104 - public const int TextAppearance_AppCompat_Widget_Button = 2131427588; - - // aapt resource value: 0x7f0b0105 - public const int TextAppearance_AppCompat_Widget_Button_Borderless_Colored = 2131427589; - - // aapt resource value: 0x7f0b0106 - public const int TextAppearance_AppCompat_Widget_Button_Colored = 2131427590; - - // aapt resource value: 0x7f0b0107 - public const int TextAppearance_AppCompat_Widget_Button_Inverse = 2131427591; - - // aapt resource value: 0x7f0b0108 - public const int TextAppearance_AppCompat_Widget_DropDownItem = 2131427592; - - // aapt resource value: 0x7f0b0109 - public const int TextAppearance_AppCompat_Widget_PopupMenu_Header = 2131427593; - - // aapt resource value: 0x7f0b010a - public const int TextAppearance_AppCompat_Widget_PopupMenu_Large = 2131427594; - - // aapt resource value: 0x7f0b010b - public const int TextAppearance_AppCompat_Widget_PopupMenu_Small = 2131427595; - - // aapt resource value: 0x7f0b010c - public const int TextAppearance_AppCompat_Widget_Switch = 2131427596; - - // aapt resource value: 0x7f0b010d - public const int TextAppearance_AppCompat_Widget_TextView_SpinnerItem = 2131427597; - - // aapt resource value: 0x7f0b0173 - public const int TextAppearance_Design_CollapsingToolbar_Expanded = 2131427699; - - // aapt resource value: 0x7f0b0174 - public const int TextAppearance_Design_Counter = 2131427700; - - // aapt resource value: 0x7f0b0175 - public const int TextAppearance_Design_Counter_Overflow = 2131427701; - - // aapt resource value: 0x7f0b0176 - public const int TextAppearance_Design_Error = 2131427702; - - // aapt resource value: 0x7f0b0177 - public const int TextAppearance_Design_Hint = 2131427703; - - // aapt resource value: 0x7f0b0178 - public const int TextAppearance_Design_Snackbar_Message = 2131427704; - - // aapt resource value: 0x7f0b0179 - public const int TextAppearance_Design_Tab = 2131427705; - - // aapt resource value: 0x7f0b0000 - public const int TextAppearance_MediaRouter_PrimaryText = 2131427328; - - // aapt resource value: 0x7f0b0001 - public const int TextAppearance_MediaRouter_SecondaryText = 2131427329; - - // aapt resource value: 0x7f0b0002 - public const int TextAppearance_MediaRouter_Title = 2131427330; - - // aapt resource value: 0x7f0b003b - public const int TextAppearance_StatusBar_EventContent = 2131427387; - - // aapt resource value: 0x7f0b003c - public const int TextAppearance_StatusBar_EventContent_Info = 2131427388; - - // aapt resource value: 0x7f0b003d - public const int TextAppearance_StatusBar_EventContent_Line2 = 2131427389; - - // aapt resource value: 0x7f0b003e - public const int TextAppearance_StatusBar_EventContent_Time = 2131427390; - - // aapt resource value: 0x7f0b003f - public const int TextAppearance_StatusBar_EventContent_Title = 2131427391; - - // aapt resource value: 0x7f0b010e - public const int TextAppearance_Widget_AppCompat_ExpandedMenu_Item = 2131427598; - - // aapt resource value: 0x7f0b010f - public const int TextAppearance_Widget_AppCompat_Toolbar_Subtitle = 2131427599; - - // aapt resource value: 0x7f0b0110 - public const int TextAppearance_Widget_AppCompat_Toolbar_Title = 2131427600; - - // aapt resource value: 0x7f0b0111 - public const int Theme_AppCompat = 2131427601; - - // aapt resource value: 0x7f0b0112 - public const int Theme_AppCompat_CompactMenu = 2131427602; - - // aapt resource value: 0x7f0b0013 - public const int Theme_AppCompat_DayNight = 2131427347; - - // aapt resource value: 0x7f0b0014 - public const int Theme_AppCompat_DayNight_DarkActionBar = 2131427348; - - // aapt resource value: 0x7f0b0015 - public const int Theme_AppCompat_DayNight_Dialog = 2131427349; - - // aapt resource value: 0x7f0b0016 - public const int Theme_AppCompat_DayNight_Dialog_Alert = 2131427350; - - // aapt resource value: 0x7f0b0017 - public const int Theme_AppCompat_DayNight_Dialog_MinWidth = 2131427351; - - // aapt resource value: 0x7f0b0018 - public const int Theme_AppCompat_DayNight_DialogWhenLarge = 2131427352; - - // aapt resource value: 0x7f0b0019 - public const int Theme_AppCompat_DayNight_NoActionBar = 2131427353; - - // aapt resource value: 0x7f0b0113 - public const int Theme_AppCompat_Dialog = 2131427603; - - // aapt resource value: 0x7f0b0114 - public const int Theme_AppCompat_Dialog_Alert = 2131427604; - - // aapt resource value: 0x7f0b0115 - public const int Theme_AppCompat_Dialog_MinWidth = 2131427605; - - // aapt resource value: 0x7f0b0116 - public const int Theme_AppCompat_DialogWhenLarge = 2131427606; - - // aapt resource value: 0x7f0b0117 - public const int Theme_AppCompat_Light = 2131427607; - - // aapt resource value: 0x7f0b0118 - public const int Theme_AppCompat_Light_DarkActionBar = 2131427608; - - // aapt resource value: 0x7f0b0119 - public const int Theme_AppCompat_Light_Dialog = 2131427609; - - // aapt resource value: 0x7f0b011a - public const int Theme_AppCompat_Light_Dialog_Alert = 2131427610; - - // aapt resource value: 0x7f0b011b - public const int Theme_AppCompat_Light_Dialog_MinWidth = 2131427611; - - // aapt resource value: 0x7f0b011c - public const int Theme_AppCompat_Light_DialogWhenLarge = 2131427612; - - // aapt resource value: 0x7f0b011d - public const int Theme_AppCompat_Light_NoActionBar = 2131427613; - - // aapt resource value: 0x7f0b011e - public const int Theme_AppCompat_NoActionBar = 2131427614; - - // aapt resource value: 0x7f0b017a - public const int Theme_Design = 2131427706; - - // aapt resource value: 0x7f0b017b - public const int Theme_Design_BottomSheetDialog = 2131427707; - - // aapt resource value: 0x7f0b017c - public const int Theme_Design_Light = 2131427708; - - // aapt resource value: 0x7f0b017d - public const int Theme_Design_Light_BottomSheetDialog = 2131427709; - - // aapt resource value: 0x7f0b017e - public const int Theme_Design_Light_NoActionBar = 2131427710; - - // aapt resource value: 0x7f0b017f - public const int Theme_Design_NoActionBar = 2131427711; - - // aapt resource value: 0x7f0b0003 - public const int Theme_MediaRouter = 2131427331; - - // aapt resource value: 0x7f0b0004 - public const int Theme_MediaRouter_Light = 2131427332; - - // aapt resource value: 0x7f0b0005 - public const int Theme_MediaRouter_Light_DarkControlPanel = 2131427333; - - // aapt resource value: 0x7f0b0006 - public const int Theme_MediaRouter_LightControlPanel = 2131427334; - - // aapt resource value: 0x7f0b011f - public const int ThemeOverlay_AppCompat = 2131427615; - - // aapt resource value: 0x7f0b0120 - public const int ThemeOverlay_AppCompat_ActionBar = 2131427616; - - // aapt resource value: 0x7f0b0121 - public const int ThemeOverlay_AppCompat_Dark = 2131427617; - - // aapt resource value: 0x7f0b0122 - public const int ThemeOverlay_AppCompat_Dark_ActionBar = 2131427618; - - // aapt resource value: 0x7f0b0123 - public const int ThemeOverlay_AppCompat_Dialog = 2131427619; - - // aapt resource value: 0x7f0b0124 - public const int ThemeOverlay_AppCompat_Dialog_Alert = 2131427620; - - // aapt resource value: 0x7f0b0125 - public const int ThemeOverlay_AppCompat_Light = 2131427621; - - // aapt resource value: 0x7f0b0007 - public const int ThemeOverlay_MediaRouter_Dark = 2131427335; - - // aapt resource value: 0x7f0b0008 - public const int ThemeOverlay_MediaRouter_Light = 2131427336; - - // aapt resource value: 0x7f0b0126 - public const int Widget_AppCompat_ActionBar = 2131427622; - - // aapt resource value: 0x7f0b0127 - public const int Widget_AppCompat_ActionBar_Solid = 2131427623; - - // aapt resource value: 0x7f0b0128 - public const int Widget_AppCompat_ActionBar_TabBar = 2131427624; - - // aapt resource value: 0x7f0b0129 - public const int Widget_AppCompat_ActionBar_TabText = 2131427625; - - // aapt resource value: 0x7f0b012a - public const int Widget_AppCompat_ActionBar_TabView = 2131427626; - - // aapt resource value: 0x7f0b012b - public const int Widget_AppCompat_ActionButton = 2131427627; - - // aapt resource value: 0x7f0b012c - public const int Widget_AppCompat_ActionButton_CloseMode = 2131427628; - - // aapt resource value: 0x7f0b012d - public const int Widget_AppCompat_ActionButton_Overflow = 2131427629; - - // aapt resource value: 0x7f0b012e - public const int Widget_AppCompat_ActionMode = 2131427630; - - // aapt resource value: 0x7f0b012f - public const int Widget_AppCompat_ActivityChooserView = 2131427631; - - // aapt resource value: 0x7f0b0130 - public const int Widget_AppCompat_AutoCompleteTextView = 2131427632; - - // aapt resource value: 0x7f0b0131 - public const int Widget_AppCompat_Button = 2131427633; - - // aapt resource value: 0x7f0b0132 - public const int Widget_AppCompat_Button_Borderless = 2131427634; - - // aapt resource value: 0x7f0b0133 - public const int Widget_AppCompat_Button_Borderless_Colored = 2131427635; - - // aapt resource value: 0x7f0b0134 - public const int Widget_AppCompat_Button_ButtonBar_AlertDialog = 2131427636; - - // aapt resource value: 0x7f0b0135 - public const int Widget_AppCompat_Button_Colored = 2131427637; - - // aapt resource value: 0x7f0b0136 - public const int Widget_AppCompat_Button_Small = 2131427638; - - // aapt resource value: 0x7f0b0137 - public const int Widget_AppCompat_ButtonBar = 2131427639; - - // aapt resource value: 0x7f0b0138 - public const int Widget_AppCompat_ButtonBar_AlertDialog = 2131427640; - - // aapt resource value: 0x7f0b0139 - public const int Widget_AppCompat_CompoundButton_CheckBox = 2131427641; - - // aapt resource value: 0x7f0b013a - public const int Widget_AppCompat_CompoundButton_RadioButton = 2131427642; - - // aapt resource value: 0x7f0b013b - public const int Widget_AppCompat_CompoundButton_Switch = 2131427643; - - // aapt resource value: 0x7f0b013c - public const int Widget_AppCompat_DrawerArrowToggle = 2131427644; - - // aapt resource value: 0x7f0b013d - public const int Widget_AppCompat_DropDownItem_Spinner = 2131427645; - - // aapt resource value: 0x7f0b013e - public const int Widget_AppCompat_EditText = 2131427646; - - // aapt resource value: 0x7f0b013f - public const int Widget_AppCompat_ImageButton = 2131427647; - - // aapt resource value: 0x7f0b0140 - public const int Widget_AppCompat_Light_ActionBar = 2131427648; - - // aapt resource value: 0x7f0b0141 - public const int Widget_AppCompat_Light_ActionBar_Solid = 2131427649; - - // aapt resource value: 0x7f0b0142 - public const int Widget_AppCompat_Light_ActionBar_Solid_Inverse = 2131427650; - - // aapt resource value: 0x7f0b0143 - public const int Widget_AppCompat_Light_ActionBar_TabBar = 2131427651; - - // aapt resource value: 0x7f0b0144 - public const int Widget_AppCompat_Light_ActionBar_TabBar_Inverse = 2131427652; - - // aapt resource value: 0x7f0b0145 - public const int Widget_AppCompat_Light_ActionBar_TabText = 2131427653; - - // aapt resource value: 0x7f0b0146 - public const int Widget_AppCompat_Light_ActionBar_TabText_Inverse = 2131427654; - - // aapt resource value: 0x7f0b0147 - public const int Widget_AppCompat_Light_ActionBar_TabView = 2131427655; - - // aapt resource value: 0x7f0b0148 - public const int Widget_AppCompat_Light_ActionBar_TabView_Inverse = 2131427656; - - // aapt resource value: 0x7f0b0149 - public const int Widget_AppCompat_Light_ActionButton = 2131427657; - - // aapt resource value: 0x7f0b014a - public const int Widget_AppCompat_Light_ActionButton_CloseMode = 2131427658; - - // aapt resource value: 0x7f0b014b - public const int Widget_AppCompat_Light_ActionButton_Overflow = 2131427659; - - // aapt resource value: 0x7f0b014c - public const int Widget_AppCompat_Light_ActionMode_Inverse = 2131427660; - - // aapt resource value: 0x7f0b014d - public const int Widget_AppCompat_Light_ActivityChooserView = 2131427661; - - // aapt resource value: 0x7f0b014e - public const int Widget_AppCompat_Light_AutoCompleteTextView = 2131427662; - - // aapt resource value: 0x7f0b014f - public const int Widget_AppCompat_Light_DropDownItem_Spinner = 2131427663; - - // aapt resource value: 0x7f0b0150 - public const int Widget_AppCompat_Light_ListPopupWindow = 2131427664; - - // aapt resource value: 0x7f0b0151 - public const int Widget_AppCompat_Light_ListView_DropDown = 2131427665; - - // aapt resource value: 0x7f0b0152 - public const int Widget_AppCompat_Light_PopupMenu = 2131427666; - - // aapt resource value: 0x7f0b0153 - public const int Widget_AppCompat_Light_PopupMenu_Overflow = 2131427667; - - // aapt resource value: 0x7f0b0154 - public const int Widget_AppCompat_Light_SearchView = 2131427668; - - // aapt resource value: 0x7f0b0155 - public const int Widget_AppCompat_Light_Spinner_DropDown_ActionBar = 2131427669; - - // aapt resource value: 0x7f0b0156 - public const int Widget_AppCompat_ListMenuView = 2131427670; - - // aapt resource value: 0x7f0b0157 - public const int Widget_AppCompat_ListPopupWindow = 2131427671; - - // aapt resource value: 0x7f0b0158 - public const int Widget_AppCompat_ListView = 2131427672; - - // aapt resource value: 0x7f0b0159 - public const int Widget_AppCompat_ListView_DropDown = 2131427673; - - // aapt resource value: 0x7f0b015a - public const int Widget_AppCompat_ListView_Menu = 2131427674; - - // aapt resource value: 0x7f0b009f - public const int Widget_AppCompat_NotificationActionContainer = 2131427487; - - // aapt resource value: 0x7f0b00a0 - public const int Widget_AppCompat_NotificationActionText = 2131427488; - - // aapt resource value: 0x7f0b015b - public const int Widget_AppCompat_PopupMenu = 2131427675; - - // aapt resource value: 0x7f0b015c - public const int Widget_AppCompat_PopupMenu_Overflow = 2131427676; - - // aapt resource value: 0x7f0b015d - public const int Widget_AppCompat_PopupWindow = 2131427677; - - // aapt resource value: 0x7f0b015e - public const int Widget_AppCompat_ProgressBar = 2131427678; - - // aapt resource value: 0x7f0b015f - public const int Widget_AppCompat_ProgressBar_Horizontal = 2131427679; - - // aapt resource value: 0x7f0b0160 - public const int Widget_AppCompat_RatingBar = 2131427680; - - // aapt resource value: 0x7f0b0161 - public const int Widget_AppCompat_RatingBar_Indicator = 2131427681; - - // aapt resource value: 0x7f0b0162 - public const int Widget_AppCompat_RatingBar_Small = 2131427682; - - // aapt resource value: 0x7f0b0163 - public const int Widget_AppCompat_SearchView = 2131427683; - - // aapt resource value: 0x7f0b0164 - public const int Widget_AppCompat_SearchView_ActionBar = 2131427684; - - // aapt resource value: 0x7f0b0165 - public const int Widget_AppCompat_SeekBar = 2131427685; - - // aapt resource value: 0x7f0b0166 - public const int Widget_AppCompat_SeekBar_Discrete = 2131427686; - - // aapt resource value: 0x7f0b0167 - public const int Widget_AppCompat_Spinner = 2131427687; - - // aapt resource value: 0x7f0b0168 - public const int Widget_AppCompat_Spinner_DropDown = 2131427688; - - // aapt resource value: 0x7f0b0169 - public const int Widget_AppCompat_Spinner_DropDown_ActionBar = 2131427689; - - // aapt resource value: 0x7f0b016a - public const int Widget_AppCompat_Spinner_Underlined = 2131427690; - - // aapt resource value: 0x7f0b016b - public const int Widget_AppCompat_TextView_SpinnerItem = 2131427691; - - // aapt resource value: 0x7f0b016c - public const int Widget_AppCompat_Toolbar = 2131427692; - - // aapt resource value: 0x7f0b016d - public const int Widget_AppCompat_Toolbar_Button_Navigation = 2131427693; - - // aapt resource value: 0x7f0b016f - public const int Widget_Design_AppBarLayout = 2131427695; - - // aapt resource value: 0x7f0b0180 - public const int Widget_Design_BottomNavigationView = 2131427712; - - // aapt resource value: 0x7f0b0181 - public const int Widget_Design_BottomSheet_Modal = 2131427713; - - // aapt resource value: 0x7f0b0182 - public const int Widget_Design_CollapsingToolbar = 2131427714; - - // aapt resource value: 0x7f0b0183 - public const int Widget_Design_CoordinatorLayout = 2131427715; - - // aapt resource value: 0x7f0b0184 - public const int Widget_Design_FloatingActionButton = 2131427716; - - // aapt resource value: 0x7f0b0185 - public const int Widget_Design_NavigationView = 2131427717; - - // aapt resource value: 0x7f0b0186 - public const int Widget_Design_ScrimInsetsFrameLayout = 2131427718; - - // aapt resource value: 0x7f0b0187 - public const int Widget_Design_Snackbar = 2131427719; - - // aapt resource value: 0x7f0b016e - public const int Widget_Design_TabLayout = 2131427694; - - // aapt resource value: 0x7f0b0188 - public const int Widget_Design_TextInputLayout = 2131427720; - - // aapt resource value: 0x7f0b0009 - public const int Widget_MediaRouter_Light_MediaRouteButton = 2131427337; - - // aapt resource value: 0x7f0b000a - public const int Widget_MediaRouter_MediaRouteButton = 2131427338; - - static Style() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Style() - { - } - } - - public partial class Styleable - { - - public static int[] ActionBar = new int[] { - 2130771996, - 2130771998, - 2130771999, - 2130772000, - 2130772001, - 2130772002, - 2130772003, - 2130772004, - 2130772005, - 2130772006, - 2130772007, - 2130772008, - 2130772009, - 2130772010, - 2130772011, - 2130772012, - 2130772013, - 2130772014, - 2130772015, - 2130772016, - 2130772017, - 2130772018, - 2130772019, - 2130772020, - 2130772021, - 2130772022, - 2130772023, - 2130772024, - 2130772086}; - - // aapt resource value: 10 - public const int ActionBar_background = 10; - - // aapt resource value: 12 - public const int ActionBar_backgroundSplit = 12; - - // aapt resource value: 11 - public const int ActionBar_backgroundStacked = 11; - - // aapt resource value: 21 - public const int ActionBar_contentInsetEnd = 21; - - // aapt resource value: 25 - public const int ActionBar_contentInsetEndWithActions = 25; - - // aapt resource value: 22 - public const int ActionBar_contentInsetLeft = 22; - - // aapt resource value: 23 - public const int ActionBar_contentInsetRight = 23; - - // aapt resource value: 20 - public const int ActionBar_contentInsetStart = 20; - - // aapt resource value: 24 - public const int ActionBar_contentInsetStartWithNavigation = 24; - - // aapt resource value: 13 - public const int ActionBar_customNavigationLayout = 13; - - // aapt resource value: 3 - public const int ActionBar_displayOptions = 3; - - // aapt resource value: 9 - public const int ActionBar_divider = 9; - - // aapt resource value: 26 - public const int ActionBar_elevation = 26; - - // aapt resource value: 0 - public const int ActionBar_height = 0; - - // aapt resource value: 19 - public const int ActionBar_hideOnContentScroll = 19; - - // aapt resource value: 28 - public const int ActionBar_homeAsUpIndicator = 28; - - // aapt resource value: 14 - public const int ActionBar_homeLayout = 14; - - // aapt resource value: 7 - public const int ActionBar_icon = 7; - - // aapt resource value: 16 - public const int ActionBar_indeterminateProgressStyle = 16; - - // aapt resource value: 18 - public const int ActionBar_itemPadding = 18; - - // aapt resource value: 8 - public const int ActionBar_logo = 8; - - // aapt resource value: 2 - public const int ActionBar_navigationMode = 2; - - // aapt resource value: 27 - public const int ActionBar_popupTheme = 27; - - // aapt resource value: 17 - public const int ActionBar_progressBarPadding = 17; - - // aapt resource value: 15 - public const int ActionBar_progressBarStyle = 15; - - // aapt resource value: 4 - public const int ActionBar_subtitle = 4; - - // aapt resource value: 6 - public const int ActionBar_subtitleTextStyle = 6; - - // aapt resource value: 1 - public const int ActionBar_title = 1; - - // aapt resource value: 5 - public const int ActionBar_titleTextStyle = 5; - - public static int[] ActionBarLayout = new int[] { - 16842931}; - - // aapt resource value: 0 - public const int ActionBarLayout_android_layout_gravity = 0; - - public static int[] ActionMenuItemView = new int[] { - 16843071}; - - // aapt resource value: 0 - public const int ActionMenuItemView_android_minWidth = 0; - - public static int[] ActionMenuView; - - public static int[] ActionMode = new int[] { - 2130771996, - 2130772002, - 2130772003, - 2130772007, - 2130772009, - 2130772025}; - - // aapt resource value: 3 - public const int ActionMode_background = 3; - - // aapt resource value: 4 - public const int ActionMode_backgroundSplit = 4; - - // aapt resource value: 5 - public const int ActionMode_closeItemLayout = 5; - - // aapt resource value: 0 - public const int ActionMode_height = 0; - - // aapt resource value: 2 - public const int ActionMode_subtitleTextStyle = 2; - - // aapt resource value: 1 - public const int ActionMode_titleTextStyle = 1; - - public static int[] ActivityChooserView = new int[] { - 2130772026, - 2130772027}; - - // aapt resource value: 1 - public const int ActivityChooserView_expandActivityOverflowButtonDrawable = 1; - - // aapt resource value: 0 - public const int ActivityChooserView_initialActivityCount = 0; - - public static int[] AlertDialog = new int[] { - 16842994, - 2130772028, - 2130772029, - 2130772030, - 2130772031, - 2130772032, - 2130772033}; - - // aapt resource value: 0 - public const int AlertDialog_android_layout = 0; - - // aapt resource value: 1 - public const int AlertDialog_buttonPanelSideLayout = 1; - - // aapt resource value: 5 - public const int AlertDialog_listItemLayout = 5; - - // aapt resource value: 2 - public const int AlertDialog_listLayout = 2; - - // aapt resource value: 3 - public const int AlertDialog_multiChoiceItemLayout = 3; - - // aapt resource value: 6 - public const int AlertDialog_showTitle = 6; - - // aapt resource value: 4 - public const int AlertDialog_singleChoiceItemLayout = 4; - - public static int[] AppBarLayout = new int[] { - 16842964, - 2130772023, - 2130772223}; - - // aapt resource value: 0 - public const int AppBarLayout_android_background = 0; - - // aapt resource value: 1 - public const int AppBarLayout_elevation = 1; - - // aapt resource value: 2 - public const int AppBarLayout_expanded = 2; - - public static int[] AppBarLayoutStates = new int[] { - 2130772224, - 2130772225}; - - // aapt resource value: 0 - public const int AppBarLayoutStates_state_collapsed = 0; - - // aapt resource value: 1 - public const int AppBarLayoutStates_state_collapsible = 1; - - public static int[] AppBarLayout_Layout = new int[] { - 2130772226, - 2130772227}; - - // aapt resource value: 0 - public const int AppBarLayout_Layout_layout_scrollFlags = 0; - - // aapt resource value: 1 - public const int AppBarLayout_Layout_layout_scrollInterpolator = 1; - - public static int[] AppCompatImageView = new int[] { - 16843033, - 2130772034}; - - // aapt resource value: 0 - public const int AppCompatImageView_android_src = 0; - - // aapt resource value: 1 - public const int AppCompatImageView_srcCompat = 1; - - public static int[] AppCompatSeekBar = new int[] { - 16843074, - 2130772035, - 2130772036, - 2130772037}; - - // aapt resource value: 0 - public const int AppCompatSeekBar_android_thumb = 0; - - // aapt resource value: 1 - public const int AppCompatSeekBar_tickMark = 1; - - // aapt resource value: 2 - public const int AppCompatSeekBar_tickMarkTint = 2; - - // aapt resource value: 3 - public const int AppCompatSeekBar_tickMarkTintMode = 3; - - public static int[] AppCompatTextHelper = new int[] { - 16842804, - 16843117, - 16843118, - 16843119, - 16843120, - 16843666, - 16843667}; - - // aapt resource value: 2 - public const int AppCompatTextHelper_android_drawableBottom = 2; - - // aapt resource value: 6 - public const int AppCompatTextHelper_android_drawableEnd = 6; - - // aapt resource value: 3 - public const int AppCompatTextHelper_android_drawableLeft = 3; - - // aapt resource value: 4 - public const int AppCompatTextHelper_android_drawableRight = 4; - - // aapt resource value: 5 - public const int AppCompatTextHelper_android_drawableStart = 5; - - // aapt resource value: 1 - public const int AppCompatTextHelper_android_drawableTop = 1; - - // aapt resource value: 0 - public const int AppCompatTextHelper_android_textAppearance = 0; - - public static int[] AppCompatTextView = new int[] { - 16842804, - 2130772038}; - - // aapt resource value: 0 - public const int AppCompatTextView_android_textAppearance = 0; - - // aapt resource value: 1 - public const int AppCompatTextView_textAllCaps = 1; - - public static int[] AppCompatTheme = new int[] { - 16842839, - 16842926, - 2130772039, - 2130772040, - 2130772041, - 2130772042, - 2130772043, - 2130772044, - 2130772045, - 2130772046, - 2130772047, - 2130772048, - 2130772049, - 2130772050, - 2130772051, - 2130772052, - 2130772053, - 2130772054, - 2130772055, - 2130772056, - 2130772057, - 2130772058, - 2130772059, - 2130772060, - 2130772061, - 2130772062, - 2130772063, - 2130772064, - 2130772065, - 2130772066, - 2130772067, - 2130772068, - 2130772069, - 2130772070, - 2130772071, - 2130772072, - 2130772073, - 2130772074, - 2130772075, - 2130772076, - 2130772077, - 2130772078, - 2130772079, - 2130772080, - 2130772081, - 2130772082, - 2130772083, - 2130772084, - 2130772085, - 2130772086, - 2130772087, - 2130772088, - 2130772089, - 2130772090, - 2130772091, - 2130772092, - 2130772093, - 2130772094, - 2130772095, - 2130772096, - 2130772097, - 2130772098, - 2130772099, - 2130772100, - 2130772101, - 2130772102, - 2130772103, - 2130772104, - 2130772105, - 2130772106, - 2130772107, - 2130772108, - 2130772109, - 2130772110, - 2130772111, - 2130772112, - 2130772113, - 2130772114, - 2130772115, - 2130772116, - 2130772117, - 2130772118, - 2130772119, - 2130772120, - 2130772121, - 2130772122, - 2130772123, - 2130772124, - 2130772125, - 2130772126, - 2130772127, - 2130772128, - 2130772129, - 2130772130, - 2130772131, - 2130772132, - 2130772133, - 2130772134, - 2130772135, - 2130772136, - 2130772137, - 2130772138, - 2130772139, - 2130772140, - 2130772141, - 2130772142, - 2130772143, - 2130772144, - 2130772145, - 2130772146, - 2130772147, - 2130772148, - 2130772149, - 2130772150, - 2130772151}; - - // aapt resource value: 23 - public const int AppCompatTheme_actionBarDivider = 23; - - // aapt resource value: 24 - public const int AppCompatTheme_actionBarItemBackground = 24; - - // aapt resource value: 17 - public const int AppCompatTheme_actionBarPopupTheme = 17; - - // aapt resource value: 22 - public const int AppCompatTheme_actionBarSize = 22; - - // aapt resource value: 19 - public const int AppCompatTheme_actionBarSplitStyle = 19; - - // aapt resource value: 18 - public const int AppCompatTheme_actionBarStyle = 18; - - // aapt resource value: 13 - public const int AppCompatTheme_actionBarTabBarStyle = 13; - - // aapt resource value: 12 - public const int AppCompatTheme_actionBarTabStyle = 12; - - // aapt resource value: 14 - public const int AppCompatTheme_actionBarTabTextStyle = 14; - - // aapt resource value: 20 - public const int AppCompatTheme_actionBarTheme = 20; - - // aapt resource value: 21 - public const int AppCompatTheme_actionBarWidgetTheme = 21; - - // aapt resource value: 50 - public const int AppCompatTheme_actionButtonStyle = 50; - - // aapt resource value: 46 - public const int AppCompatTheme_actionDropDownStyle = 46; - - // aapt resource value: 25 - public const int AppCompatTheme_actionMenuTextAppearance = 25; - - // aapt resource value: 26 - public const int AppCompatTheme_actionMenuTextColor = 26; - - // aapt resource value: 29 - public const int AppCompatTheme_actionModeBackground = 29; - - // aapt resource value: 28 - public const int AppCompatTheme_actionModeCloseButtonStyle = 28; - - // aapt resource value: 31 - public const int AppCompatTheme_actionModeCloseDrawable = 31; - - // aapt resource value: 33 - public const int AppCompatTheme_actionModeCopyDrawable = 33; - - // aapt resource value: 32 - public const int AppCompatTheme_actionModeCutDrawable = 32; - - // aapt resource value: 37 - public const int AppCompatTheme_actionModeFindDrawable = 37; - - // aapt resource value: 34 - public const int AppCompatTheme_actionModePasteDrawable = 34; - - // aapt resource value: 39 - public const int AppCompatTheme_actionModePopupWindowStyle = 39; - - // aapt resource value: 35 - public const int AppCompatTheme_actionModeSelectAllDrawable = 35; - - // aapt resource value: 36 - public const int AppCompatTheme_actionModeShareDrawable = 36; - - // aapt resource value: 30 - public const int AppCompatTheme_actionModeSplitBackground = 30; - - // aapt resource value: 27 - public const int AppCompatTheme_actionModeStyle = 27; - - // aapt resource value: 38 - public const int AppCompatTheme_actionModeWebSearchDrawable = 38; - - // aapt resource value: 15 - public const int AppCompatTheme_actionOverflowButtonStyle = 15; - - // aapt resource value: 16 - public const int AppCompatTheme_actionOverflowMenuStyle = 16; - - // aapt resource value: 58 - public const int AppCompatTheme_activityChooserViewStyle = 58; - - // aapt resource value: 94 - public const int AppCompatTheme_alertDialogButtonGroupStyle = 94; - - // aapt resource value: 95 - public const int AppCompatTheme_alertDialogCenterButtons = 95; - - // aapt resource value: 93 - public const int AppCompatTheme_alertDialogStyle = 93; - - // aapt resource value: 96 - public const int AppCompatTheme_alertDialogTheme = 96; - - // aapt resource value: 1 - public const int AppCompatTheme_android_windowAnimationStyle = 1; - - // aapt resource value: 0 - public const int AppCompatTheme_android_windowIsFloating = 0; - - // aapt resource value: 101 - public const int AppCompatTheme_autoCompleteTextViewStyle = 101; - - // aapt resource value: 55 - public const int AppCompatTheme_borderlessButtonStyle = 55; - - // aapt resource value: 52 - public const int AppCompatTheme_buttonBarButtonStyle = 52; - - // aapt resource value: 99 - public const int AppCompatTheme_buttonBarNegativeButtonStyle = 99; - - // aapt resource value: 100 - public const int AppCompatTheme_buttonBarNeutralButtonStyle = 100; - - // aapt resource value: 98 - public const int AppCompatTheme_buttonBarPositiveButtonStyle = 98; - - // aapt resource value: 51 - public const int AppCompatTheme_buttonBarStyle = 51; - - // aapt resource value: 102 - public const int AppCompatTheme_buttonStyle = 102; - - // aapt resource value: 103 - public const int AppCompatTheme_buttonStyleSmall = 103; - - // aapt resource value: 104 - public const int AppCompatTheme_checkboxStyle = 104; - - // aapt resource value: 105 - public const int AppCompatTheme_checkedTextViewStyle = 105; - - // aapt resource value: 85 - public const int AppCompatTheme_colorAccent = 85; - - // aapt resource value: 92 - public const int AppCompatTheme_colorBackgroundFloating = 92; - - // aapt resource value: 89 - public const int AppCompatTheme_colorButtonNormal = 89; - - // aapt resource value: 87 - public const int AppCompatTheme_colorControlActivated = 87; - - // aapt resource value: 88 - public const int AppCompatTheme_colorControlHighlight = 88; - - // aapt resource value: 86 - public const int AppCompatTheme_colorControlNormal = 86; - - // aapt resource value: 83 - public const int AppCompatTheme_colorPrimary = 83; - - // aapt resource value: 84 - public const int AppCompatTheme_colorPrimaryDark = 84; - - // aapt resource value: 90 - public const int AppCompatTheme_colorSwitchThumbNormal = 90; - - // aapt resource value: 91 - public const int AppCompatTheme_controlBackground = 91; - - // aapt resource value: 44 - public const int AppCompatTheme_dialogPreferredPadding = 44; - - // aapt resource value: 43 - public const int AppCompatTheme_dialogTheme = 43; - - // aapt resource value: 57 - public const int AppCompatTheme_dividerHorizontal = 57; - - // aapt resource value: 56 - public const int AppCompatTheme_dividerVertical = 56; - - // aapt resource value: 75 - public const int AppCompatTheme_dropDownListViewStyle = 75; - - // aapt resource value: 47 - public const int AppCompatTheme_dropdownListPreferredItemHeight = 47; - - // aapt resource value: 64 - public const int AppCompatTheme_editTextBackground = 64; - - // aapt resource value: 63 - public const int AppCompatTheme_editTextColor = 63; - - // aapt resource value: 106 - public const int AppCompatTheme_editTextStyle = 106; - - // aapt resource value: 49 - public const int AppCompatTheme_homeAsUpIndicator = 49; - - // aapt resource value: 65 - public const int AppCompatTheme_imageButtonStyle = 65; - - // aapt resource value: 82 - public const int AppCompatTheme_listChoiceBackgroundIndicator = 82; - - // aapt resource value: 45 - public const int AppCompatTheme_listDividerAlertDialog = 45; - - // aapt resource value: 114 - public const int AppCompatTheme_listMenuViewStyle = 114; - - // aapt resource value: 76 - public const int AppCompatTheme_listPopupWindowStyle = 76; - - // aapt resource value: 70 - public const int AppCompatTheme_listPreferredItemHeight = 70; - - // aapt resource value: 72 - public const int AppCompatTheme_listPreferredItemHeightLarge = 72; - - // aapt resource value: 71 - public const int AppCompatTheme_listPreferredItemHeightSmall = 71; - - // aapt resource value: 73 - public const int AppCompatTheme_listPreferredItemPaddingLeft = 73; - - // aapt resource value: 74 - public const int AppCompatTheme_listPreferredItemPaddingRight = 74; - - // aapt resource value: 79 - public const int AppCompatTheme_panelBackground = 79; - - // aapt resource value: 81 - public const int AppCompatTheme_panelMenuListTheme = 81; - - // aapt resource value: 80 - public const int AppCompatTheme_panelMenuListWidth = 80; - - // aapt resource value: 61 - public const int AppCompatTheme_popupMenuStyle = 61; - - // aapt resource value: 62 - public const int AppCompatTheme_popupWindowStyle = 62; - - // aapt resource value: 107 - public const int AppCompatTheme_radioButtonStyle = 107; - - // aapt resource value: 108 - public const int AppCompatTheme_ratingBarStyle = 108; - - // aapt resource value: 109 - public const int AppCompatTheme_ratingBarStyleIndicator = 109; - - // aapt resource value: 110 - public const int AppCompatTheme_ratingBarStyleSmall = 110; - - // aapt resource value: 69 - public const int AppCompatTheme_searchViewStyle = 69; - - // aapt resource value: 111 - public const int AppCompatTheme_seekBarStyle = 111; - - // aapt resource value: 53 - public const int AppCompatTheme_selectableItemBackground = 53; - - // aapt resource value: 54 - public const int AppCompatTheme_selectableItemBackgroundBorderless = 54; - - // aapt resource value: 48 - public const int AppCompatTheme_spinnerDropDownItemStyle = 48; - - // aapt resource value: 112 - public const int AppCompatTheme_spinnerStyle = 112; - - // aapt resource value: 113 - public const int AppCompatTheme_switchStyle = 113; - - // aapt resource value: 40 - public const int AppCompatTheme_textAppearanceLargePopupMenu = 40; - - // aapt resource value: 77 - public const int AppCompatTheme_textAppearanceListItem = 77; - - // aapt resource value: 78 - public const int AppCompatTheme_textAppearanceListItemSmall = 78; - - // aapt resource value: 42 - public const int AppCompatTheme_textAppearancePopupMenuHeader = 42; - - // aapt resource value: 67 - public const int AppCompatTheme_textAppearanceSearchResultSubtitle = 67; - - // aapt resource value: 66 - public const int AppCompatTheme_textAppearanceSearchResultTitle = 66; - - // aapt resource value: 41 - public const int AppCompatTheme_textAppearanceSmallPopupMenu = 41; - - // aapt resource value: 97 - public const int AppCompatTheme_textColorAlertDialogListItem = 97; - - // aapt resource value: 68 - public const int AppCompatTheme_textColorSearchUrl = 68; - - // aapt resource value: 60 - public const int AppCompatTheme_toolbarNavigationButtonStyle = 60; - - // aapt resource value: 59 - public const int AppCompatTheme_toolbarStyle = 59; - - // aapt resource value: 2 - public const int AppCompatTheme_windowActionBar = 2; - - // aapt resource value: 4 - public const int AppCompatTheme_windowActionBarOverlay = 4; - - // aapt resource value: 5 - public const int AppCompatTheme_windowActionModeOverlay = 5; - - // aapt resource value: 9 - public const int AppCompatTheme_windowFixedHeightMajor = 9; - - // aapt resource value: 7 - public const int AppCompatTheme_windowFixedHeightMinor = 7; - - // aapt resource value: 6 - public const int AppCompatTheme_windowFixedWidthMajor = 6; - - // aapt resource value: 8 - public const int AppCompatTheme_windowFixedWidthMinor = 8; - - // aapt resource value: 10 - public const int AppCompatTheme_windowMinWidthMajor = 10; - - // aapt resource value: 11 - public const int AppCompatTheme_windowMinWidthMinor = 11; - - // aapt resource value: 3 - public const int AppCompatTheme_windowNoTitle = 3; - - public static int[] BottomNavigationView = new int[] { - 2130772023, - 2130772266, - 2130772267, - 2130772268, - 2130772269}; - - // aapt resource value: 0 - public const int BottomNavigationView_elevation = 0; - - // aapt resource value: 4 - public const int BottomNavigationView_itemBackground = 4; - - // aapt resource value: 2 - public const int BottomNavigationView_itemIconTint = 2; - - // aapt resource value: 3 - public const int BottomNavigationView_itemTextColor = 3; - - // aapt resource value: 1 - public const int BottomNavigationView_menu = 1; - - public static int[] BottomSheetBehavior_Layout = new int[] { - 2130772228, - 2130772229, - 2130772230}; - - // aapt resource value: 1 - public const int BottomSheetBehavior_Layout_behavior_hideable = 1; - - // aapt resource value: 0 - public const int BottomSheetBehavior_Layout_behavior_peekHeight = 0; - - // aapt resource value: 2 - public const int BottomSheetBehavior_Layout_behavior_skipCollapsed = 2; - - public static int[] ButtonBarLayout = new int[] { - 2130772152}; - - // aapt resource value: 0 - public const int ButtonBarLayout_allowStacking = 0; - - public static int[] CardView = new int[] { - 16843071, - 16843072, - 2130771984, - 2130771985, - 2130771986, - 2130771987, - 2130771988, - 2130771989, - 2130771990, - 2130771991, - 2130771992, - 2130771993, - 2130771994}; - - // aapt resource value: 1 - public const int CardView_android_minHeight = 1; - - // aapt resource value: 0 - public const int CardView_android_minWidth = 0; - - // aapt resource value: 2 - public const int CardView_cardBackgroundColor = 2; - - // aapt resource value: 3 - public const int CardView_cardCornerRadius = 3; - - // aapt resource value: 4 - public const int CardView_cardElevation = 4; - - // aapt resource value: 5 - public const int CardView_cardMaxElevation = 5; - - // aapt resource value: 7 - public const int CardView_cardPreventCornerOverlap = 7; - - // aapt resource value: 6 - public const int CardView_cardUseCompatPadding = 6; - - // aapt resource value: 8 - public const int CardView_contentPadding = 8; - - // aapt resource value: 12 - public const int CardView_contentPaddingBottom = 12; - - // aapt resource value: 9 - public const int CardView_contentPaddingLeft = 9; - - // aapt resource value: 10 - public const int CardView_contentPaddingRight = 10; - - // aapt resource value: 11 - public const int CardView_contentPaddingTop = 11; - - public static int[] CollapsingToolbarLayout = new int[] { - 2130771998, - 2130772231, - 2130772232, - 2130772233, - 2130772234, - 2130772235, - 2130772236, - 2130772237, - 2130772238, - 2130772239, - 2130772240, - 2130772241, - 2130772242, - 2130772243, - 2130772244, - 2130772245}; - - // aapt resource value: 13 - public const int CollapsingToolbarLayout_collapsedTitleGravity = 13; - - // aapt resource value: 7 - public const int CollapsingToolbarLayout_collapsedTitleTextAppearance = 7; - - // aapt resource value: 8 - public const int CollapsingToolbarLayout_contentScrim = 8; - - // aapt resource value: 14 - public const int CollapsingToolbarLayout_expandedTitleGravity = 14; - - // aapt resource value: 1 - public const int CollapsingToolbarLayout_expandedTitleMargin = 1; - - // aapt resource value: 5 - public const int CollapsingToolbarLayout_expandedTitleMarginBottom = 5; - - // aapt resource value: 4 - public const int CollapsingToolbarLayout_expandedTitleMarginEnd = 4; - - // aapt resource value: 2 - public const int CollapsingToolbarLayout_expandedTitleMarginStart = 2; - - // aapt resource value: 3 - public const int CollapsingToolbarLayout_expandedTitleMarginTop = 3; - - // aapt resource value: 6 - public const int CollapsingToolbarLayout_expandedTitleTextAppearance = 6; - - // aapt resource value: 12 - public const int CollapsingToolbarLayout_scrimAnimationDuration = 12; - - // aapt resource value: 11 - public const int CollapsingToolbarLayout_scrimVisibleHeightTrigger = 11; - - // aapt resource value: 9 - public const int CollapsingToolbarLayout_statusBarScrim = 9; - - // aapt resource value: 0 - public const int CollapsingToolbarLayout_title = 0; - - // aapt resource value: 15 - public const int CollapsingToolbarLayout_titleEnabled = 15; - - // aapt resource value: 10 - public const int CollapsingToolbarLayout_toolbarId = 10; - - public static int[] CollapsingToolbarLayout_Layout = new int[] { - 2130772246, - 2130772247}; - - // aapt resource value: 0 - public const int CollapsingToolbarLayout_Layout_layout_collapseMode = 0; - - // aapt resource value: 1 - public const int CollapsingToolbarLayout_Layout_layout_collapseParallaxMultiplier = 1; - - public static int[] ColorStateListItem = new int[] { - 16843173, - 16843551, - 2130772153}; - - // aapt resource value: 2 - public const int ColorStateListItem_alpha = 2; - - // aapt resource value: 1 - public const int ColorStateListItem_android_alpha = 1; - - // aapt resource value: 0 - public const int ColorStateListItem_android_color = 0; - - public static int[] CompoundButton = new int[] { - 16843015, - 2130772154, - 2130772155}; - - // aapt resource value: 0 - public const int CompoundButton_android_button = 0; - - // aapt resource value: 1 - public const int CompoundButton_buttonTint = 1; - - // aapt resource value: 2 - public const int CompoundButton_buttonTintMode = 2; - - public static int[] CoordinatorLayout = new int[] { - 2130772248, - 2130772249}; - - // aapt resource value: 0 - public const int CoordinatorLayout_keylines = 0; - - // aapt resource value: 1 - public const int CoordinatorLayout_statusBarBackground = 1; - - public static int[] CoordinatorLayout_Layout = new int[] { - 16842931, - 2130772250, - 2130772251, - 2130772252, - 2130772253, - 2130772254, - 2130772255}; - - // aapt resource value: 0 - public const int CoordinatorLayout_Layout_android_layout_gravity = 0; - - // aapt resource value: 2 - public const int CoordinatorLayout_Layout_layout_anchor = 2; - - // aapt resource value: 4 - public const int CoordinatorLayout_Layout_layout_anchorGravity = 4; - - // aapt resource value: 1 - public const int CoordinatorLayout_Layout_layout_behavior = 1; - - // aapt resource value: 6 - public const int CoordinatorLayout_Layout_layout_dodgeInsetEdges = 6; - - // aapt resource value: 5 - public const int CoordinatorLayout_Layout_layout_insetEdge = 5; - - // aapt resource value: 3 - public const int CoordinatorLayout_Layout_layout_keyline = 3; - - public static int[] DesignTheme = new int[] { - 2130772256, - 2130772257, - 2130772258}; - - // aapt resource value: 0 - public const int DesignTheme_bottomSheetDialogTheme = 0; - - // aapt resource value: 1 - public const int DesignTheme_bottomSheetStyle = 1; - - // aapt resource value: 2 - public const int DesignTheme_textColorError = 2; - - public static int[] DrawerArrowToggle = new int[] { - 2130772156, - 2130772157, - 2130772158, - 2130772159, - 2130772160, - 2130772161, - 2130772162, - 2130772163}; - - // aapt resource value: 4 - public const int DrawerArrowToggle_arrowHeadLength = 4; - - // aapt resource value: 5 - public const int DrawerArrowToggle_arrowShaftLength = 5; - - // aapt resource value: 6 - public const int DrawerArrowToggle_barLength = 6; - - // aapt resource value: 0 - public const int DrawerArrowToggle_color = 0; - - // aapt resource value: 2 - public const int DrawerArrowToggle_drawableSize = 2; - - // aapt resource value: 3 - public const int DrawerArrowToggle_gapBetweenBars = 3; - - // aapt resource value: 1 - public const int DrawerArrowToggle_spinBars = 1; - - // aapt resource value: 7 - public const int DrawerArrowToggle_thickness = 7; - - public static int[] FloatingActionButton = new int[] { - 2130772023, - 2130772221, - 2130772222, - 2130772259, - 2130772260, - 2130772261, - 2130772262, - 2130772263}; - - // aapt resource value: 1 - public const int FloatingActionButton_backgroundTint = 1; - - // aapt resource value: 2 - public const int FloatingActionButton_backgroundTintMode = 2; - - // aapt resource value: 6 - public const int FloatingActionButton_borderWidth = 6; - - // aapt resource value: 0 - public const int FloatingActionButton_elevation = 0; - - // aapt resource value: 4 - public const int FloatingActionButton_fabSize = 4; - - // aapt resource value: 5 - public const int FloatingActionButton_pressedTranslationZ = 5; - - // aapt resource value: 3 - public const int FloatingActionButton_rippleColor = 3; - - // aapt resource value: 7 - public const int FloatingActionButton_useCompatPadding = 7; - - public static int[] FloatingActionButton_Behavior_Layout = new int[] { - 2130772264}; - - // aapt resource value: 0 - public const int FloatingActionButton_Behavior_Layout_behavior_autoHide = 0; - - public static int[] ForegroundLinearLayout = new int[] { - 16843017, - 16843264, - 2130772265}; - - // aapt resource value: 0 - public const int ForegroundLinearLayout_android_foreground = 0; - - // aapt resource value: 1 - public const int ForegroundLinearLayout_android_foregroundGravity = 1; - - // aapt resource value: 2 - public const int ForegroundLinearLayout_foregroundInsidePadding = 2; - - public static int[] LinearLayoutCompat = new int[] { - 16842927, - 16842948, - 16843046, - 16843047, - 16843048, - 2130772006, - 2130772164, - 2130772165, - 2130772166}; - - // aapt resource value: 2 - public const int LinearLayoutCompat_android_baselineAligned = 2; - - // aapt resource value: 3 - public const int LinearLayoutCompat_android_baselineAlignedChildIndex = 3; - - // aapt resource value: 0 - public const int LinearLayoutCompat_android_gravity = 0; - - // aapt resource value: 1 - public const int LinearLayoutCompat_android_orientation = 1; - - // aapt resource value: 4 - public const int LinearLayoutCompat_android_weightSum = 4; - - // aapt resource value: 5 - public const int LinearLayoutCompat_divider = 5; - - // aapt resource value: 8 - public const int LinearLayoutCompat_dividerPadding = 8; - - // aapt resource value: 6 - public const int LinearLayoutCompat_measureWithLargestChild = 6; - - // aapt resource value: 7 - public const int LinearLayoutCompat_showDividers = 7; - - public static int[] LinearLayoutCompat_Layout = new int[] { - 16842931, - 16842996, - 16842997, - 16843137}; - - // aapt resource value: 0 - public const int LinearLayoutCompat_Layout_android_layout_gravity = 0; - - // aapt resource value: 2 - public const int LinearLayoutCompat_Layout_android_layout_height = 2; - - // aapt resource value: 3 - public const int LinearLayoutCompat_Layout_android_layout_weight = 3; - - // aapt resource value: 1 - public const int LinearLayoutCompat_Layout_android_layout_width = 1; - - public static int[] ListPopupWindow = new int[] { - 16843436, - 16843437}; - - // aapt resource value: 0 - public const int ListPopupWindow_android_dropDownHorizontalOffset = 0; - - // aapt resource value: 1 - public const int ListPopupWindow_android_dropDownVerticalOffset = 1; - - public static int[] MediaRouteButton = new int[] { - 16843071, - 16843072, - 2130771983, - 2130772154}; - - // aapt resource value: 1 - public const int MediaRouteButton_android_minHeight = 1; - - // aapt resource value: 0 - public const int MediaRouteButton_android_minWidth = 0; - - // aapt resource value: 3 - public const int MediaRouteButton_buttonTint = 3; - - // aapt resource value: 2 - public const int MediaRouteButton_externalRouteEnabledDrawable = 2; - - public static int[] MenuGroup = new int[] { - 16842766, - 16842960, - 16843156, - 16843230, - 16843231, - 16843232}; - - // aapt resource value: 5 - public const int MenuGroup_android_checkableBehavior = 5; - - // aapt resource value: 0 - public const int MenuGroup_android_enabled = 0; - - // aapt resource value: 1 - public const int MenuGroup_android_id = 1; - - // aapt resource value: 3 - public const int MenuGroup_android_menuCategory = 3; - - // aapt resource value: 4 - public const int MenuGroup_android_orderInCategory = 4; - - // aapt resource value: 2 - public const int MenuGroup_android_visible = 2; - - public static int[] MenuItem = new int[] { - 16842754, - 16842766, - 16842960, - 16843014, - 16843156, - 16843230, - 16843231, - 16843233, - 16843234, - 16843235, - 16843236, - 16843237, - 16843375, - 2130772167, - 2130772168, - 2130772169, - 2130772170}; - - // aapt resource value: 14 - public const int MenuItem_actionLayout = 14; - - // aapt resource value: 16 - public const int MenuItem_actionProviderClass = 16; - - // aapt resource value: 15 - public const int MenuItem_actionViewClass = 15; - - // aapt resource value: 9 - public const int MenuItem_android_alphabeticShortcut = 9; - - // aapt resource value: 11 - public const int MenuItem_android_checkable = 11; - - // aapt resource value: 3 - public const int MenuItem_android_checked = 3; - - // aapt resource value: 1 - public const int MenuItem_android_enabled = 1; - - // aapt resource value: 0 - public const int MenuItem_android_icon = 0; - - // aapt resource value: 2 - public const int MenuItem_android_id = 2; - - // aapt resource value: 5 - public const int MenuItem_android_menuCategory = 5; - - // aapt resource value: 10 - public const int MenuItem_android_numericShortcut = 10; - - // aapt resource value: 12 - public const int MenuItem_android_onClick = 12; - - // aapt resource value: 6 - public const int MenuItem_android_orderInCategory = 6; - - // aapt resource value: 7 - public const int MenuItem_android_title = 7; - - // aapt resource value: 8 - public const int MenuItem_android_titleCondensed = 8; - - // aapt resource value: 4 - public const int MenuItem_android_visible = 4; - - // aapt resource value: 13 - public const int MenuItem_showAsAction = 13; - - public static int[] MenuView = new int[] { - 16842926, - 16843052, - 16843053, - 16843054, - 16843055, - 16843056, - 16843057, - 2130772171, - 2130772172}; - - // aapt resource value: 4 - public const int MenuView_android_headerBackground = 4; - - // aapt resource value: 2 - public const int MenuView_android_horizontalDivider = 2; - - // aapt resource value: 5 - public const int MenuView_android_itemBackground = 5; - - // aapt resource value: 6 - public const int MenuView_android_itemIconDisabledAlpha = 6; - - // aapt resource value: 1 - public const int MenuView_android_itemTextAppearance = 1; - - // aapt resource value: 3 - public const int MenuView_android_verticalDivider = 3; - - // aapt resource value: 0 - public const int MenuView_android_windowAnimationStyle = 0; - - // aapt resource value: 7 - public const int MenuView_preserveIconSpacing = 7; - - // aapt resource value: 8 - public const int MenuView_subMenuArrow = 8; - - public static int[] NavigationView = new int[] { - 16842964, - 16842973, - 16843039, - 2130772023, - 2130772266, - 2130772267, - 2130772268, - 2130772269, - 2130772270, - 2130772271}; - - // aapt resource value: 0 - public const int NavigationView_android_background = 0; - - // aapt resource value: 1 - public const int NavigationView_android_fitsSystemWindows = 1; - - // aapt resource value: 2 - public const int NavigationView_android_maxWidth = 2; - - // aapt resource value: 3 - public const int NavigationView_elevation = 3; - - // aapt resource value: 9 - public const int NavigationView_headerLayout = 9; - - // aapt resource value: 7 - public const int NavigationView_itemBackground = 7; - - // aapt resource value: 5 - public const int NavigationView_itemIconTint = 5; - - // aapt resource value: 8 - public const int NavigationView_itemTextAppearance = 8; - - // aapt resource value: 6 - public const int NavigationView_itemTextColor = 6; - - // aapt resource value: 4 - public const int NavigationView_menu = 4; - - public static int[] PopupWindow = new int[] { - 16843126, - 16843465, - 2130772173}; - - // aapt resource value: 1 - public const int PopupWindow_android_popupAnimationStyle = 1; - - // aapt resource value: 0 - public const int PopupWindow_android_popupBackground = 0; - - // aapt resource value: 2 - public const int PopupWindow_overlapAnchor = 2; - - public static int[] PopupWindowBackgroundState = new int[] { - 2130772174}; - - // aapt resource value: 0 - public const int PopupWindowBackgroundState_state_above_anchor = 0; - - public static int[] RecycleListView = new int[] { - 2130772175, - 2130772176}; - - // aapt resource value: 0 - public const int RecycleListView_paddingBottomNoButtons = 0; - - // aapt resource value: 1 - public const int RecycleListView_paddingTopNoTitle = 1; - - public static int[] RecyclerView = new int[] { - 16842948, - 16842993, - 2130771968, - 2130771969, - 2130771970, - 2130771971}; - - // aapt resource value: 1 - public const int RecyclerView_android_descendantFocusability = 1; - - // aapt resource value: 0 - public const int RecyclerView_android_orientation = 0; - - // aapt resource value: 2 - public const int RecyclerView_layoutManager = 2; - - // aapt resource value: 4 - public const int RecyclerView_reverseLayout = 4; - - // aapt resource value: 3 - public const int RecyclerView_spanCount = 3; - - // aapt resource value: 5 - public const int RecyclerView_stackFromEnd = 5; - - public static int[] ScrimInsetsFrameLayout = new int[] { - 2130772272}; - - // aapt resource value: 0 - public const int ScrimInsetsFrameLayout_insetForeground = 0; - - public static int[] ScrollingViewBehavior_Layout = new int[] { - 2130772273}; - - // aapt resource value: 0 - public const int ScrollingViewBehavior_Layout_behavior_overlapTop = 0; - - public static int[] SearchView = new int[] { - 16842970, - 16843039, - 16843296, - 16843364, - 2130772177, - 2130772178, - 2130772179, - 2130772180, - 2130772181, - 2130772182, - 2130772183, - 2130772184, - 2130772185, - 2130772186, - 2130772187, - 2130772188, - 2130772189}; - - // aapt resource value: 0 - public const int SearchView_android_focusable = 0; - - // aapt resource value: 3 - public const int SearchView_android_imeOptions = 3; - - // aapt resource value: 2 - public const int SearchView_android_inputType = 2; - - // aapt resource value: 1 - public const int SearchView_android_maxWidth = 1; - - // aapt resource value: 8 - public const int SearchView_closeIcon = 8; - - // aapt resource value: 13 - public const int SearchView_commitIcon = 13; - - // aapt resource value: 7 - public const int SearchView_defaultQueryHint = 7; - - // aapt resource value: 9 - public const int SearchView_goIcon = 9; - - // aapt resource value: 5 - public const int SearchView_iconifiedByDefault = 5; - - // aapt resource value: 4 - public const int SearchView_layout = 4; - - // aapt resource value: 15 - public const int SearchView_queryBackground = 15; - - // aapt resource value: 6 - public const int SearchView_queryHint = 6; - - // aapt resource value: 11 - public const int SearchView_searchHintIcon = 11; - - // aapt resource value: 10 - public const int SearchView_searchIcon = 10; - - // aapt resource value: 16 - public const int SearchView_submitBackground = 16; - - // aapt resource value: 14 - public const int SearchView_suggestionRowLayout = 14; - - // aapt resource value: 12 - public const int SearchView_voiceIcon = 12; - - public static int[] SnackbarLayout = new int[] { - 16843039, - 2130772023, - 2130772274}; - - // aapt resource value: 0 - public const int SnackbarLayout_android_maxWidth = 0; - - // aapt resource value: 1 - public const int SnackbarLayout_elevation = 1; - - // aapt resource value: 2 - public const int SnackbarLayout_maxActionInlineWidth = 2; - - public static int[] Spinner = new int[] { - 16842930, - 16843126, - 16843131, - 16843362, - 2130772024}; - - // aapt resource value: 3 - public const int Spinner_android_dropDownWidth = 3; - - // aapt resource value: 0 - public const int Spinner_android_entries = 0; - - // aapt resource value: 1 - public const int Spinner_android_popupBackground = 1; - - // aapt resource value: 2 - public const int Spinner_android_prompt = 2; - - // aapt resource value: 4 - public const int Spinner_popupTheme = 4; - - public static int[] SwitchCompat = new int[] { - 16843044, - 16843045, - 16843074, - 2130772190, - 2130772191, - 2130772192, - 2130772193, - 2130772194, - 2130772195, - 2130772196, - 2130772197, - 2130772198, - 2130772199, - 2130772200}; - - // aapt resource value: 1 - public const int SwitchCompat_android_textOff = 1; - - // aapt resource value: 0 - public const int SwitchCompat_android_textOn = 0; - - // aapt resource value: 2 - public const int SwitchCompat_android_thumb = 2; - - // aapt resource value: 13 - public const int SwitchCompat_showText = 13; - - // aapt resource value: 12 - public const int SwitchCompat_splitTrack = 12; - - // aapt resource value: 10 - public const int SwitchCompat_switchMinWidth = 10; - - // aapt resource value: 11 - public const int SwitchCompat_switchPadding = 11; - - // aapt resource value: 9 - public const int SwitchCompat_switchTextAppearance = 9; - - // aapt resource value: 8 - public const int SwitchCompat_thumbTextPadding = 8; - - // aapt resource value: 3 - public const int SwitchCompat_thumbTint = 3; - - // aapt resource value: 4 - public const int SwitchCompat_thumbTintMode = 4; - - // aapt resource value: 5 - public const int SwitchCompat_track = 5; - - // aapt resource value: 6 - public const int SwitchCompat_trackTint = 6; - - // aapt resource value: 7 - public const int SwitchCompat_trackTintMode = 7; - - public static int[] TabItem = new int[] { - 16842754, - 16842994, - 16843087}; - - // aapt resource value: 0 - public const int TabItem_android_icon = 0; - - // aapt resource value: 1 - public const int TabItem_android_layout = 1; - - // aapt resource value: 2 - public const int TabItem_android_text = 2; - - public static int[] TabLayout = new int[] { - 2130772275, - 2130772276, - 2130772277, - 2130772278, - 2130772279, - 2130772280, - 2130772281, - 2130772282, - 2130772283, - 2130772284, - 2130772285, - 2130772286, - 2130772287, - 2130772288, - 2130772289, - 2130772290}; - - // aapt resource value: 3 - public const int TabLayout_tabBackground = 3; - - // aapt resource value: 2 - public const int TabLayout_tabContentStart = 2; - - // aapt resource value: 5 - public const int TabLayout_tabGravity = 5; - - // aapt resource value: 0 - public const int TabLayout_tabIndicatorColor = 0; - - // aapt resource value: 1 - public const int TabLayout_tabIndicatorHeight = 1; - - // aapt resource value: 7 - public const int TabLayout_tabMaxWidth = 7; - - // aapt resource value: 6 - public const int TabLayout_tabMinWidth = 6; - - // aapt resource value: 4 - public const int TabLayout_tabMode = 4; - - // aapt resource value: 15 - public const int TabLayout_tabPadding = 15; - - // aapt resource value: 14 - public const int TabLayout_tabPaddingBottom = 14; - - // aapt resource value: 13 - public const int TabLayout_tabPaddingEnd = 13; - - // aapt resource value: 11 - public const int TabLayout_tabPaddingStart = 11; - - // aapt resource value: 12 - public const int TabLayout_tabPaddingTop = 12; - - // aapt resource value: 10 - public const int TabLayout_tabSelectedTextColor = 10; - - // aapt resource value: 8 - public const int TabLayout_tabTextAppearance = 8; - - // aapt resource value: 9 - public const int TabLayout_tabTextColor = 9; - - public static int[] TextAppearance = new int[] { - 16842901, - 16842902, - 16842903, - 16842904, - 16842906, - 16843105, - 16843106, - 16843107, - 16843108, - 2130772038}; - - // aapt resource value: 5 - public const int TextAppearance_android_shadowColor = 5; - - // aapt resource value: 6 - public const int TextAppearance_android_shadowDx = 6; - - // aapt resource value: 7 - public const int TextAppearance_android_shadowDy = 7; - - // aapt resource value: 8 - public const int TextAppearance_android_shadowRadius = 8; - - // aapt resource value: 3 - public const int TextAppearance_android_textColor = 3; - - // aapt resource value: 4 - public const int TextAppearance_android_textColorHint = 4; - - // aapt resource value: 0 - public const int TextAppearance_android_textSize = 0; - - // aapt resource value: 2 - public const int TextAppearance_android_textStyle = 2; - - // aapt resource value: 1 - public const int TextAppearance_android_typeface = 1; - - // aapt resource value: 9 - public const int TextAppearance_textAllCaps = 9; - - public static int[] TextInputLayout = new int[] { - 16842906, - 16843088, - 2130772291, - 2130772292, - 2130772293, - 2130772294, - 2130772295, - 2130772296, - 2130772297, - 2130772298, - 2130772299, - 2130772300, - 2130772301, - 2130772302, - 2130772303, - 2130772304}; - - // aapt resource value: 1 - public const int TextInputLayout_android_hint = 1; - - // aapt resource value: 0 - public const int TextInputLayout_android_textColorHint = 0; - - // aapt resource value: 6 - public const int TextInputLayout_counterEnabled = 6; - - // aapt resource value: 7 - public const int TextInputLayout_counterMaxLength = 7; - - // aapt resource value: 9 - public const int TextInputLayout_counterOverflowTextAppearance = 9; - - // aapt resource value: 8 - public const int TextInputLayout_counterTextAppearance = 8; - - // aapt resource value: 4 - public const int TextInputLayout_errorEnabled = 4; - - // aapt resource value: 5 - public const int TextInputLayout_errorTextAppearance = 5; - - // aapt resource value: 10 - public const int TextInputLayout_hintAnimationEnabled = 10; - - // aapt resource value: 3 - public const int TextInputLayout_hintEnabled = 3; - - // aapt resource value: 2 - public const int TextInputLayout_hintTextAppearance = 2; - - // aapt resource value: 13 - public const int TextInputLayout_passwordToggleContentDescription = 13; - - // aapt resource value: 12 - public const int TextInputLayout_passwordToggleDrawable = 12; - - // aapt resource value: 11 - public const int TextInputLayout_passwordToggleEnabled = 11; - - // aapt resource value: 14 - public const int TextInputLayout_passwordToggleTint = 14; - - // aapt resource value: 15 - public const int TextInputLayout_passwordToggleTintMode = 15; - - public static int[] Toolbar = new int[] { - 16842927, - 16843072, - 2130771998, - 2130772001, - 2130772005, - 2130772017, - 2130772018, - 2130772019, - 2130772020, - 2130772021, - 2130772022, - 2130772024, - 2130772201, - 2130772202, - 2130772203, - 2130772204, - 2130772205, - 2130772206, - 2130772207, - 2130772208, - 2130772209, - 2130772210, - 2130772211, - 2130772212, - 2130772213, - 2130772214, - 2130772215, - 2130772216, - 2130772217}; - - // aapt resource value: 0 - public const int Toolbar_android_gravity = 0; - - // aapt resource value: 1 - public const int Toolbar_android_minHeight = 1; - - // aapt resource value: 21 - public const int Toolbar_buttonGravity = 21; - - // aapt resource value: 23 - public const int Toolbar_collapseContentDescription = 23; - - // aapt resource value: 22 - public const int Toolbar_collapseIcon = 22; - - // aapt resource value: 6 - public const int Toolbar_contentInsetEnd = 6; - - // aapt resource value: 10 - public const int Toolbar_contentInsetEndWithActions = 10; - - // aapt resource value: 7 - public const int Toolbar_contentInsetLeft = 7; - - // aapt resource value: 8 - public const int Toolbar_contentInsetRight = 8; - - // aapt resource value: 5 - public const int Toolbar_contentInsetStart = 5; - - // aapt resource value: 9 - public const int Toolbar_contentInsetStartWithNavigation = 9; - - // aapt resource value: 4 - public const int Toolbar_logo = 4; - - // aapt resource value: 26 - public const int Toolbar_logoDescription = 26; - - // aapt resource value: 20 - public const int Toolbar_maxButtonHeight = 20; - - // aapt resource value: 25 - public const int Toolbar_navigationContentDescription = 25; - - // aapt resource value: 24 - public const int Toolbar_navigationIcon = 24; - - // aapt resource value: 11 - public const int Toolbar_popupTheme = 11; - - // aapt resource value: 3 - public const int Toolbar_subtitle = 3; - - // aapt resource value: 13 - public const int Toolbar_subtitleTextAppearance = 13; - - // aapt resource value: 28 - public const int Toolbar_subtitleTextColor = 28; - - // aapt resource value: 2 - public const int Toolbar_title = 2; - - // aapt resource value: 14 - public const int Toolbar_titleMargin = 14; - - // aapt resource value: 18 - public const int Toolbar_titleMarginBottom = 18; - - // aapt resource value: 16 - public const int Toolbar_titleMarginEnd = 16; - - // aapt resource value: 15 - public const int Toolbar_titleMarginStart = 15; - - // aapt resource value: 17 - public const int Toolbar_titleMarginTop = 17; - - // aapt resource value: 19 - public const int Toolbar_titleMargins = 19; - - // aapt resource value: 12 - public const int Toolbar_titleTextAppearance = 12; - - // aapt resource value: 27 - public const int Toolbar_titleTextColor = 27; - - public static int[] View = new int[] { - 16842752, - 16842970, - 2130772218, - 2130772219, - 2130772220}; - - // aapt resource value: 1 - public const int View_android_focusable = 1; - - // aapt resource value: 0 - public const int View_android_theme = 0; - - // aapt resource value: 3 - public const int View_paddingEnd = 3; - - // aapt resource value: 2 - public const int View_paddingStart = 2; - - // aapt resource value: 4 - public const int View_theme = 4; - - public static int[] ViewBackgroundHelper = new int[] { - 16842964, - 2130772221, - 2130772222}; - - // aapt resource value: 0 - public const int ViewBackgroundHelper_android_background = 0; - - // aapt resource value: 1 - public const int ViewBackgroundHelper_backgroundTint = 1; - - // aapt resource value: 2 - public const int ViewBackgroundHelper_backgroundTintMode = 2; - - public static int[] ViewStubCompat = new int[] { - 16842960, - 16842994, - 16842995}; - - // aapt resource value: 0 - public const int ViewStubCompat_android_id = 0; - - // aapt resource value: 2 - public const int ViewStubCompat_android_inflatedId = 2; - - // aapt resource value: 1 - public const int ViewStubCompat_android_layout = 1; - - static Styleable() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Styleable() - { - } - } - } -} -#pragma warning restore 1591 diff --git a/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/Resources/drawable-hdpi/icon.png b/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/Resources/drawable-hdpi/icon.png deleted file mode 100644 index 964f110ab..000000000 Binary files a/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/Resources/drawable-hdpi/icon.png and /dev/null differ diff --git a/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/Resources/drawable-xhdpi/icon.png b/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/Resources/drawable-xhdpi/icon.png deleted file mode 100644 index 3c01e60ce..000000000 Binary files a/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/Resources/drawable-xhdpi/icon.png and /dev/null differ diff --git a/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/Resources/drawable-xxhdpi/icon.png b/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/Resources/drawable-xxhdpi/icon.png deleted file mode 100644 index 0d8c1c57d..000000000 Binary files a/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/Resources/drawable-xxhdpi/icon.png and /dev/null differ diff --git a/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/Resources/drawable/icon.png b/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/Resources/drawable/icon.png deleted file mode 100644 index b0ba7150f..000000000 Binary files a/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/Resources/drawable/icon.png and /dev/null differ diff --git a/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/Resources/layout/Tabbar.axml b/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/Resources/layout/Tabbar.axml deleted file mode 100644 index ad1f87d81..000000000 --- a/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/Resources/layout/Tabbar.axml +++ /dev/null @@ -1,11 +0,0 @@ - - diff --git a/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/Resources/layout/Toolbar.axml b/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/Resources/layout/Toolbar.axml deleted file mode 100644 index aabd0a3b7..000000000 --- a/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/Resources/layout/Toolbar.axml +++ /dev/null @@ -1,9 +0,0 @@ - - diff --git a/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/Resources/values/styles.xml b/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/Resources/values/styles.xml deleted file mode 100644 index 43b0a58c1..000000000 --- a/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/Resources/values/styles.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - diff --git a/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/packages.config b/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/packages.config deleted file mode 100644 index a39b26d78..000000000 --- a/Samples/CSharp/AlphaTab.Samples.XamarinForms.Android/packages.config +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Samples/CSharp/AlphaTab.Samples.XamarinNative.Android/AlphaTab.Samples.XamarinNative.Android.csproj b/Samples/CSharp/AlphaTab.Samples.XamarinNative.Android/AlphaTab.Samples.XamarinNative.Android.csproj deleted file mode 100644 index 7f94f06c8..000000000 --- a/Samples/CSharp/AlphaTab.Samples.XamarinNative.Android/AlphaTab.Samples.XamarinNative.Android.csproj +++ /dev/null @@ -1,101 +0,0 @@ - - - - Debug - AnyCPU - 8.0.30703 - 2.0 - {48297892-A3F5-43B1-9001-2F3BE04F7486} - {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - Library - Properties - AlphaTab.Samples.XamarinNative.Android - AlphaTab.Samples.XamarinNative.Android - 512 - True - Resources\Resource.Designer.cs - Off - True - v7.1 - Properties\AndroidManifest.xml - - - True - Full - False - bin\Debug\ - DEBUG;TRACE - prompt - 4 - True - None - False - - - PdbOnly - True - True - bin\Release\ - TRACE - prompt - 4 - False - SdkOnly - True - - - - - - - - - - - - - - - - - - Assets\Canon.gp5 - - - - - - - - Designer - - - - - - - - - - - - - - - - - {bcc950ea-7465-47fc-a7af-e733b55ec91f} - AlphaTab.CSharp - - - - - - - \ No newline at end of file diff --git a/Samples/CSharp/AlphaTab.Samples.XamarinNative.Android/Assets/AboutAssets.txt b/Samples/CSharp/AlphaTab.Samples.XamarinNative.Android/Assets/AboutAssets.txt deleted file mode 100644 index ee3988629..000000000 --- a/Samples/CSharp/AlphaTab.Samples.XamarinNative.Android/Assets/AboutAssets.txt +++ /dev/null @@ -1,19 +0,0 @@ -Any raw assets you want to be deployed with your application can be placed in -this directory (and child directories) and given a Build Action of "AndroidAsset". - -These files will be deployed with you package and will be accessible using Android's -AssetManager, like this: - -public class ReadAsset : Activity -{ - protected override void OnCreate (Bundle bundle) - { - base.OnCreate (bundle); - - InputStream input = Assets.Open ("my_asset.txt"); - } -} - -Additionally, some Android functions will automatically load asset files: - -Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf"); \ No newline at end of file diff --git a/Samples/CSharp/AlphaTab.Samples.XamarinNative.Android/MainActivity.cs b/Samples/CSharp/AlphaTab.Samples.XamarinNative.Android/MainActivity.cs deleted file mode 100644 index bcd2f65ed..000000000 --- a/Samples/CSharp/AlphaTab.Samples.XamarinNative.Android/MainActivity.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System.IO; -using AlphaTab.Importer; -using AlphaTab.Model; -using Android.App; -using Android.Content; -using Android.OS; -using Android.Util; -using Android.Views; - -namespace AlphaTab.Samples.XamarinNative.Android -{ - [Activity(Label = "AlphaTab.Samples.XamarinNative.Android", MainLauncher = true, Icon = "@drawable/icon")] - public class MainActivity : Activity - { - private Score _score; - - private Platform.CSharp.Xamarin.Android.AlphaTab _alphaTabControl; - - protected override void OnCreate(Bundle bundle) - { - base.OnCreate(bundle); - - ActionBar.Hide(); - - SetContentView(Resource.Layout.Main); - - _alphaTabControl = FindViewById(Resource.Id.AlphaTabControl); - - byte[] canon; - using (var stream = Assets.Open("Canon.gp5")) - { - using (var ms = new MemoryStream()) - { - stream.CopyTo(ms); - canon = ms.ToArray(); - } - } - LoadScore(canon); - } - - private void LoadScore(byte[] bytes) - { - _score = ScoreLoader.LoadScoreFromBytes(bytes); - _alphaTabControl.Tracks = new[] - { - _score.Tracks[0] - }; - } - } -} - diff --git a/Samples/CSharp/AlphaTab.Samples.XamarinNative.Android/Properties/AndroidManifest.xml b/Samples/CSharp/AlphaTab.Samples.XamarinNative.Android/Properties/AndroidManifest.xml deleted file mode 100644 index 7b8976635..000000000 --- a/Samples/CSharp/AlphaTab.Samples.XamarinNative.Android/Properties/AndroidManifest.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/Samples/CSharp/AlphaTab.Samples.XamarinNative.Android/Properties/AssemblyInfo.cs b/Samples/CSharp/AlphaTab.Samples.XamarinNative.Android/Properties/AssemblyInfo.cs deleted file mode 100644 index b8816d3f7..000000000 --- a/Samples/CSharp/AlphaTab.Samples.XamarinNative.Android/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using Android.App; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("AlphaTab.Samples.XamarinNative.Android")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("AlphaTab.Samples.XamarinNative.Android")] -[assembly: AssemblyCopyright("Copyright © 2017")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] -[assembly: ComVisible(false)] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Samples/CSharp/AlphaTab.Samples.XamarinNative.Android/Readme.md b/Samples/CSharp/AlphaTab.Samples.XamarinNative.Android/Readme.md deleted file mode 100644 index d2cb97661..000000000 --- a/Samples/CSharp/AlphaTab.Samples.XamarinNative.Android/Readme.md +++ /dev/null @@ -1,4 +0,0 @@ -# AlphaTab Xamarin Native Android Sample - - This sample shows how to embedd alphaTab into a Xamarin App targeting Android. The sample loads an embedded Guitar Pro file and displays it using - the Xamarin Control provided by alphaTab. \ No newline at end of file diff --git a/Samples/CSharp/AlphaTab.Samples.XamarinNative.Android/Resources/AboutResources.txt b/Samples/CSharp/AlphaTab.Samples.XamarinNative.Android/Resources/AboutResources.txt deleted file mode 100644 index c2bca974c..000000000 --- a/Samples/CSharp/AlphaTab.Samples.XamarinNative.Android/Resources/AboutResources.txt +++ /dev/null @@ -1,44 +0,0 @@ -Images, layout descriptions, binary blobs and string dictionaries can be included -in your application as resource files. Various Android APIs are designed to -operate on the resource IDs instead of dealing with images, strings or binary blobs -directly. - -For example, a sample Android app that contains a user interface layout (main.axml), -an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png) -would keep its resources in the "Resources" directory of the application: - -Resources/ - drawable/ - icon.png - - layout/ - main.axml - - values/ - strings.xml - -In order to get the build system to recognize Android resources, set the build action to -"AndroidResource". The native Android APIs do not operate directly with filenames, but -instead operate on resource IDs. When you compile an Android application that uses resources, -the build system will package the resources for distribution and generate a class called "R" -(this is an Android convention) that contains the tokens for each one of the resources -included. For example, for the above Resources layout, this is what the R class would expose: - -public class R { - public class drawable { - public const int icon = 0x123; - } - - public class layout { - public const int main = 0x456; - } - - public class strings { - public const int first_string = 0xabc; - public const int second_string = 0xbcd; - } -} - -You would then use R.drawable.icon to reference the drawable/icon.png file, or R.layout.main -to reference the layout/main.axml file, or R.strings.first_string to reference the first -string in the dictionary file values/strings.xml. \ No newline at end of file diff --git a/Samples/CSharp/AlphaTab.Samples.XamarinNative.Android/Resources/Resource.Designer.cs b/Samples/CSharp/AlphaTab.Samples.XamarinNative.Android/Resources/Resource.Designer.cs deleted file mode 100644 index 8aa44a20e..000000000 --- a/Samples/CSharp/AlphaTab.Samples.XamarinNative.Android/Resources/Resource.Designer.cs +++ /dev/null @@ -1,128 +0,0 @@ -#pragma warning disable 1591 -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -[assembly: global::Android.Runtime.ResourceDesignerAttribute("AlphaTab.Samples.XamarinNative.Android.Resource", IsApplication=true)] - -namespace AlphaTab.Samples.XamarinNative.Android -{ - - - [System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")] - public partial class Resource - { - - static Resource() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - public static void UpdateIdValues() - { - } - - public partial class Attribute - { - - static Attribute() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Attribute() - { - } - } - - public partial class Color - { - - // aapt resource value: 0x7f040000 - public const int White = 2130968576; - - static Color() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Color() - { - } - } - - public partial class Drawable - { - - // aapt resource value: 0x7f020000 - public const int Icon = 2130837504; - - static Drawable() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Drawable() - { - } - } - - public partial class Id - { - - // aapt resource value: 0x7f060000 - public const int AlphaTabControl = 2131099648; - - static Id() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Id() - { - } - } - - public partial class Layout - { - - // aapt resource value: 0x7f030000 - public const int Main = 2130903040; - - static Layout() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private Layout() - { - } - } - - public partial class String - { - - // aapt resource value: 0x7f050001 - public const int ApplicationName = 2131034113; - - // aapt resource value: 0x7f050000 - public const int Hello = 2131034112; - - static String() - { - global::Android.Runtime.ResourceIdManager.UpdateIdValues(); - } - - private String() - { - } - } - } -} -#pragma warning restore 1591 diff --git a/Samples/CSharp/AlphaTab.Samples.XamarinNative.Android/Resources/drawable/Icon.png b/Samples/CSharp/AlphaTab.Samples.XamarinNative.Android/Resources/drawable/Icon.png deleted file mode 100644 index 8074c4c57..000000000 Binary files a/Samples/CSharp/AlphaTab.Samples.XamarinNative.Android/Resources/drawable/Icon.png and /dev/null differ diff --git a/Samples/CSharp/AlphaTab.Samples.XamarinNative.Android/Resources/layout/Main.axml b/Samples/CSharp/AlphaTab.Samples.XamarinNative.Android/Resources/layout/Main.axml deleted file mode 100644 index cc31d111c..000000000 --- a/Samples/CSharp/AlphaTab.Samples.XamarinNative.Android/Resources/layout/Main.axml +++ /dev/null @@ -1,11 +0,0 @@ - - - - diff --git a/Samples/CSharp/AlphaTab.Samples.XamarinNative.Android/Resources/values/Colors.xml b/Samples/CSharp/AlphaTab.Samples.XamarinNative.Android/Resources/values/Colors.xml deleted file mode 100644 index c271ea619..000000000 --- a/Samples/CSharp/AlphaTab.Samples.XamarinNative.Android/Resources/values/Colors.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - #FFFFFFFF - diff --git a/Samples/CSharp/AlphaTab.Samples.XamarinNative.Android/Resources/values/Strings.xml b/Samples/CSharp/AlphaTab.Samples.XamarinNative.Android/Resources/values/Strings.xml deleted file mode 100644 index 42b47ec6a..000000000 --- a/Samples/CSharp/AlphaTab.Samples.XamarinNative.Android/Resources/values/Strings.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - Hello World, Click Me! - AlphaTab.Samples.XamarinNative.Android - diff --git a/Source/AlphaSynth.FlashOutput/CircularSampleBuffer.hx b/Source/AlphaSynth.FlashOutput/CircularSampleBuffer.hx deleted file mode 100644 index 5dace265f..000000000 --- a/Source/AlphaSynth.FlashOutput/CircularSampleBuffer.hx +++ /dev/null @@ -1,76 +0,0 @@ -class CircularSampleBuffer -{ - private var _buffer:SampleArray; - private var _writePosition:Int; - private var _readPosition:Int; - private var _sampleCount:Int; - - public function new(size:Int) - { - _buffer = new SampleArray(size); - _writePosition = 0; - _readPosition = 0; - _sampleCount = 0; - } - - public var count(get, null):Int; - private inline function get_count() - { - return _sampleCount; - } - - public function clear() - { - _readPosition = 0; - _writePosition = 0; - _sampleCount = 0; - _buffer = new SampleArray(_buffer.length); - } - - public function write(data:SampleArray, offset:Int, count:Int) - { - var samplesWritten = 0; - if (count > _buffer.length - _sampleCount) - { - count = _buffer.length - _sampleCount; - } - - var writeToEnd:Int = Std.int(Math.min(_buffer.length - _writePosition, count)); - SampleArray.blit(data, offset, _buffer, _writePosition, writeToEnd); - _writePosition += writeToEnd; - _writePosition %= _buffer.length; - samplesWritten += writeToEnd; - if (samplesWritten < count) - { - SampleArray.blit(data, offset + samplesWritten, _buffer, _writePosition, count - samplesWritten); - _writePosition += (count - samplesWritten); - samplesWritten = count; - } - _sampleCount += samplesWritten; - return samplesWritten; - } - - public function read(data:SampleArray, offset:Int, count:Int) - { - if (count > _sampleCount) - { - count = _sampleCount; - } - var samplesRead = 0; - var readToEnd = Std.int(Math.min(_buffer.length - _readPosition, count)); - SampleArray.blit(_buffer, _readPosition, data, offset, readToEnd); - samplesRead += readToEnd; - _readPosition += readToEnd; - _readPosition %= _buffer.length; - - if (samplesRead < count) - { - SampleArray.blit(_buffer, _readPosition, data, offset + samplesRead, count - samplesRead); - _readPosition += (count - samplesRead); - samplesRead = count; - } - - _sampleCount -= samplesRead; - return samplesRead; - } -} \ No newline at end of file diff --git a/Source/AlphaSynth.FlashOutput/FlashOutputDevice.hx b/Source/AlphaSynth.FlashOutput/FlashOutputDevice.hx deleted file mode 100644 index 400888ba7..000000000 --- a/Source/AlphaSynth.FlashOutput/FlashOutputDevice.hx +++ /dev/null @@ -1,184 +0,0 @@ -import flash.events.SampleDataEvent; -import flash.external.ExternalInterface; -import flash.media.Sound; -import flash.media.SoundChannel; -import flash.Lib; -import flash.utils.Endian; - -class FlashOutputDevice -{ - private static inline var BufferSize = 8192; - private static inline var BufferCount = 10; - - private static var Instance:FlashOutputDevice; - public static function main() : Void - { - Instance = new FlashOutputDevice(Lib.current.loaderInfo.parameters.id); - } - - private var _id:String; - private var _sound:Sound; - private var _soundChannel:SoundChannel; - private var _circularBuffer:CircularSampleBuffer; - private var _finished:Bool; - - public function new(id:String) - { - _id = id; - logDebug('Initializing Flash Output'); - - _finished = false; - _circularBuffer = new CircularSampleBuffer(BufferSize * BufferCount); - _sound = new Sound(); - _sound.addEventListener(SampleDataEvent.SAMPLE_DATA, generateSound); - - ExternalInterface.addCallback("AlphaSynthSequencerFinished", sequencerFinished); - ExternalInterface.addCallback("AlphaSynthResetSamples", resetSamples); - ExternalInterface.addCallback("AlphaSynthAddSamples", addSamples); - ExternalInterface.addCallback("AlphaSynthPlay", play); - ExternalInterface.addCallback("AlphaSynthPause", pause); - - ready(); - - logDebug('Flash Output initialized'); - } - - // API for JavaScript - private function sequencerFinished() - { - _finished = true; - } - - private function addSamples(b64:String) - { - var decoded = haxe.crypto.Base64.decode(b64); - var bytes = decoded.getData(); - bytes.endian = Endian.LITTLE_ENDIAN; - var sampleArray = cast(decoded.getData(), SampleArray); - _circularBuffer.write(sampleArray, 0, sampleArray.length); - } - - private function resetSamples() - { - _circularBuffer.clear(); - } - - private function play() - { - logDebug('FlashOutput: Play'); - try - { - sampleRequest(); - _finished = false; - _soundChannel = _sound.play(0); - } - catch(e:Dynamic) - { - logError('FlashOutput: Play Error: ' + Std.string(e)); - } - } - - private function pause() - { - logDebug('FlashOutput: Pause'); - try - { - if(_soundChannel != null) - { - _soundChannel.stop(); - _soundChannel = null; - } - } - catch(e:Dynamic) - { - logError('FlashOutput: Pause Error: ' + Std.string(e)); - } - } - - // API to JavaScript - private function sampleRequest() - { - // if we fall under the half of buffers - // we request one half - var count = (BufferCount / 2) * BufferSize; - if (_circularBuffer.count < count) - { - for (i in 0 ... Std.int(BufferCount/2)) - { - ExternalInterface.call("AlphaSynth.Main.AlphaSynthFlashOutput.OnSampleRequest", _id); - } - } - } - - private function finished() - { - ExternalInterface.call("AlphaSynth.Main.AlphaSynthFlashOutput.OnFinished", _id); - } - - private function ready() - { - ExternalInterface.call("AlphaSynth.Main.AlphaSynthFlashOutput.OnReady", _id); - } - - private function samplesPlayed(samplesPlayed:Int) - { - ExternalInterface.call("AlphaSynth.Main.AlphaSynthFlashOutput.OnSamplesPlayed", _id, samplesPlayed); - } - - private function logDebug(msg:String) - { - ExternalInterface.call("AlphaSynth.Util.Logger.Debug", msg); - } - - private function logError(msg:String) - { - ExternalInterface.call("AlphaSynth.Util.Logger.Error", msg); - } - - // play logic - private function generateSound(e:SampleDataEvent) - { - try - { - if (_circularBuffer.count < BufferSize) - { - if (_finished) - { - finished(); - } - else - { - for (i in 0 ... BufferSize) - { - e.data.writeFloat(0); - } - } - } - else - { - var buffer = new SampleArray(BufferSize); - var samplesRead = _circularBuffer.read(buffer, 0, buffer.length); - - var raw = buffer.toData(); - raw.position = 0; - - for (i in 0 ... BufferSize) - { - e.data.writeFloat(raw.readFloat()); - } - - samplesPlayed(Std.int(samplesRead / 2)); - } - - if (!_finished) - { - sampleRequest(); - } - } - catch(e:Dynamic) - { - var stack = haxe.CallStack.toString(haxe.CallStack.exceptionStack()); - logError('FlashOutput: Generate Error: ' + Std.string(e) + '\r\n' + stack); - } - } -} \ No newline at end of file diff --git a/Source/AlphaSynth.FlashOutput/SampleArray.hx b/Source/AlphaSynth.FlashOutput/SampleArray.hx deleted file mode 100644 index 338636ffa..000000000 --- a/Source/AlphaSynth.FlashOutput/SampleArray.hx +++ /dev/null @@ -1,28 +0,0 @@ -abstract SampleArray(flash.utils.ByteArray) -{ - public inline function new(length:Int) - { - this = new flash.utils.ByteArray(); - this.endian = flash.utils.Endian.LITTLE_ENDIAN; - this.length = length * 4; - } - - public var length(get, never):Int; - inline function get_length():Int - { - return Std.int(this.length / 4); - } - - public inline function toData() : flash.utils.ByteArray - { - return cast this; - } - - public static inline function blit(src:SampleArray, srcPos:Int, dest:SampleArray, destPos:Int, len:Int):Void - { - var destArray = dest.toData(); - var srcArray = src.toData(); - destArray.position = destPos * 4; - destArray.writeBytes(srcArray, srcPos * 4, len * 4); - } -} \ No newline at end of file diff --git a/Source/AlphaSynth.FlashOutput/make.bat b/Source/AlphaSynth.FlashOutput/make.bat deleted file mode 100644 index 8eb19b2f7..000000000 --- a/Source/AlphaSynth.FlashOutput/make.bat +++ /dev/null @@ -1 +0,0 @@ -haxe -main FlashOutputDevice -swf ..\..\Build\JavaScript\AlphaSynth.FlashOutput.swf \ No newline at end of file diff --git a/Source/AlphaTab.CSharp/AlphaTab.CSharp.csproj b/Source/AlphaTab.CSharp/AlphaTab.CSharp.csproj deleted file mode 100644 index febdec77b..000000000 --- a/Source/AlphaTab.CSharp/AlphaTab.CSharp.csproj +++ /dev/null @@ -1,53 +0,0 @@ - - - <_SdkLanguageName>CSharp - - - - AlphaTab - AlphaTab - AlphaTab - netstandard2.0;net471;MonoAndroid60 - true - $(OutDir)$(AssemblyName).xml - $(NoWarn);0162 - - - - $(DefineConstants);ANDROID; - - - - - - - - - - - - - - - - - - - - - - - - - - - MSBuild:Compile - - - - - - - - - \ No newline at end of file diff --git a/Source/AlphaTab.CSharp/AlphaTab.xml b/Source/AlphaTab.CSharp/AlphaTab.xml deleted file mode 100644 index 1704844b4..000000000 --- a/Source/AlphaTab.CSharp/AlphaTab.xml +++ /dev/null @@ -1,5319 +0,0 @@ - - - - AlphaTab - - - - - Represents a collection of key-value pairs. - - - - - - - Initializes a new instance of the class. - - - - - Gets or sets the value at the specified index. - - The key to access the item. - The value stored at the specified index. - - - - Gets the number of elements stored in this dictionary - - - - - - - - Removes the value with the specified key. - - The key to remove from the dictionary. - - - - Determines whether the dictionary container contains the specified key. - - The key to check the existence for. - - true if the specified key is contained; otherwise, false. - - - - - - - - Represents a strongly typed list of elements. - - The type fo the elements - - - - - Initializes a new instance of the class. - - - - - Gets the number of elements contained in the list. - - - - - Gets or sets the value at the specified index. - - The index of which item to access. - The item located at the specified index. - - - - Adds the specified item to the list. - - The item to be added. - - - - Sorts the elements in the list using the specified comparison. - - The comparison to use when comparing elements for sorting. - - - - Clones this instance. - - - - - - Removes the item at the specified index. - - The index to remove. - - - - Converts the current list into an array of all elements. - - An array containing all elements. - - - - - - - Searches for the given item in the list and returns the index. - - The item to search - The index at which the specified item was found, or -1 if the item is not contained in the list. - - - - Reverses the items in the list - - - - - Inserts an element at the specified index. - - The index at which the item should be inserted - The item to insert. - - - - Represents an array of audiosamples. - - - - - Gets the audio samples as floats. - - - - - Initializes a new instance of the class. - - The length of the array. - - - - Gets or sets the sample at the specified index. - - The index of the sample to get or set. - The sample at the specified index. - - - - Gets the total number of samples contained in this array - - - - - Resets all samples in the array to 0. - - - - - Copies a range of samples from the given source array into the specified destination. - - The array where to copy the samples from. - The start index from which to start copying. - The array where to copy the samples to. - The start index at which the samples should be written in the destination array. - The number of samples to copy. - - - - Represents a fixed size circular sample buffer that can be written to and read from. - - - - - Initializes a new instance of the class. - - The size. - - - - Gets the number of samples written to the buffer. - - - - - Clears all samples written to this buffer. - - - - - Writes the given samples to this buffer. - - The sample array to read from. - - - - - - - This is the main synthesizer component which can be used to - play a via a . - - - - - Gets the used for playing the generated samples. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Initializes a new instance of the class. - - The output to use for playing the generated samples. - - - - - - - - - - - - - - - - - - - Loads the given midi file for playback. - - The midi file to load - - - - - - - - - - - - - - - - - - - Occurs when the playback of the whole midi file finished. - - - - - Occurs when the playback state changes. - - - - - Occurs when the soundfont was successfully loaded. - - - - - Occurs when AlphaSynth is ready to start the playback. - This is the case once the is ready, a SoundFont was loaded and also a MidiFle is loaded. - - - - - Occurs when the soundfont failed to be loaded. - - - - - Occurs when the midi file was successfully loaded. - - - - - Occurs when the midi failed to be loaded. - - - - - Occurs whenever the current time of the played audio changes. - - - - - Represents the info when the player state changes. - - - - - The new state of the player. - - - - - Initializes a new instance of the class. - - The state. - - - - Represents the info when the time in the synthesizer changes. - - - - - Gets the current time in milliseconds. - - - - - Gets the length of the played song in milliseconds. - - - - - Gets the current time in midi ticks. - - - - - Gets the length of the played song in midi ticks. - - - - - Initializes a new instance of the class. - - The current time. - The end time. - The current tick. - The end tick. - - - - The public API interface for interacting with the synthesizer. - - - - - Gets or sets whether the synthesizer is ready for interaction. (output and worker are initialized) - - - - - Gets or sets whether the synthesizer is ready for playback. (output, worker are initialized, soundfont and midi are loaded) - p - - - - Gets the current player state. - - - - - Gets or sets the loging level. - - - - - Gets or sets the current master volume as percentage. (range: 0.0-3.0, default 1.0) - - - - - Gets or sets the metronome volume. (range: 0.0-3.0, default 0.0) - - - - - Gets or sets the current playback speed as percentage. (range: 0.125-8.0, default: 1.0) - - - - - Gets or sets the position within the song in midi ticks. - - - - - Gets or sets the position within the song in milliseconds. - - - - - Gets or sets the range of the song that should be played. Set this to null - to play the whole song. - - - - - Gets or sets whether the playback should automatically restart after it finished. - - - - - Starts the playback if possible - - - - - Pauses the playback if was running - - - - - Starts the playback if possible, pauses the playback if was running - - - - - Stopps the playback - - - - - Loads a soundfont from the given data - - a byte array to load the data from - - - - Gets the mute state of a channel. - - The channel number - true if the channel should be muted, otherwise false. - - - - Resets the mute/solo state of all channels - - - - - Gets the solo state of a channel. - - The channel number - true if the channel should be played solo, otherwise false. - - - - Gets or sets the current and initial volume of the given channel. - - The channel number. - The volume of of the channel (0.0-3.0) - - - - Gets or sets the current and initial program of the given channel. - - The channel number. - The midi program. - - - - This is the base interface for output devices which can - request and playback audio samples. - - - - - Gets the sample rate required by the output. - - - - - Called when the output should be opened. - - - - - Called when the sequencer finished the playback. - This tells the output not to request any samples anymore after the existing buffers are finished. - - - - - Called when the output should start the playback. - - - - - Called when the output should stop the playback. - - - - - Called when samples have been synthesized and should be added to the playback buffer. - - - - - - Called when the samples in the output buffer should be reset. This is neeed for instance when seeking to another position. - - - - - Fired when the output has been successfully opened and is ready to play samples. - - - - - Fired when a certain number of samples have been played. - - - - - Fired when the output needs more samples to be played. - - - - - Fired when the last samples after calling SequencerFinished have been played. - - - - - This sequencer dispatches midi events to the synthesizer based on the current - synthesize position. The sequencer does not consider the playback speed. - - - - - Note that this is not the actual playback position. It's the position where we are currently synthesizing at. - Depending on the buffer size of the output, this position is after the actual playback. - - - - - Gets the duration of the song in ticks. - - - - - Gets the duration of the song in milliseconds. - - - - - Gets or sets the playback speed. - - - - - Lists all midi events. - - - - - A note is released. - - - - - A note is started. - - - - - The pressure that was used to play the note. - - - - - Change of a midi controller - - - - - Change of a midi program - - - - - The pressure that should be applied to the whole channel. - - - - - A change of the audio pitch. - - - - - A meta event. See for details. - - - - - Lists all midi controllers. - - - - - Bank Select. MSB - - - - - Modulation wheel or lever MSB - - - - - Data entry MSB - - - - - Channel Volume MSB - - - - - Pan MSB - - - - - Expression Controller MSB - - - - - Modulation wheel or level LSB - - - - - Data Entry LSB - - - - - Channel Volume LSB - - - - - Pan LSB - - - - - Expression controller LSB - - - - - Damper pedal (sustain) - - - - - Legato Footswitch - - - - - Non-Registered Parameter Number LSB - - - - - Non-Registered Parameter Number MSB - - - - - Registered Parameter Number LSB - - - - - Registered Parameter Number MSB - - - - - Reset all controllers - - - - - All notes of. - - - - - Represents a midi event. - - - - - Gets or sets the raw midi message. - - - - - Gets or sets the absolute tick of this midi event. - - - - - Gets or sets the channel of this midi event. - - - - - Gets or sets the command of this midi event. - - - - - Gets or sets the first data component of this midi event. - - - - - Gets or sets the second data component of this midi event. - - - - - Initializes a new instance of the class. - - The absolute midi ticks of this event.. - The status information of this event. - The first data component of this midi event. - The second data component of this midi event. - - - - Writes the midi event as binary into the given stream. - - The stream to write to. - - - - Represents a midi file with a single track that can be played via - - - - - Gets or sets the division per quarter notes. - - - - - Gets a list of midi events sorted by time. - - - - - Initializes a new instance of the class. - - - - - Adds the given midi event a the correct time position into the file. - - - - - - Writes the midi file into a binary format. - - The binary midi file. - - - - Writes the midi file as binary into the given stream. - - The stream to write to. - - - - Lists the different states of the player - - - - - Player is paused - - - - - Player is playing - - - - - Represents a range of the song that should be played. - - - - - The position in midi ticks from where the song should start. - - - - - The position in midi ticks to where the song should be played. - - - - - The size of the individual sub buffers in samples - - - - - The number of sub buffers - - - - - Gets or sets the overall buffer of samples consisting of multiple microbuffers. - - - - - The patch bank that holds all of the currently loaded instrument patches - - - - - The number of samples per second produced per channel - - - - - The master volume - - - - - The metronome volume - - - - - Parameters for a single synth channel including its program, bank, and cc list. - - - - - program number - - - - - bank number - - - - - channel pressure event - - - - - (vol) pan positions controlling both right and left output levels - - - - - (vol) channel volume controller - - - - - (vol) expression controller - - - - - (pitch) mod wheel pitch modifier in partial cents ie. 22.3 - - - - - (pitch) pitch bend including both semitones and cents - - - - - controls max and min pitch bend range semitones - - - - - controls max and min pitch bend range cents - - - - - (pitch) transposition in semitones - - - - - (pitch) transposition in cents - - - - - hold pedal status (true) for active - - - - - legato pedal status (true) for active - - - - - registered parameter number - - - - - Resets all of the channel's controllers to initial first power on values. Not the same as CC-121. - - - - - This public class provides names for all general midi instruments. - - - - - This implementation of the generates a - object which can be used in AlphaSynth for playback. - - - - - Initializes a new instance of the class. - - The midi file. - - - - - - - - - - - - - - - - - - - - - - - - - - - - A handler is responsible for writing midi events to a custom structure - - - - - Adds a time signature to the generated midi file - - The midi ticks when this event should be happening. - The time signature numerator - The time signature denominator - - - - Adds a rest to the generated midi file. - - The midi track on which the rest should be "played". - The midi ticks when the rest is "playing". - The midi channel on which the rest should be "played". - - - - Adds a note to the generated midi file - - The midi track on which the note should be played. - The midi ticks when the note should start playing. - The duration the note in midi ticks. - The key of the note to play - The dynamic which should be applied to the note. - The midi channel on which the note should be played. - - - - Adds a control change to the generated midi file. - - The midi track on which the controller should change. - The midi ticks when the controller should change. - The midi channel on which the controller should change. - The midi controller that should change. - The value to which the midi controller should change - - - - Add a program change to the generated midi file - - The midi track on which the program should change. - The midi ticks when the program should change. - The midi channel on which the program should change. - The new program for the selected track and channel. - - - - Add a tempo change to the generated midi file. - - The midi ticks when the tempo should change change. - The tempo as BPM - - - - Add a bend to the generated midi file. - - The midi track on which the bend should change. - The midi ticks when the bend should change. - The midi channel on which the bend should change. - The new bend for the selected track and channel. - - - - Indicates that the track is finished on the given ticks. - - The track that was finished. - The end tick for this track. - - - - This generator creates a midi file using a score. - - - - - Gets a lookup object which can be used to quickly find beats and bars - at a given midi tick position. - - - - - Initializes a new instance of the class. - - The score for which the midi file should be generated. - The settings ot use for generation. - The handler that should be used for generating midi events. - - - - - - - The amount of ticks per quarter note used within this midi system. - (Pulses Per Quarter Note) - - - - - Converts the given midi tick duration into milliseconds. - - The duration in midi ticks - The current tempo in BPM. - The converted duration in milliseconds. - - - - Converts the given midi tick duration into milliseconds. - - The duration in milliseconds - The current tempo in BPM. - The converted duration in midi ticks. - - - - Converts a duration value to its ticks equivalent. - - - - - - - Converts a numerical value to its ticks equivalent. - - the numerical proportion to convert. (i.E. timesignature denominator, note duration,...) - - - - - Represents the time period, for which a is played. - - - - - Gets or sets the start time in midi ticks at which the given beat is played. - - - - - Gets or sets the end time in midi ticks at which the given beat is played. - - - - - Gets or sets the beat which is played. - - - - - Gets or sets whether the beat is the placeholder beat for an empty bar. - - - - - Represents the time period, for which all bars of a are played. - - - - - Gets or sets the start time in midi ticks at which the MasterBar is played. - - - - - Gets or sets the end time in midi ticks at which the MasterBar is played. - - - - - Gets or sets the current tempo when the MasterBar is played. - - - - - Gets or sets the MasterBar which is played. - - - - - Gets or sets the list of object which define the durations - for all played within the period of this MasterBar. - - - - - Gets or sets the of the next masterbar in the - - - - - Initializes a new instance of the class. - - - - - Performs the neccessary finalization steps after all information was written. - - - - - Adds a new to the list of played beats during this MasterBar period. - - - - - - Represents the results of searching the currently played beat. - - - - - - Gets or sets the beat that is currently played. - - - - - Gets or sets the beat that will be played next. - - - - - Gets or sets the duration in milliseconds how long this beat is playing. - - - - - This class holds all information about when s and s are played. - - - - - Gets a dictionary of all master bars played. The index is the index equals to . - - - This lookup only contains the first time a MasterBar is played. For a whole sequence of the song refer to . - - - - - Gets a list of all sorted by time. - - - - - Initializes a new instance of the class. - - - - - Performs the neccessary finalization steps after all information was written. - - - - - Finds the currently played beat given a list of tracks and the current time. - - The tracks in which to search the played beat for. - The current time in midi ticks. - The information about the current beat or null if no beat could be found. - - - - Gets the for a given masterbar at which the masterbar is played the first time. - - The masterbar to find the time period for. - A containing the details about the first time the is played. - - - - Gets the start time in midi ticks for a given masterbar at which the masterbar is played the first time. - - The masterbar to find the time period for. - The time in midi ticks at which the masterbar is played the first time or 0 if the masterbar is not contained - - - - Adds a new to the lookup table. - - The item to add. - - - - Adds the given to the current . - - The lookup to add. - - - - This public class represents the global alphaTab environment where - alphaTab looks for information like available layout engines - staves etc. - - - - - This class allows reading zip files. - - - - - Load a complete ZipFile to the memory. - - the binary source to read from. - - - - - You can set a file filter method using this setter. On parsing - the filestructure this function can determine based on the filename - whether this file will be available after loading. - This way we can reduce the amount of memory we store. - - - - - Gets the list of entries stored in this Zip File. - - - - - Creates a new GpxFileSystem instance - - - - - This utility public class allows bitwise reading of a stream - - - - - Represents a stream of binary data that can be read from. - - - - - Gets or sets the current read position relative in the stream. - - - - - Gets the total number of bytes contained in the stream. - - - - - Resets the stream for reading the data from the beginning. - - - - - Skip the given number of bytes. - - The number of bytes to skip. - - - - Read a single byte from the data stream. - - The value of the next byte or -1 if there is no more data. - - - - Reads the given number of bytes from the stream into the given buffer. - - The buffer to fill. - The offset in the buffer where to start writing. - The number of bytes to read. - - - - - Reads the remaining data. - - - - - - Represents a writer where binary data can be written to. - - - - - Write a single byte to the stream. - - The value to write. - - - - Write data from the given buffer. - - The buffer to get the data from. - The offset where to start reading the data. - The number of bytes to write - - - - this public class represents a file within the GpxFileSystem - - - - - This is the base public interface for canvas implementations on different plattforms. - - - - - This is the path drawing API for canvas implementations - - - - - A color object which allows accessing each color component individually. - - - - - Gets the hex string for black. - - - - - Initializes a new instance of the class. - - The red component. - The green component. - The blue component. - The alpha component. - - - - Gets or sets the raw RGBA value. - - - - - Gets or sets the alpha component of the color. - - - - - Gets or sets the red component of the color. - - - - - Gets or sets the green component of the color. - - - - - Gets or sets the blue component of the color. - - - - - Gets the RGBA hex string to use in CSS areas. - - - - - This container public class can store the definition for a font and it's style. - - - - - Gets or sets the font family name. - - - - - Gets or sets the font size in pixels. - - - - - Gets or sets the font style. - - - - - Gets a value indicating whether the font is bold. - - - - - Gets a value indicating whether the font is italic. - - - - - Initializes a new instance of the class. - - The family. - The size. - The style. - - - - Lists all flags for font styles. - - - - - No flags. - - - - - Font is bold - - - - - Font is italic. - - - - - This public enum lists all different text alignments - - - - - Text is left aligned. - - - - - Text is centered. - - - - - Text is right aligned. - - - - - This public enum lists all base line modes - - - - - Text is aligned on top. - - - - - Text is aligned middle - - - - - Text is aligend on the bottom. - - - - - This public class stores text widths for several fonts and allows width calculation - - - - - The supported fonts by the FontSizes public class - - - - - This SVG canvas renders the music symbols by adding a CSS class 'at' to all elements. - - - - - A canvas implementation storing SVG data - - - - - This public class contains instance specific settings for alphaTab - - - This public class contains instance specific settings for alphaTab - - - - - Sets the zoom level of the rendered notation - - - - - The initial size of the canvas during loading or the width for particular layouts (e.g. page layout). - - - - - The engine which should be used to render the the tablature. -
      -
    • default - Platform specific default engine
    • -
    • html5 - HTML5 Canvas
    • -
    • svg - SVG
    • -
    -
    -
    - - - The layout specific settings - - - - - Specific settings for importers. Keys are specific for the importers. - General -
      -
    • encoding - The text encoding to use when decoding strings (string, default:utf-8)
    • -
    - MusicXML -
      -
    • musicxmlMergePartGroups - If part-groups should be merged into a single track (boolean, default:false)
    • -
    -
    -
    - - - The default stretch force to use for layouting. - - - - - Forces the fingering rendering to use always the piano finger stýle where - fingers are rendered as 1-5 instead of p,i,m,a,c and T,1,2,3,4. - - - - - The staves that should be shown in the music sheet. - This is one of the profiles registered in the - - - - - The transposition pitch offsets for the individual tracks. - They apply to rendering and playback. - - - - - The transposition pitch offsets for the individual tracks. - They apply to rendering only. - - - - - The log level to use within alphaTab - - - - - If set to true the guitar tabs on grace beats are rendered smaller. - - - - - If set to true bend arrows expand to the end of the last tied note - of the string. Otherwise they end on the next beat. - - - - - If set to true the note heads on tied notes - will have parenthesis if they are preceeded by bends. - - - - - If set to true a tab number will be shown in case - a bend is increased on a tied note. - - - - - Gets or sets the mode to use for display and play music notation elements. - - - - - Gets or sets the fingering mode to use. - - - - - If set to true, 0 is shown on dive whammy bars. - - - - - If set to true, line effects (like w/bar, let-ring etc) - are drawn until the end of the beat instead of the start. - - - - - Gets or sets the settings on how the vibrato audio is generated. - - - - - Gets or sets the height factor for slurs. The factor is multiplied with the distance - between slur start and end. - - - - - Gets or sets the bend duration in milliseconds for songbook bends. - - - - - Gets or sets whether in the also the - position and area of each individual note is provided. - - - - - Gets the default settings for the songbook display mode. - - - - - Gets the default settings. - - - - - The base class for all errors that can happen in alphaTab. - - - - - Initializes a new instance of the class. - - The message that describes the error. - - - - This class allows converting scores into alphaTex. - - - - - Initializes a new instance of the class. - - - - - Exports the given track. - - The track to export - - - - Returns the generated tex code. - - - - - - This importer can parse alphaTex markup into a score structure. - - - - - Initializes the song with some required default values. - - - - - - Converts a clef string into the clef value. - - the string to convert - the clef value - - - - Converts a clef tuning into the clef value. - - the tuning value to convert - the clef value - - - - Converts a keysignature string into the assocciated value. - - the string to convert - the assocciated keysignature value - - - - Reads the next character of the source stream. - - - - - Reads the next terminal symbol. - - - - - Checks if the given character is a letter. - (no control characters, whitespaces, numbers or dots) - - the character - true if the given character is a letter, otherwise false. - - - - Checks if the given charater is a non terminal. - - the character - true if the given character is a terminal, otherwise false. - - - - Checks if the given character is a digit. - - the character - true if the given character is a digit, otherwise false. - - - - Reads a string from the stream. - - the read string. - - - - Reads a number from the stream. - - the read number. - - - - Tries to apply a beat effect to the given beat. - - true if a effect could be applied, otherwise false - - - - A list of terminals recognized by the alphaTex-parser - - - - - A parser for the BinaryStylesheet file of Guitar Pro 6 and 7. - - - The BinaryStylesheet is a simple binary key-value store. - - File: - int32 (big endian) | Number of KeyValuePairs - KeyValuePair[] | The raw records - - KeyValuePair: - 1 Byte | length of the key - n Bytes | key as utf8 encoded string - 1 Byte | Data Type - n Bytes | Value - - Values based on Data Type: - 0 = bool - 0 == false - 1 = int32 (big endian) - 2 = float (big endian, IEEE) - 3 = string - int16 (big endian) | length of string - n bytes | utf-8 encoded string - 4 = point - int32 (big endian) | X-coordinate - int32 (big endian) | Y-coordinate - 5 = size - int32 (big endian) | Width - int32 (big endian) | Height - 6 = rectangle - int32 (big endian) | X-coordinate - int32 (big endian) | Y-coordinate - int32 (big endian) | Width - int32 (big endian) | Height - 7 = color - 1 byte | Red - 1 byte | Green - 1 byte | Blue - 1 byte | Alpha - - - - - Skips an integer (4byte) and reads a string using - a bytesize - - - - - - Reads an integer as size, and then the string itself - - - - - - Reads an integer as size, skips a byte and reads the string itself - - - - - - Reads a byte as size and the string itself. - Additionally it is ensured the specified amount of bytes is read. - - the data to read from. - the amount of bytes to read - - - - - this public class represents a file within the GpxFileSystem - - - - - This public class represents the file system structure - stored within a GPX container file. - - - - - You can set a file filter method using this setter. On parsing - the filestructure this function can determine based on the filename - whether this file will be available after loading. - This way we can reduce the amount of memory we store. - - - - - Gets the list of files stored in this FileSystem. - - - - - Creates a new GpxFileSystem instance - - - - - Load a complete FileSystem to the memory. - - the binary source to read from. - - - - - Reads the 4 byte header as a string. - - the BitInput to read from - a string with 4 characters representing the header. - - - - Decompresses the given bitinput using the GPX compression format. Only use this method - if you are sure the binary data is compressed using the GPX format. Otherwise unexpected - behavior can occure. - - the bitInput to read the data from - true if the header should NOT be included in the result byteset, otherwise false - the decompressed byte data. if skipHeader is set to false the BCFS header is included. - - - - Reads a block from the given data source. - - the data source - - - - - Reads an uncompressed data block into the model. - - the data store to read from. - - - - Reads a zeroterminated ascii string from the given source - - the data source to read from - the offset to start reading from - the max length to read - the ascii string read from the datasource. - - - - Reads an 4 byte signed integer from the given source - - the data source to read from - offset the offset to start reading from - - - - - This ScoreImporter can read Guitar Pro 7 (gp) files. - - - - - This ScoreImporter can read Guitar Pro 6 (gpx) files. - - - - - This structure represents a duration within a gpif model. - - - - - This public class can parse a score.gpif xml file into the model structure - - - - - GPX range: 0-100 - Internal range: 0 - 60 - - - - - GPIF: 25 per quarternote - Internal Range: 1 per quarter note - - - - - A mixtablechange describes several track changes. - - - - - An exception indicating no reader for importing a file could be found. - - - - - Initializes a new instance of the class. - - - - - This is the base public class for creating new song importers which - enable reading scores from any binary datasource - - - - - The raw data to read from. - - - - - The settings to use during the import. - - - - Gets all default ScoreImporters - @return - - - - Initializes the importer with the given data and settings. - - - - - - - Gets the importer specific setting using the specified key. - - The key of the setting to load the value for. - The default value to load if no setting was specified. - The importer setting specified by the user, or the given defaultValue if the key was not contained. - - - - Get the human readable name of the importer. - - - - - Reads the contained in the data. - - The score that was contained in the data. - - - - The ScoreLoader enables you easy loading of Scores using all - available importers - - - - - Loads the score from the given binary data. - - The binary data containing a score in any known file format. - The settings to use during importing. - The loaded score. - - - - The exception thrown by a in case the - binary data does not contain a reader compatible structure. - - - - - Initializes a new instance of the class. - - The message that describes the error. - - - - Lists all types of note acceuntations - - - - - No accentuation - - - - - Normal accentuation - - - - - Heavy accentuation - - - - - Defines all possible accidentals for notes. - - - - - No accidental - - - - - Naturalize - - - - - Sharp - - - - - Flat - - - - - Natural for smear bends - - - - - Sharp for smear bends - - - - - Flat for smear bends - - - - - Automations are used to change the behaviour of a song. - - - - - Gets or sets whether the automation is applied linear. - - - - - Gets or sets the type of the automation. - - - - - Gets or sets the target value of the automation. - - - - - Gets or sets the relative position of of the automation. - - - - - Gets or sets the additional text of the automation. s - - - - - This public enumeration lists all types of automations. - - - - - Tempo change. - - - - - Colume change. - - - - - Instrument change. - - - - - Balance change. - - - - - A bar is a single block within a track, also known as Measure. - - - - - This is a global counter for all beats. We use it - at several locations for lookup tables. - - - - - Gets or sets the unique id of this bar. - - - - - Gets or sets the zero-based index of this bar within the staff. - - - - - Gets or sets the next bar that comes after this bar. - - - - - Gets or sets the previous bar that comes before this bar. - - - - - Gets or sets the clef on this bar. - - - - - Gets or sets the ottava applied to the clef. - - - - - Gets or sets the reference to the parent staff. - - - - - Gets or sets the list of voices contained in this bar. - - - - - Gets or sets the simile mark on this bar. - - - - - Gets the masterbar for this bar. - - - - - Gets a value indicating whether all voices in this bar are empty and therefore the whole bar is empty. - - - - - Initializes a new instance of the class. - - - - - A beat is a single block within a bar. A beat is a combination - of several notes played at the same time. - - - - - This is a global counter for all beats. We use it - at several locations for lookup tables. - - - - - Gets or sets the unique id of this beat. - - - - - Gets or sets the zero-based index of this beat within the voice. - - - - - Gets or sets the previous beat within the whole song. - - - - - Gets or sets the next beat within the whole song. - - - - - Gets a value indicating whether this beat is the last beat in the voice. - - - - - Gets or sets the reference to the parent voice this beat belongs to. - - - - - Gets or sets the list of notes contained in this beat. - - - - - Gets the lookup where the notes per string are registered. - If this staff contains string based notes this lookup allows fast access. - - - - - Gets or sets a value indicating whether this beat is considered empty. - - - - - Gets or sets which whammy bar style should be used for this bar. - - - - - Gets or sets the ottava applied to this beat. - - - - - Gets or sets the fermata applied to this beat. - - - - - Gets a value indicating whether this beat starts a legato slur. - - - - - Gets a value indicating whether this beat ends a legato slur. - - - - - Gets or sets the note with the lowest pitch in this beat. Only visible notes are considered. - - - - - Gets or sets the note with the highest pitch in this beat. Only visible notes are considered. - - - - - Gets or sets the note with the highest string number in this beat. Only visible notes are considered. - - - - - Gets or sets the note with the lowest string number in this beat. Only visible notes are considered. - - - - - Gets or sets the duration of this beat. - - - - - Gets or sets whether this beat starts a slur. - - - - - Gets or sets whether this beat ends or continues a slur. - - - - - Gets or sets the slur origin beat - - - - - Gets or sets the slur destination beat. - - - - - Gets or sets whether this beat is considered as rest. - - - - - Gets or sets whether any note in this beat has a let-ring applied. - - - - - Gets or sets whether any note in this beat has a palm-mute paplied. - - - - - Gets or sets a list of all automations on this beat. - - - - - Gets or sets the number of dots applied to the duration of this beat. - - - - - Gets or sets a value indicating whether this beat is fade-in. - - - - - Gets or sets the lyrics shown on this beat. - - - - - Gets or sets a value indicating whether the beat is played in rasgueado style. - - - - - Gets or sets a value indicating whether the notes on this beat are played with a pop-style (bass). - - - - - Gets or sets a value indicating whether the notes on this beat are played with a slap-style (bass). - - - - - Gets or sets a value indicating whether the notes on this beat are played with a tap-style (bass). - - - - - Gets or sets the text annotation shown on this beat. - - - - - Gets or sets the brush type applied to the notes of this beat. - - - - - Gets or sets the duration of the brush between the notes in midi ticks. - - - - - Gets or sets the tuplet denominator. - - - - - Gets or sets the tuplet numerator. - - - - - Gets or sets whether there is a tuplet applied to the duration of this beat. - - - - - Gets or sets whether this beat continues a whammy effect. - - - - - Gets or sets the whammy bar style of this beat. - - - - - Gets or sets the points defining the whammy bar usage. - - - - - Gets or sets the highest point with for the highest whammy bar value. - - - - - Gets or sets the highest point with for the lowest whammy bar value. - - - - - Gets a value indicating whether a whammy bar is used on this beat. - - - - - Gets or sets the vibrato effect used on this beat. - - - - - Gets or sets the ID of the chord used on this beat. - - - - - Gets a value indicating whether a chord is used on this beat. - - - - - Gets the chord used on this beat. - - - - - Gets or sets the grace style of this beat. - - - - - Gets or sets the pickstroke applied on this beat. - - - - - Gets whether a tremolo effect is played on this beat. - - - - - Gets or sets the speed of the tremolo effect. - - - - - Gets or sets whether a crescendo/decrescendo is applied on this beat. - - - - - The timeline position of the voice within the current bar as it is displayed. (unit: midi ticks) - - - This might differ from the actual playback time due to special grace types. - - - - - The timeline position of the voice within the current bar as it is played. (unit: midi ticks) - - - This might differ from the actual playback time due to special grace types. - - - - - Gets or sets the duration that is used for the display of this beat. It defines the size/width of the beat in - the music sheet. (unit: midi ticks). - - - - - Gets or sets the duration that the note is played during the audio generation. - - - - - Gets the absolute display start time within the song. - - - - - Gets the absolute playback start time within the song. - - - - - Gets or sets the dynamics applied to this beat. - - - - - Gets or sets a value indicating whether the beam direction should be inverted. - - - - - Initializes a new instance of the class. - - - - - Checks whether the current beat is timewise before the given beat. - - - - - - - Checks whether the current beat is timewise after the given beat. - - - - - - - A single point of a bending graph. Used to - describe WhammyBar and String Bending effects. - - - - - The maximum offset for points - - - - - The maximum value for points. - - - - - Gets or sets offset of the point relative to the note duration (0-60) - - - - - Gets or sets the 1/4 note value offsets for the bend. - - - - - Initializes a new instance of the class. - - The offset. - The value. - - - - Lists the different bend styles - - - - - The bends are as described by the bend points - - - - - The bends are gradual over the beat duration. - - - - - The bends are done fast before the next note. - - - - - Lists all types of bends - - - - - No bend at all - - - - - Individual points define the bends in a flexible manner. - This system was mainly used in Guitar Pro 3-5 - - - - - Simple Bend from an unbended string to a higher note. - - - - - Release of a bend that was started on an earlier note. - - - - - A bend that starts from an unbended string, - and also releases the bend after some time. - - - - - Holds a bend that was started on an earlier note - - - - - A bend that is already started before the note is played then it is held until the end. - - - - - A bend that is already started before the note is played and - bends even further, then it is held until the end. - - - - - A bend that is already started before the note is played and - then releases the bend to a lower note where it is held until the end. - - - - - Lists all types of how to brush multiple notes on a beat. - - - - - No brush. - - - - - Normal brush up. - - - - - Normal brush down. - - - - - Arpeggio up. - - - - - Arpeggio down. - - - - - A chord definition. - - - - - Gets or sets the name of the chord - - - - - Indicates the first fret of the chord diagram. - - - - - Gets or sets the frets played on the individual strings for this chord. - - The order in this list goes from the highest string to the lowest string. - - -1 indicates that the string is not played. - - - - - Gets or sets a list of frets where the finger should hold a barre - - - - - Initializes a new instance of the class. - - - - - This public enumeration lists all supported Clefs. - - - - - Neutral clef. - - - - - C3 clef - - - - - C4 clef - - - - - F4 clef - - - - - G2 clef - - - - - Lists all Crescendo and Decrescendo types. - - - - - No crescendo applied. - - - - - Normal crescendo applied. - - - - - Normal decrescendo applied. - - - - - Lists all durations of a beat. - - - - - A quadruple whole note duration - - - - - A double whole note duration - - - - - A whole note duration - - - - - A 1/2 note duration - - - - - A 1/4 note duration - - - - - A 1/8 note duration - - - - - A 1/16 note duration - - - - - A 1/32 note duration - - - - - A 1/64 note duration - - - - - A 1/128 note duration - - - - - A 1/256 note duration - - - - - Lists all dynamics. - - - - - pianississimo (very very soft) - - - - - pianissimo (very soft) - - - - - piano (soft) - - - - - mezzo-piano (half soft) - - - - - mezzo-forte (half loud) - - - - - forte (loud) - - - - - fortissimo (very loud) - - - - - fortississimo (very very loud) - - - - - Represents a fermata. - - - - - Gets or sets the type of fermata. - - - - - Gets or sets the actual lenght of the fermata. - - - - - Lists all types of fermatas - - - - - A short fermata (triangle symbol) - - - - - A medium fermata (round symbol) - - - - - A long fermata (rectangular symbol) - - - - - Lists all fingers. - - - - - Unknown type (not documented) - - - - - No finger, dead note - - - - - The thumb - - - - - The index finger - - - - - The middle finger - - - - - The annular finger - - - - - The little finger - - - - - Lists all types of grace notes - - - - - No grace, normal beat. - - - - - The beat contains on-beat grace notes. - - - - - The beat contains before-beat grace notes. - - - - - The beat contains very special bend-grace notes used in SongBook style displays. - - - - - Lists all harmonic types. - - - - - No harmonics. - - - - - Natural harmonic - - - - - Artificial harmonic - - - - - Pinch harmonics - - - - - Tap harmonics - - - - - Semi harmonics - - - - - Feedback harmonics - - - - - This public enumeration lists all available key signatures - - - - - Cb (7 flats) - - - - - Gb (6 flats) - - - - - Db (5 flats) - - - - - Ab (4 flats) - - - - - Eb (3 flats) - - - - - Bb (2 flats) - - - - - F (1 flat) - - - - - C (no signs) - - - - - G (1 sharp) - - - - - D (2 sharp) - - - - - A (3 sharp) - - - - - E (4 sharp) - - - - - B (5 sharp) - - - - - F# (6 sharp) - - - - - C# (8 sharp) - - - - - This public enumeration lists all available types of KeySignatures - - - - - Major - - - - - Minor - - - - - Represents the lyrics of a song. - - - - - Gets or sets he start bar on which the lyrics should begin. - - - - - Gets or sets the raw lyrics text in Guitar Pro format. - (spaces split word syllables, plus merge syllables, [..] are comments) - - - - - Gets or sets the prepared chunks of the lyrics to apply to beats. - - - - - The MasterBar stores information about a bar which affects - all tracks. - - - - - The maximum alternate endings. (1 byte with 8 bitflags) - - - - - Gets or sets the bitflag for the alternate endings. Each bit defines for which repeat counts - the bar is played. - - - - - Gets or sets the next masterbar in the song. - - - - - Gets or sets the next masterbar in the song. - - - - - Gets the zero based index of the masterbar. - - - - - Gets or sets the key signature used on all bars. - - - - - Gets or sets the type of key signature (major/minor) - - - - - Gets or sets whether a double bar is shown for this masterbar. - - - - - Gets or sets whether a repeat section starts on this masterbar. - - - - - Gets or sets whether a repeat section ends on this masterbar. - - - - - Gets or sets the number of repeats for the current repeat section. - - - - - Gets or sets the repeat group this bar belongs to. - - - - - Gets or sets the time signature numerator. - - - - - Gets or sets the time signature denominiator. - - - - - Gets or sets whether this is bar has a common time signature. - - - - - Gets or sets the triplet feel that is valid for this bar. - - - - - Gets or sets the new section information for this bar. - - - - - Gets a value indicating whether a new section starts on this bar. - - - - - Gets or sets the tempo automation for this bar. - - - - - Gets or sets the reference to the score this song belongs to. - - - - - Gets or sets the fermatas for this bar. The key is the offset of the fermata in midi ticks. - - - - - The timeline position of the voice within the whole score. (unit: midi ticks) - - - - - Initializes a new instance of the class. - - - - - Calculates the time spent in this bar. (unit: midi ticks) - - - - - - Adds a fermata to the masterbar. - - The offset of the fermata within the bar in midi ticks. - The fermata. - - - - Gets the fermata for a given beat. - - The beat to get the fermata for. - - - - - This public class contains some utilities for working with model public classes - - - - - A note is a single played sound on a fretted instrument. - It consists of a fret offset and a string on which the note is played on. - It also can be modified by a lot of different effects. - - - - - This is a global counter for all notes. We use it - at several locations for lookup tables. - - - - - Gets or sets the unique id of this note. - - - - - Gets or sets the zero-based index of this note within the beat. - - - - - Gets or sets the accentuation of this note. - - - - - Gets or sets the bend type for this note. - - - - - Gets or sets the bend style for this note. - - - - - Gets or sets the note from which this note continues the bend. - - - - - Gets or sets whether this note continues a bend from a previous note. - - - - - Gets or sets a list of the points defining the bend behavior. - - - - - Gets or sets the bend point with the highest bend value. - - - - - Gets a value indicating whether this note is bended. - - - - - Gets a value indicating whether this note is defined via a string on the instrument. . - - - - - Gets or sets the fret on which this note is played on the instrument. - - - - - Gets or sets the string number where the note is placed. - 1 is the lowest string on the guitar and the bottom line on the tablature. - It then increases the the number of strings on available on the track. - - - - - Gets a value indicating whether the value of this note is defined via octave and tone. - - - - - Gets or sets the octave on which this note is played. - - - - - Gets or sets the tone of this note within the octave. - - - - - Gets a value indicating whether this note is a percussion note. - - - - - Gets or sets the percusson element. - - - - - Gets or sets the variation of this note. - - - - - Gets or sets whether this note is visible on the music sheet. - - - - - Gets or sets whether this note starts a hammeron or pulloff. - - - - - Gets a value indicating whether this note ends a hammeron or pulloff. - - - - - Gets the origin of the hammeron/pulloff of this note. - - - - - Gets the destination for the hammeron/pullof started by this note. - - - - - Gets or sets whether this note starts a slur. - - - - - Gets or sets whether a slur finished or continues on this note. - - - - - Gets or sets the origin of the slur this note contributes to. - - - - - Gets or sets the destination of the slur this note contributes to. - - - - - Gets or sets whether this note has an harmonic effect. - - - - - Gets or sets the harmonic type applied to this note. - - - - - Gets or sets the value defining the harmonic pitch. - - - - - Gets or sets whether the note is a ghost note and shown in parenthesis. Also this will make the note a bit more silent. - - - - - Gets or sets whether this note has a let-ring effect. - - - - - Gets or sets the destination note for the let-ring effect. - - - - - Gets or sets whether this note has a palm-mute effect. - - - - - Gets or sets the destination note for the palm-mute effect. - - - - - Gets or sets whether the note is shown and played as dead note. - - - - - Gets or sets whether the note is played as staccato. - - - - - Gets or sets the slide type this note is played with. - - - - - Gets or sets the target note for several slide types. - - - - - Gets or sets whether a vibrato is played on the note. - - - - - Gets or sets the origin of the tied if this note is tied. - - - - - Gets or sets the desination of the tie. - - - - - Gets or sets whether this note is ends a tied note. - - - - - Gets or sets whether this note starts or continues a tied note. - - - - - Gets or sets the fingers used for this note on the left hand. - - - - - Gets or sets the fingers used for this note on the right hand. - - - - - Gets or sets whether this note has fingering defined. - - - - - Gets or sets the target note value for the trill effect. - - - - - Gets the fret for the trill. - - - - - Gets a value indicating whether this note has a trill effect. - - - - - Gets or sets the speed of the trill effect. - - - - - Gets or sets the percentual duration of the note relative to the overall beat duration . - - - - - Gets or sets how accidetnals for this note should be handled. - - - - - Gets or sets the reference to the parent beat to which this note belongs to. - - - - - Gets or sets the dynamics for this note. - - - - - Gets the base note value for the string of this note. - - - - - Gets the absolute value of this note for playback. - - - - - Gets or sets the harmonic pitch value for this note. - - - - - Gets the absolute value of this note considering - offsets by bends and ottavia - - - - - Gets the absolute value of this note considering all effects beside bends. - - - - - Gets or sets whether the note has a offset of a quartertone caused by bends. - - - - - Initializes a new instance of the class. - - - - - Lists the modes how accidentals are handled for notes - - - - - Accidentals are calculated automatically. - - - - - If the default behavior calculates a Sharp, use flat instead (and vice versa). - - - - - This will move the note one line down and applies a Naturalize. - - - - - This will move the note one line down and applies a Sharp. - - - - - This will move the note one line up and applies a Flat. - - - - - Lists all ottavia. - - - - - 2 octaves higher - - - - - 1 octave higher - - - - - Normal - - - - - 1 octave lower - - - - - 2 octaves lower. - - - - - Lists all types of pick strokes. - - - - - No pickstroke used. - - - - - Pickstroke up. - - - - - Pickstroke down - - - - - This public class stores the midi specific information of a track needed - for playback. - - - - - Gets or sets the volume (0-16) - - - - - Gets or sets the balance (0-16; 8=center) - - - - - Gets or sets the midi port to use. - - - - - Gets or sets the midi program to use. - - - - - Gets or sets the primary channel for all normal midi events. - - - - - Gets or sets the secondary channel for special midi events. - - - - - Gets or sets whether the track is muted. - - - - - Gets or sets whether the track is playing alone. - - - - - Initializes a new instance of the class. - - - - - This class represents the rendering stylesheet. - It contains settings which control the display of the score when rendered. - - - - - Gets or sets whether dynamics are hidden. - - - - - Initializes a new instance of the class. - - - - - This public class can store the information about a group of measures which are repeated - - - - - All masterbars repeated within this group - - - - - a list of masterbars which open the group. - - - - - a list of masterbars which close the group. - - - - - true if the repeat group was opened well - - - - - true if the repeat group was closed well - - - - - Initializes a new instance of the class. - - - - - The score is the root node of the complete - model. It stores the basic information of - a song and stores the sub components. - - - - - The album of this song. - - - - - The artist who performs this song. - - - - - The owner of the copyright of this song. - - - - - Additional instructions - - - - - The author of the music. - - - - - Some additional notes about the song. - - - - - The subtitle of the song. - - - - - The title of the song. - - - - - The author of the song lyrics - - - - - The author of this tablature. - - - - - Gets or sets the global tempo of the song in BPM. The tempo might change via . - - - - - Gets or sets the name/label of the tempo. - - - - - Gets or sets a list of all masterbars contained in this song. - - - - - Gets or sets a list of all tracks contained in this song. - - - - - Gets or sets the rendering stylesheet for this song. - - - - - Initializes a new instance of the class. - - - - - This public class is used to describe the beginning of a - section within a song. It acts like a marker. - - - - - Gets or sets the marker ID for this section. - - - - - Gets or sets the descriptional text of this section. - - - - - Initializes a new instance of the class. - - - - - Lists all simile mark types as they are assigned to bars. - - - - - No simile mark is applied - - - - - A simple simile mark. The previous bar is repeated. - - - - - A double simile mark. This value is assigned to the first - bar of the 2 repeat bars. - - - - - A double simile mark. This value is assigned to the second - bar of the 2 repeat bars. - - - - - This public enum lists all different types of finger slides on a string. - - - - - No slide. - - - - - Shift slide to next note on same string - - - - - Legato slide to next note on same string. - - - - - Slide into the note from below on the same string. - - - - - Slide into the note from above on the same string. - - - - - Slide out from the note from upwards on the same string. - - - - - Slide out from the note from downwards on the same string. - - - - - Pickslide down on this note - - - - - Pickslide up on this note - - - - - This class describes a single staff within a track. There are instruments like pianos - where a single track can contain multiple staffs. - - - - - Gets or sets the zero-based index of this staff within the track. - - - - - Gets or sets the reference to the track this staff belongs to. - - - - - Gets or sets a list of all bars contained in this staff. - - - - - Gets or sets a list of all chords defined for this staff. refers to entries in this lookup. - - - - - Gets or sets the fret on which a capo is set. s - - - - - Gets or sets the number of semitones this track should be - transposed. This applies to rendering and playback. - - - - - Gets or sets the number of semitones this track should be - transposed. This applies only to rendering. - - - - - Get or set the guitar tuning of the guitar. This tuning also indicates the number of strings shown in the - guitar tablature. Unlike the property this array directly represents - the order of the tracks shown in the tablature. The first item is the most top tablature line. - - - - - Gets or sets the name of the tuning. - - - - - Gets a value indicating whether this staff contains string based notes. - - - - - Gets or sets the staff kind. - - - - - Initializes a new instance of the class. - - - - - Represents the different kinds of staffs. - - - - - The staff should be shown as guitar tablature. - - - - - The staff should be shown as normal music notation without tabs. - - - - - The staff should be shown as percussion tabs. - - - - - The staff should be shown as mixed tab/music notaiton - - - - - This public class describes a single track or instrument of score. - It is bascially a list of staffs containing individual music notation kinds. - - - - - Gets or sets the zero-based index of this track. - - - - - Gets or sets the reference this track belongs to. - - - - - Gets or sets the list of staffs that are defined for this track. - - - - - Gets or sets the playback information for this track. - - - - - Gets or sets the display color defined for this track. - - - - - Gets or sets the long name of this track. - - - - - Gets or sets the short name of this track. - - - - - Initializes a new instance of the class. - - The stave count. - - - - This public enumeration lists all feels of triplets. - - - - - No triplet feel - - - - - Triplet 16th - - - - - Triplet 8th - - - - - Dotted 16th - - - - - Dotte d8th - - - - - Scottish 16th - - - - - Scottish 8th - - - - - This public class represents a predefined string tuning. - - - - - Gets the default tuning for the given string count. - - The string count. - The tuning for the given string count or null if the string count is not defined. - - - - Gets a list of all tuning presets for a given stirng count. - - The string count. - The list of known tunings for the given string count or an empty list if the string count is not defined. - - - - Tries to find a known tuning by a given list of tuning values. - - The values defining the tuning. - The known tuning. - - - - Gets or sets whether this is the standard tuning for this number of strings. - - - - - Gets or sets the name of the tuning. - - - - - Gets or sets the values for each string of the instrument. - - - - - Initializes a new instance of the class. - - The name. - The tuning. - if set to true [is standard]. - - - - Checks if the given string is a tuning inticator. - Checks if the given string is a tuning inticator. - - - - - - This public enum lists all vibrato types that can be performed. - - - - - No vibrato. - - - - - A slight vibrato. - - - - - A wide vibrato. - - - - - A voice represents a group of beats - that can be played during a bar. - - - - - Gets or sets the zero-based index of this voice within the bar. - - - - - Gets or sets the reference to the bar this voice belongs to. - - - - - Gets or sets the list of beats contained in this voice. - - - - - Gets or sets a value indicating whether this voice is empty. - - - - - Initializes a new instance of the class. - - - - - Lists all types of whammy bars - - - - - No whammy at all - - - - - Individual points define the whammy in a flexible manner. - This system was mainly used in Guitar Pro 3-5 - - - - - Simple dive to a lower or higher note. - - - - - A dive to a lower or higher note and releasing it back to normal. - - - - - Continue to hold the whammy at the position from a previous whammy. - - - - - Dive to a lower or higher note before playing it. - - - - - Dive to a lower or higher note before playing it, then change to another - note. - - - - - This is the base public class for creating blocks which can render bars. - - - - - Gets or sets whether this renderer is linked to the next one - by some glyphs like a vibrato effect - - - - - Gets or sets whether this renderer can wrap to the next line - or it needs to stay connected to the previous one. - (e.g. when having double bar repeats we must not separate the 2 bars) - - - - - Gets the top padding for the main content of the renderer. - Can be used to specify where i.E. the score lines of the notation start. - - - - - - Gets the bottom padding for the main content of the renderer. - Can be used to specify where i.E. the score lines of the notation end. - - - - - Lists the different position modes for - - - - - Gets the pre-notes position which is located before the accidentals - - - - - Gets the on-notes position which is located after the accidentals but before the note heads. - - - - - Gets the middel-notes position which is located after in the middle the note heads. - - - - - Get the post-notes position which is located at after the note heads. - - - - - Get the end-beat position which is located at the end of the beat. This position is almost - equal to the pre-notes position of the next beat. - - - - - This is the base public class for creating factories providing BarRenderers - - - - - Lists all sizing types of the effect bar glyphs - - - - - The effect glyph is placed above the pre-beat glyph which is before - the actual note in the area where also accidentals are renderered. - - - - - The effect glyph is placed above the on-beat glyph which is where - the actual note head glyphs are placed. - - - - - The effect glyph is placed above the on-beat glyph which is where - the actual note head glyphs are placed. The glyph will size to the end of - the applied beat. - - - - - The effect glyph is placed above the on-beat glyph and expaded to the - on-beat position of the next beat. - - - - - The effect glyph is placed above the on-beat glyph and expaded to the - on-beat position of the next beat. - - - - - The effect glyph is placed above the on-beat glyph and expaded to the - on-beat position of the next beat. The glyph will size to the end of - the applied beat. - - - - - The effect glyph is placed on the whole bar covering the whole width - - - - - This renderer is responsible for displaying effects above or below the other staves - like the vibrato. - - - - - Effect-Glyphs implementing this public interface get notified - as they are expanded over multiple beats. - - - - - Gets or sets the beat where the glyph belongs to. - - - - - Gets or sets the next glyph of the same type in case - the effect glyph is expanded when using . - - - - - Gets or sets the previous glyph of the same type in case - the effect glyph is expanded when using . - - - - - A glyph is a single symbol which can be added to a GlyphBarRenderer for automated - layouting and drawing of stacked symbols. - - - - - This glyph allows to group several other glyphs to be - drawn at the same x position - - - - - Gets a value whether this glyph is linked with a previous glyph for rendering. - This means this glyph will not be rendered itself, but rendered as part of the very first glyph of this link-group. - - - - - Gets a value whether this glyph is linked with the next glyph for rendering. - - - - - This simple glyph allows to put an empty region in to a BarRenderer. - - - - - This glyph acts as container for handling - multiple voice rendering - - - - - A public class implementing this public interface can provide the - data needed by a EffectBarRenderer to create effect glyphs dynamically. - - - - - Gets the unique effect name for this effect. (Used for grouping) - - - - - Gets a value indicating whether this effect can share the space - with other effects if required. - (Example: tempo and dynamics don't share their space with other effects, a let-ring and palm-mute will share the space if possible) - - true if this effect bar should only be created once for the first track, otherwise false. - - - - Gets a value indicating whether this effect glyphs - should only be added once on the first track if multiple tracks are rendered. - (Example: this allows to render the tempo changes only once) - - true if this effect bar should only be created once for the first track, otherwise false. - - - - Checks whether the given beat has the appropriate effect set and - needs a glyph creation - - - the beat storing the data - true if the beat has the effect set, otherwise false. - - - - Gets the sizing mode of the glyphs created by this info. - - the sizing mode to apply to the glyphs during layout - - - - Creates a new effect glyph for the given beat. - - the renderer which requests for glyph creation - the beat storing the data - the glyph which needs to be added to the renderer - - - - Checks whether an effect glyph can be expanded to a particular beat. - - the beat which already has the glyph applied - the beat which the glyph should get expanded to - true if the glyph can be expanded, false if a new glyph needs to be created. - - - - Represents the public interface of the component that can render scores. - - - - - Gets or sets the lookup which allows fast access to beats at a given position. - - - - - Invalidates the drawn music sheet and initiates a redraw. - - - - - Triggers a relayout to the given size including redrawing. - - - - - - Initiates the rendering of the specified tracks of the given score. - - The score defining the tracks. - The indexes of the tracks to draw. - - - - Occurs before the rendering of the tracks starts. - - - - - Occurs after the rendering of the tracks finished. - - - - - Occurs whenever a part of the whole music sheet is rendered and can be displayed. - - - - - Occurs when the whole rendering and layout process finished. - - - - - Occurs whenever an error happens. - - - - - Updates the settings to the given object. - - - - - - Destroys the renderer. - - - - - This eventargs define the details about the rendering and layouting process and are - provided whenever a part of of the music sheet is rendered. - - - - - Gets or sets the width of the current rendering result. - - - - - Gets or sets the height of the current rendering result. - - - - - Gets or sets the currently known total width of the final music sheet. - - - - - Gets or sets the currently known total height of the final music sheet. - - - - - Gets or sets the index of the first masterbar that was rendered in this result. - - - - - Gets or sets the index of the last masterbar that was rendered in this result. - - - - - Gets or sets the render engine specific result object which contains the rendered music sheet. - - - - - A list of the elements which can be shown in the header and footer - of a rendered song sheet. All values can be combined using bit-operators as they are flags. - - - - - No elements get rendered. - - - - - Enables rendering of the title. - - - - - Enables rendering of the subtitle. - - - - - Enables rendering of the artist. - - - - - Enables rendering of the album. - - - - - Enables rendering of the words. - - - - - Enables rendering of the music. - - - - - Enables rendering of the words and music. - - - - - Enables rendering of the copyright. - - - - - Enables rendering of the page number. - - - - - Enables rendering of all elements. - - - - - This layout arranges the bars all horizontally - - - - - This layout arranges the bars into a fixed width and dynamic height region. - - - - - Realignes the bars in this line according to the available space - - - - - This is the base public class for creating new layouting engines for the score renderer. - - - - - This public class contains central definitions for controlling the visual appearance. - - - - - This BarRenderer renders a bar using standard music notation. - - - - - The step offsets of sharp symbols for sharp key signatures. - - - - - The step offsets of sharp symbols for flat key signatures. - - - - - Gets the relative y position of the given steps relative to first line. - - the amount of steps while 2 steps are one line - - - - - - This Factory procudes ScoreBarRenderer instances - - - - - This is the main wrapper of the rendering engine which - can render a single track of a score object into a notation sheet. - - - - - This public class stores size information about a stave. - It is used by the layout engine to collect the sizes of score parts - to align the parts across multiple staves. - - - - - an internal version number that increments whenever a change was made. - - - - - This container represents a single column of bar renderers independent from any staves. - This container can be used to reorganize renderers into a new staves. - - - - - A Staff represents a single line within a StaveGroup. - It stores BarRenderer instances created from a given factory. - - - - - This is the index of the track being rendered. This is not the index of the track within the model, - but the n-th track being rendered. It is the index of the array defining - which tracks should be rendered. - For single-track rendering this will always be zero. - - - - - This is the visual offset from top where the - Staff contents actually start. Used for grouping - using a accolade - - - - - This is the visual offset from top where the - Staff contents actually ends. Used for grouping - using a accolade - - - - - A Staff consists of a list of different staves and groups - them using an accolade. - - - - - Indicates whether this line is full or not. If the line is full the - bars can be aligned to the maximum width. If the line is not full - the bars will not get stretched. - - - - - The width that the content bars actually need - - - - - This BarRenderer renders a bar using guitar tablature notation - - - - - Gets the relative y position of the given steps relative to first line. - - the amount of steps while 2 steps are one line - - - - - - This Factory produces TabBarRenderer instances - - - - - This small utilty public class allows the assignment of accidentals within a - desired scope. - - - - - a lookup list containing an info whether the notes within an octave - need an accidental rendered. the accidental symbol is determined based on the type of key signature. - - - - - Contains the list of notes within an octave have accidentals set. - - - - - We always have 7 steps per octave. - (by a step the offsets inbetween score lines is meant, - 0 steps is on the first line (counting from top) - 1 steps is on the space inbetween the first and the second line - - - - - Those are the amount of steps for the different clefs in case of a note value 0 - [Neutral, C3, C4, F4, G2] - - - - - The step offsets of the notes within an octave in case of for sharp keysignatures - - - - - The step offsets of the notes within an octave in case of for flat keysignatures - - - - - Calculates the accidental for the given note and assignes the value to it. - The new accidental type is also registered within the current scope - - - - - - - Calculates the accidental for the given note value and assignes the value to it. - The new accidental type is also registered within the current scope - - - - - - - - - Lists all types how two voices can be joined with bars. - - - - - Full Bar from current to next - - - - - A small Bar from current to previous - - - - - A small bar from current to next - - - - - This public class helps drawing beams and bars for notes. - - - - - stores the X-positions for beat indices - - - - - the number of fingering indicators that will be drawn - - - - - an indicator whether any beat has a tuplet on it. - - - - - the first min note within this group - - - - - the first max note within this group - - - - - the last min note within this group - - - - - the last max note within this group - - - - - the overall min note value within this group. - This includes values caused by bends. - - - - - the overall max note value within this group - This includes values caused by bends. - - - - - Returns whether the the position of the given beat, was registered by the staff of the given ID - - - - - - - - Maps the given note to a normal note value to place the note at the - correct line on score notation - - - - - - - Lists all modes on how alphaTab can handle the display and playback of music notation. - - - - - Music elements will be displayed and played as in Guitar Pro. - - - - - Music elements will be displayed and played as in traditional songbooks. - Changes: - 1. Bends - For bends additional grace beats are introduced. - Bends are categorized into gradual and fast bends. - - Gradual bends are indicated by beat text "grad" or "grad.". Bend will sound along the beat duration. - - Fast bends are done right before the next note. If the next note is tied even on-beat of the next note. - 2. Whammy Bars - Dips are shown as simple annotation over the beats - Whammy Bars are categorized into gradual and fast. - - Gradual whammys are indicated by beat text "grad" or "grad.". Whammys will sound along the beat duration. - - Fast whammys are done right the beat. - 3. Let Ring - Tied notes with let ring are not shown in standard notation - Let ring does not cause a longer playback, duration is defined via tied notes. - - - - - Lists all modes on how fingerings should be displayed. - - - - - Fingerings will be shown in the standard notation staff. - - - - - Fingerings will be shown in a effect band above the tabs in case - they have only a single note on the beat. - - - - - This object defines the details on how to generate the vibrato effects. - - - - - Gets or sets the wavelength of the note-wide vibrato in midi ticks. - - - - - Gets or sets the amplitude for the note-wide vibrato in semitones. - - - - - Gets or sets the wavelength of the note-slight vibrato in midi ticks. - - - - - Gets or sets the amplitude for the note-slight vibrato in semitones. - - - - - Gets or sets the wavelength of the beat-wide vibrato in midi ticks. - - - - - Gets or sets the amplitude for the beat-wide vibrato in semitones. - - - - - Gets or sets the wavelength of the beat-slight vibrato in midi ticks. - - - - - Gets or sets the amplitude for the beat-slight vibrato in semitones. - - - - - Represents the layout specific settings. - - - - - The layouting mode used to arrange the the notation. -
      -
    • page - Bars are aligned in rows using a fixed width
    • -
    • horizontal - Bars are aligned horizontally in one row
    • -
    -
    -
    - - - Additional layout mode specific settings. - mode=page -
      -
    • barsPerRow - Limit the displayed bars per row, -1 for sized based limit (integer, default:-1)
    • -
    • start - The bar start index to start layouting with (integer: default: 0)
    • -
    • count - The amount of bars to render overall, -1 for all till the end (integer, default:-1)
    • -
    • hideInfo - Render the song information or not (boolean, default:false)
    • -
    • hideTuning - Render the tuning information or not (boolean, default:false)
    • -
    • hideTrackNames - Render the track names or not (boolean, default:false)
    • -
    - mode=horizontal -
      -
    • start - The bar start index to start layouting with (integer: default: 0)
    • -
    • count - The amount of bars to render overall, -1 for all till the end (integer, default:-1)
    • -
    • hideTrackNames - Render the track names or not (boolean, default:false)
    • -
    -
    -
    - - - Initializes a new instance of the class. - - - - - Represents the stave specific settings. - - - - - The stave profile name as it is registered in - Default Profiles: -
      -
    • score-tab - Standard music notation and guitar tablature are rendered (default)
    • -
    • score - Only standard music notation is rendered
    • -
    • tab - Only guitar tablature is rendered
    • -
    -
    -
    - - - Additional stave sspecific settings - id=tab -
      -
    • rhythm - Renders rhythm beams to tablature notes
    • -
    -
    -
    - - - Defines all loglevels. - - - - - No logging - - - - - Debug level (internal details are displayed). - - - - - Info level (only important details are shown) - - - - - Warning level - - - - - Error level. - - - - - faster than enum - - -
    -
    diff --git a/Source/AlphaTab.CSharp/Collections/FastDictionary.cs b/Source/AlphaTab.CSharp/Collections/FastDictionary.cs deleted file mode 100644 index 41d2e5151..000000000 --- a/Source/AlphaTab.CSharp/Collections/FastDictionary.cs +++ /dev/null @@ -1,92 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System.Collections; -using System.Collections.Generic; -using System.Runtime.CompilerServices; - -namespace AlphaTab.Collections -{ - /// - /// Represents a collection of key-value pairs. - /// - /// - /// - public class FastDictionary : IEnumerable - { - private readonly Dictionary _dictionary; - - /// - /// Initializes a new instance of the class. - /// - public FastDictionary() - { - _dictionary = new Dictionary(); - } - - /// - /// Gets or sets the value at the specified index. - /// - /// The key to access the item. - /// The value stored at the specified index. - [IndexerName("Item")] - public TValue this[TKey index] - { - get => _dictionary[index]; - set => _dictionary[index] = value; - } - - /// - /// Gets the number of elements stored in this dictionary - /// - public int Count => _dictionary.Count; - - /// - public IEnumerator GetEnumerator() - { - return _dictionary.Keys.GetEnumerator(); - } - - /// - /// Removes the value with the specified key. - /// - /// The key to remove from the dictionary. - public void Remove(TKey key) - { - _dictionary.Remove(key); - } - - /// - /// Determines whether the dictionary container contains the specified key. - /// - /// The key to check the existence for. - /// - /// true if the specified key is contained; otherwise, false. - /// - public bool ContainsKey(TKey key) - { - return _dictionary.ContainsKey(key); - } - - /// - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } -} diff --git a/Source/AlphaTab.CSharp/Collections/FastList.cs b/Source/AlphaTab.CSharp/Collections/FastList.cs deleted file mode 100644 index 636aa118e..000000000 --- a/Source/AlphaTab.CSharp/Collections/FastList.cs +++ /dev/null @@ -1,150 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using AlphaTab.Audio.Synth.Midi.Event; - -namespace AlphaTab.Collections -{ - /// - /// Represents a strongly typed list of elements. - /// - /// The type fo the elements - /// - public class FastList : IEnumerable - { - private readonly List _list; - - /// - /// Initializes a new instance of the class. - /// - public FastList() - { - _list = new List(); - } - - private FastList(IEnumerable collection) - { - _list = new List(collection); - } - - /// - /// Gets the number of elements contained in the list. - /// - public int Count => _list.Count; - - /// - /// Gets or sets the value at the specified index. - /// - /// The index of which item to access. - /// The item located at the specified index. - [IndexerName("Item")] - public T this[int index] - { - get => _list[index]; - set => _list[index] = value; - } - - /// - /// Adds the specified item to the list. - /// - /// The item to be added. - public void Add(T item) - { - _list.Add(item); - } - - /// - /// Sorts the elements in the list using the specified comparison. - /// - /// The comparison to use when comparing elements for sorting. - public void Sort(Comparison comparison) - { - _list.Sort(comparison); - } - - /// - /// Clones this instance. - /// - /// - public FastList Clone() - { - return new FastList(this); - } - - /// - /// Removes the item at the specified index. - /// - /// The index to remove. - public void RemoveAt(int index) - { - _list.RemoveAt(index); - } - - /// - /// Converts the current list into an array of all elements. - /// - /// An array containing all elements. - public T[] ToArray() - { - return _list.ToArray(); - } - - /// - public IEnumerator GetEnumerator() - { - return _list.GetEnumerator(); - } - - /// - /// Searches for the given item in the list and returns the index. - /// - /// The item to search - /// The index at which the specified item was found, or -1 if the item is not contained in the list. - public int IndexOf(T item) - { - return _list.IndexOf(item); - } - - /// - /// Reverses the items in the list - /// - public void Reverse() - { - _list.Reverse(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - /// - /// Inserts an element at the specified index. - /// - /// The index at which the item should be inserted - /// The item to insert. - public void InsertAt(int insertPos, T item) - { - _list.Insert(insertPos, item); - } - } -} diff --git a/Source/AlphaTab.CSharp/Collections/SampleArray.cs b/Source/AlphaTab.CSharp/Collections/SampleArray.cs deleted file mode 100644 index 2e36c1267..000000000 --- a/Source/AlphaTab.CSharp/Collections/SampleArray.cs +++ /dev/null @@ -1,81 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; - -namespace AlphaTab.Audio.Synth.Ds -{ - /// - /// Represents an array of audiosamples. - /// - public class SampleArray - { - /// - /// Gets the audio samples as floats. - /// - public float[] Samples - { - get; - } - - /// - /// Initializes a new instance of the class. - /// - /// The length of the array. - public SampleArray(int length) - { - Samples = new float[length]; - } - - /// - /// Gets or sets the sample at the specified index. - /// - /// The index of the sample to get or set. - /// The sample at the specified index. - public float this[int index] - { - get => Samples[index]; - set => Samples[index] = value; - } - - /// - /// Gets the total number of samples contained in this array - /// - public int Length => Samples.Length; - - /// - /// Resets all samples in the array to 0. - /// - public void Clear() - { - Array.Clear(Samples, 0, Samples.Length); - } - - /// - /// Copies a range of samples from the given source array into the specified destination. - /// - /// The array where to copy the samples from. - /// The start index from which to start copying. - /// The array where to copy the samples to. - /// The start index at which the samples should be written in the destination array. - /// The number of samples to copy. - public static void Blit(SampleArray src, int srcPos, SampleArray dest, int destPos, int len) - { - Array.Copy(src.Samples, srcPos, dest.Samples, destPos, len); - } - } -} diff --git a/Source/AlphaTab.CSharp/Collections/StringBuilder.cs b/Source/AlphaTab.CSharp/Collections/StringBuilder.cs deleted file mode 100644 index 2bcc116e2..000000000 --- a/Source/AlphaTab.CSharp/Collections/StringBuilder.cs +++ /dev/null @@ -1,53 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using System.Globalization; - -namespace AlphaTab.Collections -{ - class StringBuilder - { - private readonly System.Text.StringBuilder _sb; - - public StringBuilder() - { - _sb = new System.Text.StringBuilder(); - } - - public void Append(object s) - { - _sb.Append(Convert.ToString(s, CultureInfo.InvariantCulture)); - } - - public void AppendChar(int i) - { - _sb.Append(Platform.Platform.StringFromCharCode(i)); - } - - public void AppendLine(string s = "") - { - _sb.Append(s).Append("\r\n"); - } - - public override string ToString() - { - return _sb.ToString(); - } - } -} diff --git a/Source/AlphaTab.CSharp/Environment.cs b/Source/AlphaTab.CSharp/Environment.cs deleted file mode 100644 index 5fabfd571..000000000 --- a/Source/AlphaTab.CSharp/Environment.cs +++ /dev/null @@ -1,37 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Platform.CSharp; -using AlphaTab.Platform.Svg; -using AlphaTab.Rendering; - -namespace AlphaTab -{ - partial class Environment - { - static void PlatformInit() - { - RenderEngines["default"] = () => new SkiaCanvas(); - RenderEngines["svg"] = () => new CssFontSvgCanvas(); -#if NET471 - RenderEngines["gdi"] = () => new GdiCanvas(); -#endif - RenderEngines["skia"] = () => new SkiaCanvas(); - } - } -} diff --git a/Source/AlphaTab.CSharp/IO/ReadableStream.cs b/Source/AlphaTab.CSharp/IO/ReadableStream.cs deleted file mode 100644 index 284716b77..000000000 --- a/Source/AlphaTab.CSharp/IO/ReadableStream.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System; -using System.IO; - -namespace AlphaTab.IO -{ - class ReadableStream : Stream - { - private readonly IReadable _readable; - - private ReadableStream(IReadable readable) - { - _readable = readable; - } - - public static Stream Create(IReadable readable) - { - if (readable is StreamWrapper wrapper) - { - return wrapper.Stream; - } - else if (readable is ByteBuffer buffer) - { - return new MemoryStream(buffer.GetBuffer(), false); - } - - return new ReadableStream(readable); - } - - - public override void Flush() - { - } - - public override long Seek(long offset, SeekOrigin origin) - { - switch (origin) - { - case SeekOrigin.Begin: - _readable.Position = (int)offset; - break; - case SeekOrigin.Current: - _readable.Position += (int)offset; - break; - case SeekOrigin.End: - _readable.Position = _readable.Length - (int)offset - 1; - break; - } - - return _readable.Position; - } - - public override void SetLength(long value) - { - throw new NotSupportedException(); - } - - public override int Read(byte[] buffer, int offset, int count) - { - return _readable.Read(buffer, offset, count); - } - - public override void Write(byte[] buffer, int offset, int count) - { - throw new NotSupportedException(); - } - - public override bool CanRead => true; - public override bool CanSeek => true; - public override bool CanWrite => false; - public override long Length => _readable.Length; - - public override long Position - { - get => _readable.Position; - set => _readable.Position = (int)value; - } - } -} diff --git a/Source/AlphaTab.CSharp/IO/StreamWrapper.cs b/Source/AlphaTab.CSharp/IO/StreamWrapper.cs deleted file mode 100644 index 7b8c36810..000000000 --- a/Source/AlphaTab.CSharp/IO/StreamWrapper.cs +++ /dev/null @@ -1,84 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using System.IO; - -namespace AlphaTab.IO -{ - sealed class StreamWrapper : IReadable, IWriteable, IDisposable - { - public Stream Stream { get; } - - public int Position - { - get { return (int)Stream.Position; } - set { Stream.Position = value; } - } - - public int Length => (int)Stream.Length; - - public StreamWrapper(Stream stream) - { - Stream = stream; - } - - public void Reset() - { - Stream.Seek(0, SeekOrigin.Begin); - } - - public void Skip(int offset) - { - Stream.Seek(offset, SeekOrigin.Current); - } - - public int ReadByte() - { - return Stream.ReadByte(); - } - - public int Read(byte[] buffer, int offset, int count) - { - return Stream.Read(buffer, offset, count); - } - - public void WriteByte(byte value) - { - Stream.WriteByte(value); - } - - public void Write(byte[] buffer, int offset, int count) - { - Stream.Write(buffer, offset, count); - } - - public byte[] ReadAll() - { - using (var ms = new MemoryStream()) - { - Stream.CopyTo(ms); - return ms.ToArray(); - } - } - - public void Dispose() - { - Stream.Dispose(); - } - } -} diff --git a/Source/AlphaTab.CSharp/IO/ZipFile.cs b/Source/AlphaTab.CSharp/IO/ZipFile.cs deleted file mode 100644 index 628e3b0f9..000000000 --- a/Source/AlphaTab.CSharp/IO/ZipFile.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System.IO; - -namespace AlphaTab.IO -{ - partial class ZipFile - { - /// - /// Load a complete ZipFile to the memory. - /// - /// the binary source to read from. - /// - public void Load(IReadable s) - { -#if NET471 || NETSTANDARD2_0 - using (var zipArchive = new System.IO.Compression.ZipArchive(ReadableStream.Create(s), System.IO.Compression.ZipArchiveMode.Read)) - { - foreach (var entry in zipArchive.Entries) - { - if (FileFilter == null || FileFilter(entry.Name)) - { - using (var data = new MemoryStream((int)entry.Length)) - using (var source = entry.Open()) - { - source.CopyTo(data); - Entries.Add(new ZipEntry - { - Data = data.ToArray(), - FileName = entry.Name - }); - } - } - } - } -#elif ANDROID - using (var zipInputStream = new Java.Util.Zip.ZipInputStream(ReadableStream.Create(s))) - { - Java.Util.Zip.ZipEntry entry; - byte[] copyBuffer = new byte[4096]; - while ((entry = zipInputStream.NextEntry) != null) - { - if (FileFilter == null || FileFilter(entry.Name)) - { - using (var data = new MemoryStream((int) entry.Size)) - { - int c; - while ((c = zipInputStream.Read(copyBuffer)) > 0) - { - data.Write(copyBuffer, 0, c); - } - - Entries.Add(new ZipEntry - { - Data = data.ToArray(), - FileName = entry.Name - }); - } - } - } - - } -#endif - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab.CSharp/Platform/CSharp/GdiCanvas.cs b/Source/AlphaTab.CSharp/Platform/CSharp/GdiCanvas.cs deleted file mode 100644 index 1393bd81a..000000000 --- a/Source/AlphaTab.CSharp/Platform/CSharp/GdiCanvas.cs +++ /dev/null @@ -1,433 +0,0 @@ -#if NET471 -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Rendering; -using AlphaTab.Rendering.Glyphs; -using AlphaTab.Rendering.Utils; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Drawing; -using System.Drawing.Drawing2D; -using System.Drawing.Imaging; -using System.Drawing.Text; -using System.Runtime.InteropServices; -using AlphaTab.Platform.Model; -using Color = AlphaTab.Platform.Model.Color; -using Font = AlphaTab.Platform.Model.Font; -using FontStyle = AlphaTab.Platform.Model.FontStyle; -using GdiFont = System.Drawing.Font; -using GdiFontStyle = System.Drawing.FontStyle; -using GdiColor = System.Drawing.Color; - -namespace AlphaTab.Platform.CSharp -{ - class GdiCanvas : ICanvas - { - protected const float BlurCorrection = 0.5f; - - private static readonly Bitmap MeasurementImage; - private static readonly Graphics MeasurementGraphics; - private static readonly PrivateFontCollection MusicFontCollection; - private static readonly StringFormat MusicFontFormat; - - static GdiCanvas() - { - MeasurementImage = new Bitmap(1, 1); - var newGraphics = MeasurementGraphics = Graphics.FromImage(MeasurementImage); - newGraphics.SmoothingMode = SmoothingMode.HighQuality; - newGraphics.TextRenderingHint = TextRenderingHint.AntiAlias; - newGraphics.Clear(GdiColor.Transparent); - - MusicFontFormat = new StringFormat(StringFormat.GenericTypographic) - { - LineAlignment = StringAlignment.Center, - Alignment = StringAlignment.Near - }; - - MusicFontCollection = new PrivateFontCollection(); - - using (var bravura = typeof(GdiCanvas).Assembly.GetManifestResourceStream(typeof(GdiCanvas), "Bravura.ttf")) - { - var dataPtr = Marshal.AllocCoTaskMem((int)bravura.Length); - try - { - var fontData = new byte[bravura.Length]; - bravura.Read(fontData, 0, fontData.Length); - Marshal.Copy(fontData, 0, dataPtr, fontData.Length); - - MusicFontCollection.AddMemoryFont(dataPtr, fontData.Length); - } - finally - { - Marshal.FreeCoTaskMem(dataPtr); - } - } - } - - private static readonly Dictionary FontLookup = new Dictionary(); - private static GdiFont GetMusicFont(float scale) - { - GdiFont font; - if (!FontLookup.TryGetValue(scale, out font)) - { - FontLookup[scale] = font = new GdiFont(MusicFontCollection.Families[0], 34 * scale, GdiFontStyle.Regular, GraphicsUnit.Pixel); - } - return font; - } - - - private Bitmap _image; - private float _width; - private float _height; - private Graphics _graphics; - - private GraphicsPath _currentPath; - - private float _currentX; - private float _currentY; - - private readonly StringFormat _stringFormat; - - private float _lineWidth; - private GdiFont _font; - private TextAlign _textAlign; - private TextBaseline _textBaseline; - - private SolidBrush _brush; - private Pen _pen; - private GdiColor _color; - - public RenderingResources Resources { get; set; } - - public Color Color - { - get - { - return new Color(_color.R, _color.G, _color.B, _color.A); - } - set - { - if (value == null) throw new ArgumentNullException("value"); - _color = GdiColor.FromArgb(value.A, value.R, value.G, value.B); - RecreateBrush(); - RecreatePen(); - } - } - - public float LineWidth - { - get { return _lineWidth; } - set - { - _lineWidth = value; - RecreatePen(); - } - } - - - public Font Font - { - get - { - FontStyle fs = FontStyle.Plain; - if (_font.Bold) fs |= FontStyle.Bold; - if (_font.Italic) fs |= FontStyle.Italic; - return new Font(_font.FontFamily.Name, _font.Size, fs); - } - set - { - var fontStyle = GdiFontStyle.Regular; - if (value.IsBold) fontStyle |= GdiFontStyle.Bold; - if (value.IsItalic) fontStyle = GdiFontStyle.Italic; - - _font = new GdiFont(value.Family, value.Size, fontStyle, GraphicsUnit.Pixel); - } - } - - public TextAlign TextAlign - { - get { return _textAlign; } - set - { - _textAlign = value; - switch (value) - { - case TextAlign.Left: - _stringFormat.Alignment = StringAlignment.Near; - break; - case TextAlign.Center: - _stringFormat.Alignment = StringAlignment.Center; - break; - case TextAlign.Right: - _stringFormat.Alignment = StringAlignment.Far; - break; - } - } - } - - public TextBaseline TextBaseline - { - get { return _textBaseline; } - set - { - _textBaseline = value; - switch (value) - { - case TextBaseline.Top: - _stringFormat.LineAlignment = StringAlignment.Near; - break; - case TextBaseline.Middle: - _stringFormat.LineAlignment = StringAlignment.Center; - break; - case TextBaseline.Bottom: - _stringFormat.LineAlignment = StringAlignment.Far; - break; - } - } - } - - public GdiCanvas() - { - _width = 1; - _height = 1; - - _currentPath = new GraphicsPath(FillMode.Winding); - _stringFormat = new StringFormat(StringFormat.GenericTypographic); - _stringFormat.LineAlignment = StringAlignment.Near; - - _lineWidth = 1; - _currentX = 0; - _currentY = 0; - _font = new GdiFont("Arial", 10, GraphicsUnit.Pixel); - _textAlign = TextAlign.Left; - _textBaseline = TextBaseline.Top; - - Color = new Color(255, 255, 255); - - RecreateImage(); - } - - public void BeginRender(float width, float height) - { - _width = width; - _height = height; - RecreateImage(); - } - - public object EndRender() - { - _graphics.Dispose(); - return _image; - } - - public virtual object OnPreRender() - { - // nothing to do - return null; - } - - public virtual object OnRenderFinished() - { - // nothing to do - return null; - } - - public void BeginGroup(string identifier) - { - } - - public void EndGroup() - { - } - - private void RecreateImage() - { - var newImage = new Bitmap((int)_width, (int)_height, PixelFormat.Format32bppPArgb); - var newGraphics = Graphics.FromImage(newImage); - newGraphics.CompositingMode = CompositingMode.SourceOver; - newGraphics.SmoothingMode = SmoothingMode.HighQuality; - newGraphics.TextRenderingHint = TextRenderingHint.AntiAlias; - - if (_graphics != null) - _graphics.Dispose(); - - _image = newImage; - _graphics = newGraphics; - } - - private void RecreatePen() - { - var newPen = new Pen(_color, _lineWidth); - if (_pen != null) - { - _pen.Dispose(); - } - _pen = newPen; - } - - private void RecreateBrush() - { - var newBrush = new SolidBrush(_color); - if (_brush != null) - { - _brush.Dispose(); - } - _brush = newBrush; - } - - public void Clear() - { - _graphics.Clear(GdiColor.Transparent); - } - - public void FillRect(float x, float y, float w, float h) - { - x = ((int)x - BlurCorrection); - y = ((int)y - BlurCorrection); - _graphics.FillRectangle(_brush, x, y, w, h); - } - - public void StrokeRect(float x, float y, float w, float h) - { - x = ((int)x - BlurCorrection); - y = ((int)y - BlurCorrection); - _graphics.DrawRectangle(_pen, x, y, w, h); - } - - public void BeginPath() - { - _currentPath.StartFigure(); - } - - public void ClosePath() - { - _currentPath.CloseFigure(); - } - - public void MoveTo(float x, float y) - { - x = ((int)x - BlurCorrection); - y = ((int)y - BlurCorrection); - _currentX = x; - _currentY = y; - } - - public void LineTo(float x, float y) - { - x = ((int)x - BlurCorrection); - y = ((int)y - BlurCorrection); - _currentPath.AddLine(_currentX, _currentY, x, y); - _currentX = x; - _currentY = y; - } - - public void QuadraticCurveTo(float cpx, float cpy, float x, float y) - { - _currentPath.AddBezier(_currentX, _currentY, cpx, cpy, cpx, cpy, x, y); - _currentX = x; - _currentY = y; - } - - public void BezierCurveTo(float cp1x, float cp1y, float cp2x, float cp2y, float x, float y) - { - _currentPath.AddBezier(_currentX, _currentY, cp1x, cp1y, cp2x, cp2y, x, y); - _currentX = x; - _currentY = y; - } - - public void FillCircle(float x, float y, float radius) - { - _currentPath.StartFigure(); - _currentPath.AddEllipse(x - radius, y - radius, radius * 2, radius * 2); - _currentPath.CloseFigure(); - _currentX = x; - _currentY = y; - Fill(); - } - - public void Fill() - { - _graphics.FillPath(_brush, _currentPath); - _currentPath.Dispose(); - _currentPath = new GraphicsPath(FillMode.Winding); - } - - public void Stroke() - { - _graphics.DrawPath(_pen, _currentPath); - _currentPath.Dispose(); - _currentPath = new GraphicsPath(FillMode.Winding); - } - - public void FillText(string text, float x, float y) - { - _graphics.DrawString(text, _font, _brush, new Point((int)x, (int)y), _stringFormat); - } - - public float MeasureText(string text) - { - lock (MeasurementGraphics) - { - return MeasurementGraphics.MeasureString(text, _font).Width; - } - } - - public void FillMusicFontSymbol(float x, float y, float scale, MusicFontSymbol symbol) - { - if (symbol == MusicFontSymbol.None) - { - return; - } - - // for whatever reason the padding on GDI font rendering is a bit messed up, there is 1px padding on the left - x += scale; - - _graphics.DrawString(Platform.StringFromCharCode((int)symbol), GetMusicFont(scale), _brush, x, y, MusicFontFormat); - } - - public void FillMusicFontSymbols(float x, float y, float scale, MusicFontSymbol[] symbols) - { - var s = ""; - foreach (var symbol in symbols) - { - if (symbol != MusicFontSymbol.None) - { - s += Platform.StringFromCharCode((int)symbol); - } - } - - // for whatever reason the padding on GDI font rendering is a bit messed up, there is 1px padding on the left - x += scale; - - _graphics.DrawString(s, GetMusicFont(scale), _brush, x, y, MusicFontFormat); - } - - public void BeginRotate(float centerX, float centerY, float angle) - { - _graphics.TranslateTransform(centerX, centerY); - _graphics.RotateTransform(angle); - } - - public void EndRotate() - { - _graphics.ResetTransform(); - } - } -} -#endif \ No newline at end of file diff --git a/Source/AlphaTab.CSharp/Platform/CSharp/SkiaCanvas.cs b/Source/AlphaTab.CSharp/Platform/CSharp/SkiaCanvas.cs deleted file mode 100644 index 25d0935b7..000000000 --- a/Source/AlphaTab.CSharp/Platform/CSharp/SkiaCanvas.cs +++ /dev/null @@ -1,327 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System.Reflection; -using AlphaTab.Platform.Model; -using AlphaTab.Rendering; -using AlphaTab.Rendering.Glyphs; -using SkiaSharp; - -namespace AlphaTab.Platform.CSharp -{ - class SkiaCanvas : ICanvas - { - private static readonly SKTypeface MusicFont; - private static readonly int MusicFontSize = 34; - - static SkiaCanvas() - { - var type = typeof(SkiaCanvas).GetTypeInfo(); - var bravura = type.Assembly.GetManifestResourceStream(type.Namespace + ".Bravura.ttf"); - { - MusicFont = SKTypeface.FromStream(bravura); - } - } - - private SKSurface _surface; - private SKPath _path; - private string _typeFaceCache; - private SKTypeface _typeFace; - private float _width; - private float _height; - - public Color Color { get; set; } - public float LineWidth { get; set; } - public Font Font { get; set; } - - public SKTypeface TypeFace - { - get - { - if (_typeFaceCache != Font.ToCssString()) - { - if (_typeFace != null) - { - _typeFace.Dispose(); - } - _typeFaceCache = Font.ToCssString(); - _typeFace = SKTypeface.FromFamilyName(Font.Family, - Font.IsBold ? SKFontStyleWeight.Bold : SKFontStyleWeight.Normal, - SKFontStyleWidth.Normal, - Font.IsItalic ? SKFontStyleSlant.Italic : SKFontStyleSlant.Upright - ); - } - return _typeFace; - } - } - - public TextAlign TextAlign { get; set; } - - public TextBaseline TextBaseline { get; set; } - public RenderingResources Resources { get; set; } - - public SkiaCanvas() - { - Color = new Color(255, 255, 255); - LineWidth = 1; - Font = new Font("Arial", 10); - TextAlign = TextAlign.Left; - TextBaseline = TextBaseline.Middle; - } - - public void BeginRender(float width, float height) - { - _width = width; - _height = height; - var newImage = SKSurface.Create((int)width, (int)height, SKImageInfo.PlatformColorType, SKAlphaType.Premul); - _surface = newImage; - if (_path != null) - { - _path.Dispose(); - } - _path = new SKPath(); - _path.FillType = SKPathFillType.Winding; - } - - public object EndRender() - { - var image = _surface.Snapshot(); - _surface.Dispose(); - return image; - } - - public virtual object OnPreRender() - { - // nothing to do - return null; - } - - public virtual object OnRenderFinished() - { - // nothing to do - return null; - } - - public void Clear() - { - _surface.Canvas.Clear(SKColors.Transparent); - } - - public void FillRect(float x, float y, float w, float h) - { - using (var paint = CreatePaint()) - { - paint.BlendMode = SKBlendMode.SrcOver; - _surface.Canvas.DrawRect(SKRect.Create(x, y, w, h), paint); - } - } - - private SKPaint CreatePaint() - { - var paint = new SKPaint(); - paint.IsAntialias = true; - paint.Color = (SKColor)((uint)((int)Color.A << 24 | (int)Color.R << 16 | (int)Color.G << 8) | (uint)Color.B); - return paint; - } - - public void StrokeRect(float x, float y, float w, float h) - { - using (var paint = CreatePaint()) - { - paint.BlendMode = SKBlendMode.SrcOver; - paint.StrokeWidth = LineWidth; - paint.IsStroke = true; - _surface.Canvas.DrawRect(SKRect.Create(x, y, w, h), paint); - } - } - - public void BeginPath() - { - _path.Reset(); - } - - public void ClosePath() - { - _path.Close(); - } - - public void MoveTo(float x, float y) - { - _path.MoveTo(x, y); - } - - public void LineTo(float x, float y) - { - _path.LineTo(x, y); - } - - public void QuadraticCurveTo(float cpx, float cpy, float x, float y) - { - _path.QuadTo(cpx, cpy, x, y); - } - - public void BezierCurveTo(float cp1x, float cp1y, float cp2x, float cp2y, float x, float y) - { - _path.CubicTo(cp1x, cp1y, cp2x, cp2y, x, y); - } - - public void FillCircle(float x, float y, float radius) - { - BeginPath(); - _path.AddCircle(x, y, radius); - ClosePath(); - Fill(); - } - - public void Fill() - { - using (var paint = CreatePaint()) - { - _surface.Canvas.DrawPath(_path, paint); - } - - _path.Reset(); - } - - public void Stroke() - { - using (var paint = CreatePaint()) - { - paint.StrokeWidth = LineWidth; - paint.IsStroke = true; - _surface.Canvas.DrawPath(_path, paint); - } - _path.Reset(); - } - - public void BeginGroup(string identifier) - { - } - - public void EndGroup() - { - } - - public void FillText(string text, float x, float y) - { - using (var paint = CreatePaint()) - { - paint.Typeface = TypeFace; - paint.TextSize = Font.Size; - switch (TextAlign) - { - case TextAlign.Left: - paint.TextAlign = SKTextAlign.Left; - break; - case TextAlign.Center: - paint.TextAlign = SKTextAlign.Center; - break; - case TextAlign.Right: - paint.TextAlign = SKTextAlign.Right; - break; - } - - _surface.Canvas.DrawText(text, x, y + GetFontBaseline(TextBaseline, paint), paint); - } - } - - private float GetFontBaseline(TextBaseline baseline, SKPaint paint) - { - switch (baseline) - { - case TextBaseline.Top: // TopTextBaseline - return paint.FontMetrics.Ascent; - case TextBaseline.Middle: // MiddleTextBaseline - return -paint.FontMetrics.Descent + paint.TextSize / 2; - case TextBaseline.Bottom: // BottomTextBaseline - return -paint.FontMetrics.Descent; - default: - break; - } - - return 0; - } - - public float MeasureText(string text) - { - using (var paint = CreatePaint()) - { - paint.Typeface = TypeFace; - paint.TextSize = Font.Size; - switch (TextAlign) - { - case TextAlign.Left: - paint.TextAlign = SKTextAlign.Left; - break; - case TextAlign.Center: - paint.TextAlign = SKTextAlign.Center; - break; - case TextAlign.Right: - paint.TextAlign = SKTextAlign.Right; - break; - } - return paint.MeasureText(text); - } - } - - public void FillMusicFontSymbol(float x, float y, float scale, MusicFontSymbol symbol) - { - if (symbol == MusicFontSymbol.None) - { - return; - } - - using (var paint = CreatePaint()) - { - paint.Typeface = MusicFont; - paint.TextSize = MusicFontSize * scale; - _surface.Canvas.DrawText(Platform.StringFromCharCode((int)symbol), x, y, paint); - } - } - public void FillMusicFontSymbols(float x, float y, float scale, MusicFontSymbol[] symbols) - { - var s = ""; - foreach (var symbol in symbols) - { - if (symbol != MusicFontSymbol.None) - { - s += Platform.StringFromCharCode((int)symbol); - } - } - - using (var paint = CreatePaint()) - { - paint.Typeface = MusicFont; - paint.TextSize = MusicFontSize * scale; - _surface.Canvas.DrawText(s, x, y, paint); - } - } - - public void BeginRotate(float centerX, float centerY, float angle) - { - _surface.Canvas.Save(); - _surface.Canvas.Translate(centerX, centerY); - _surface.Canvas.RotateDegrees(angle); - } - - public void EndRotate() - { - _surface.Canvas.Restore(); - } - - } -} \ No newline at end of file diff --git a/Source/AlphaTab.CSharp/Platform/CSharp/WinForms/AlphaTabControl.cs b/Source/AlphaTab.CSharp/Platform/CSharp/WinForms/AlphaTabControl.cs deleted file mode 100644 index b1272c96f..000000000 --- a/Source/AlphaTab.CSharp/Platform/CSharp/WinForms/AlphaTabControl.cs +++ /dev/null @@ -1,427 +0,0 @@ -#if NET471 -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.ComponentModel; -using System.Drawing; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Threading; -using System.Threading.Tasks; -using System.Windows.Forms; -using AlphaTab.Model; -using AlphaTab.Rendering; - -namespace AlphaTab.Platform.CSharp.WinForms -{ - public class AlphaTabControl : Panel, INotifyPropertyChanged - { - private bool _initialRenderCompleted; - private bool _redrawPending; - private int _isRendering; // interlocked bool - private IEnumerable _tracks; - private float _scale; - private int _scoreWidth; - private string _layoutMode; - private float _stretchForce; - private string _stavesMode; - private float _actualScoreHeight; - private float _actualScoreWidth; - private string _renderEngine; - private int _totalResultCount; - - private AlphaTabLayoutPanel _layoutPanel; - - [Browsable(false)] - [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] - public IEnumerable Tracks - { - get { return _tracks; } - set - { - if (_tracks == value) return; - - var observable = _tracks as INotifyCollectionChanged; - if (observable != null) - { - observable.CollectionChanged -= OnTracksChanged; - } - - _tracks = value; - - observable = _tracks as INotifyCollectionChanged; - if (observable != null) - { - observable.CollectionChanged += OnTracksChanged; - } - - OnPropertyChanged(); - InvalidateTracks(true); - } - } - - [DefaultValue(1f)] - [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] - public float Scale - { - get { return _scale; } - set - { - if (value.Equals(_scale)) return; - _scale = value; - OnPropertyChanged(); - InvalidateTracks(true); - } - } - - [DefaultValue(-1)] - [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] - public int ScoreWidth - { - get { return _scoreWidth; } - set - { - if (value == _scoreWidth) return; - _scoreWidth = value; - OnPropertyChanged(); - OnPropertyChanged(nameof(ScoreAutoSize)); - InvalidateTracks(true); - - } - } - - [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] - public bool ScoreAutoSize - { - get { return ScoreWidth < 0; } - } - - [DefaultValue("page")] - [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] - public string LayoutMode - { - get { return _layoutMode; } - set - { - if (value == _layoutMode) return; - _layoutMode = value; - OnPropertyChanged(); - InvalidateTracks(true); - } - } - - [DefaultValue(1f)] - [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] - public float StretchForce - { - get { return _stretchForce; } - set - { - if (value.Equals(_stretchForce)) return; - _stretchForce = value; - OnPropertyChanged(); - InvalidateTracks(true); - } - } - - [DefaultValue("score-tab")] - [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] - public string StavesMode - { - get { return _stavesMode; } - set - { - if (value == _stavesMode) return; - _stavesMode = value; - OnPropertyChanged(); - InvalidateTracks(true); - } - } - - [Browsable(false)] - [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] - public float ActualScoreHeight - { - get { return _actualScoreHeight; } - private set - { - if (value.Equals(_actualScoreHeight)) return; - _actualScoreHeight = value; - OnPropertyChanged(); - } - } - - [Browsable(false)] - [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] - public float ActualScoreWidth - { - get { return _actualScoreWidth; } - private set - { - if (value.Equals(_actualScoreWidth)) return; - _actualScoreWidth = value; - OnPropertyChanged(); - } - } - - [DefaultValue("gdi")] - [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] - public string RenderEngine - { - get { return _renderEngine; } - set - { - if (value == _renderEngine) return; - _renderEngine = value; - OnPropertyChanged(); - InvalidateTracks(true); - } - } - - private ScoreRenderer _renderer; - - [Browsable(false)] - [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] - public IScoreRenderer Renderer => _renderer; - - public AlphaTabControl() - { - _layoutPanel = new AlphaTabLayoutPanel(); - AutoScroll = true; - Controls.Add(_layoutPanel); - - var settings = Settings.Defaults; - settings.Engine = "gdi"; - _scale = settings.Scale; - _scoreWidth = settings.Width; - _layoutMode = settings.Layout.Mode; - _stretchForce = settings.StretchForce; - _stavesMode = settings.Staves.Id; - _renderEngine = settings.Engine; - - _renderer = new ScoreRenderer(settings); - _renderer.PreRender += result => - { - BeginInvoke(new Action(() => - { - _layoutPanel.SuspendLayout(); - while (_layoutPanel.HasChildren) - { - var child = _layoutPanel.Controls[0] as PictureBox; - if (child != null) - { - _layoutPanel.Controls.Remove(child); - child.Image.Dispose(); - } - } - _layoutPanel.ResumeLayout(true); - GC.Collect(); - - AddPartialResult(result); - })); - }; - _renderer.PartialRenderFinished += result => - { - BeginInvoke(new Action(() => - { - AddPartialResult(result); - })); - }; - _renderer.RenderFinished += result => - { - BeginInvoke(new Action(() => - { - _initialRenderCompleted = true; - _isRendering = 0; - AddPartialResult(result); - OnRenderFinished(); - if (_redrawPending) - { - ResizeTrack(RenderWidth); - } - GC.Collect(); - })); - }; - } - - protected override void OnPaddingChanged(EventArgs e) - { - base.OnPaddingChanged(e); - if (_layoutPanel != null) - { - _layoutPanel.Location = new Point(Padding.Left, Padding.Top); - } - } - - protected override void OnControlAdded(ControlEventArgs e) - { - base.OnControlAdded(e); - if (e.Control != _layoutPanel) - { - Controls.Remove(e.Control); - } - } - - protected override void OnForeColorChanged(EventArgs e) - { - base.OnForeColorChanged(e); - if (_layoutPanel != null) - { - _layoutPanel.BackColor = ForeColor; - } - } - - private void OnTracksChanged(object sender, NotifyCollectionChangedEventArgs e) - { - InvalidateTracks(true); - } - - public void InvalidateTracks(bool force) - { - var trackArray = Tracks?.ToArray(); - if (trackArray == null || trackArray.Length == 0) return; - - var width = RenderWidth - Padding.Horizontal; - if (width > 0) - { - if (trackArray == _renderer.Tracks && !force) - { - return; - } - - var settings = _renderer.Settings; - settings.Width = width; - settings.Engine = RenderEngine; - settings.Scale = Scale; - settings.Layout.Mode = LayoutMode; - settings.StretchForce = StretchForce; - settings.Staves.Id = StavesMode; - _renderer.UpdateSettings(settings); - ModelUtils.ApplyPitchOffsets(settings, trackArray[0].Score); - - _initialRenderCompleted = false; - _isRendering = 1; - - Task.Factory.StartNew(() => - { - _renderer.Render(trackArray[0].Score, trackArray.Select(t=>t.Index).ToArray()); - }); - } - else - { - _initialRenderCompleted = false; - _redrawPending = true; - _isRendering = 0; - } - } - - private int RenderWidth - { - get - { - return (int)(ScoreAutoSize ? ClientRectangle.Width : ScoreWidth); - } - } - - protected override void OnClientSizeChanged(EventArgs e) - { - base.OnClientSizeChanged(e); - if (ScoreAutoSize) - { - ResizeTrack(RenderWidth); - } - } - - - private void ResizeTrack(double width) - { - int newWidth = (int)width - Padding.Horizontal; - if (Interlocked.Exchange(ref _isRendering, 1) == 1) - { - _redrawPending = true; - } - else if (width > 0) - { - _redrawPending = false; - if (!_initialRenderCompleted) - { - InvalidateTracks(true); - } - else if (newWidth != _renderer.Settings.Width) - { - Task.Factory.StartNew(() => - { - _renderer.Resize(newWidth); - }); - } - else - { - _isRendering = 0; - } - } - } - - private void AddPartialResult(RenderFinishedEventArgs result) - { - ActualScoreWidth = result.TotalWidth; - ActualScoreHeight = result.TotalHeight; - _layoutPanel.Width = (int)result.TotalWidth; - _layoutPanel.Height = (int)result.TotalHeight; - if (result.RenderResult != null) - { - var bitmap = result.RenderResult as Bitmap; - if (bitmap == null) - { - using (result.RenderResult as IDisposable) - { - bitmap = SkiaUtil.ToBitmap(result.RenderResult); - } - } - - var pic = new PictureBox - { - AutoSize = false, - BackColor = ForeColor, - Width = (int) result.Width, - Height = (int) result.Height, - Image = bitmap, - Padding = Padding.Empty, - Margin = Padding.Empty, - BorderStyle = BorderStyle.None - }; - _layoutPanel.Controls.Add(pic); - } - } - - public event EventHandler RenderFinished; - public event PropertyChangedEventHandler PropertyChanged; - - protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - - protected virtual void OnRenderFinished() - { - RenderFinished?.Invoke(this, EventArgs.Empty); - } - } -} -#endif \ No newline at end of file diff --git a/Source/AlphaTab.CSharp/Platform/CSharp/WinForms/AlphaTabLayoutPanel.cs b/Source/AlphaTab.CSharp/Platform/CSharp/WinForms/AlphaTabLayoutPanel.cs deleted file mode 100644 index 91412c257..000000000 --- a/Source/AlphaTab.CSharp/Platform/CSharp/WinForms/AlphaTabLayoutPanel.cs +++ /dev/null @@ -1,77 +0,0 @@ -#if NET471 -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System.ComponentModel; -using System.Diagnostics; -using System.Drawing; -using System.Windows.Forms; -using System.Windows.Forms.Layout; - -namespace AlphaTab.Platform.CSharp.WinForms -{ - public class AlphaTabLayoutPanel : Panel - { - private AlphaTabLayoutEngine _laoyutEngine; - - public override LayoutEngine LayoutEngine - { - get { return _laoyutEngine ?? (_laoyutEngine = new AlphaTabLayoutEngine()); } - } - - public AlphaTabLayoutPanel() - { - DoubleBuffered = true; - ResizeRedraw = true; - } - - class AlphaTabLayoutEngine : LayoutEngine - { - public override bool Layout(object container, LayoutEventArgs layoutEventArgs) - { - var parent = (Control)container; - - var xChild = 0; - var yChild = 0; - - var rowHeight = 0; - - foreach (Control child in parent.Controls) - { - child.Location = new Point(xChild, yChild); - - xChild += child.Width; - if (child.Height > rowHeight) - { - rowHeight = child.Height; - } - - if (xChild >= parent.Width) - { - xChild = 0; - yChild += rowHeight; - rowHeight = 0; - } - } - - return false; - } - } - } -} -#endif \ No newline at end of file diff --git a/Source/AlphaTab.CSharp/Platform/CSharp/WinForms/SkiaUtil.cs b/Source/AlphaTab.CSharp/Platform/CSharp/WinForms/SkiaUtil.cs deleted file mode 100644 index f9869e995..000000000 --- a/Source/AlphaTab.CSharp/Platform/CSharp/WinForms/SkiaUtil.cs +++ /dev/null @@ -1,26 +0,0 @@ -#if NET471 -using System.Drawing; -using System.Drawing.Imaging; -using SkiaSharp; - -namespace AlphaTab.Platform.CSharp.WinForms -{ - class SkiaUtil - { - public static Bitmap ToBitmap(object data) - { - var image = (SKImage) data; - var info = new SKImageInfo(image.Width, image.Height); - var bitmap = new Bitmap(image.Width, image.Height, PixelFormat.Format32bppPArgb); - var bitmapData = bitmap.LockBits(new Rectangle(Point.Empty, bitmap.Size), ImageLockMode.WriteOnly, bitmap.PixelFormat); - // copy - using (var pixmap = new SKPixmap(info, bitmapData.Scan0, bitmapData.Stride)) - { - image.ReadPixels(pixmap, 0, 0); - } - bitmap.UnlockBits(bitmapData); - return bitmap; - } - } -} -#endif \ No newline at end of file diff --git a/Source/AlphaTab.CSharp/Platform/CSharp/Wpf/AlphaTab.cs b/Source/AlphaTab.CSharp/Platform/CSharp/Wpf/AlphaTab.cs deleted file mode 100644 index f76473ac3..000000000 --- a/Source/AlphaTab.CSharp/Platform/CSharp/Wpf/AlphaTab.cs +++ /dev/null @@ -1,423 +0,0 @@ -#if NET471 -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Collections.Specialized; -using System.Drawing; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Media; -using AlphaTab.Model; -using AlphaTab.Rendering; -using AlphaTab.Util; - -namespace AlphaTab.Platform.CSharp.Wpf -{ - public class AlphaTab : Control - { - static AlphaTab() - { - DefaultStyleKeyProperty.OverrideMetadata(typeof(AlphaTab), new FrameworkPropertyMetadata(typeof(AlphaTab))); - } - - private bool _initialRenderCompleted; - private bool _redrawPending; - private int _isRendering; // interlocked bool - private ScrollViewer _scrollView; - private readonly ObservableCollection _images; - -#region Track - - public static readonly DependencyProperty TracksProperty = DependencyProperty.Register("Tracks", typeof(IEnumerable), typeof(AlphaTab), new PropertyMetadata(default(IEnumerable), OnTracksChanged)); - - private static void OnTracksChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var observable = e.OldValue as INotifyCollectionChanged; - if (observable != null) - { - ((AlphaTab)d).UnregisterObservableCollection(observable); - } - - observable = e.NewValue as INotifyCollectionChanged; - if (observable != null) - { - ((AlphaTab)d).RegisterObservableCollection(observable); - } - - ((AlphaTab)d).InvalidateTracks(true); - } - - private void RegisterObservableCollection(INotifyCollectionChanged collection) - { - collection.CollectionChanged += OnTracksChanged; - } - - private void UnregisterObservableCollection(INotifyCollectionChanged collection) - { - collection.CollectionChanged -= OnTracksChanged; - } - - private void OnTracksChanged(object sender, NotifyCollectionChangedEventArgs e) - { - InvalidateTracks(true); - } - - public IEnumerable Tracks - { - get { return (IEnumerable)GetValue(TracksProperty); } - set { SetValue(TracksProperty, value); } - } - -#endregion - -#region Scale - - public static readonly DependencyProperty ScaleProperty = DependencyProperty.Register("Scale", typeof(float), typeof(AlphaTab), new PropertyMetadata(1f, OnScaleChanged)); - private static void OnScaleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - ((AlphaTab)d).InvalidateTracks(true); - } - - public float Scale - { - get { return (float)GetValue(ScaleProperty); } - set { SetValue(ScaleProperty, value); } - } - -#endregion - -#region ScoreWidth - - public static readonly DependencyProperty ScoreWidthProperty = DependencyProperty.Register("ScoreWidth", typeof(int), typeof(AlphaTab), new PropertyMetadata(-1, OnScoreWidthChanged)); - - private static void OnScoreWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - ((AlphaTab)d).InvalidateTracks(true); - } - - public int ScoreWidth - { - get { return (int)GetValue(ScoreWidthProperty); } - set { SetValue(ScoreWidthProperty, value); } - } - -#endregion - -#region ScoreAutoSize - - public bool ScoreAutoSize - { - get { return ScoreWidth < 0; } - } - -#endregion - -#region LayoutMode - - public static readonly DependencyProperty LayoutModeProperty = DependencyProperty.Register("LayoutMode", typeof(string), typeof(AlphaTab), new PropertyMetadata("page", OnLayoutModeChanged)); - - private static void OnLayoutModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - ((AlphaTab)d).InvalidateTracks(true); - } - - public string LayoutMode - { - get { return (string)GetValue(LayoutModeProperty); } - set { SetValue(LayoutModeProperty, value); } - } - -#endregion - -#region StretchForce - - public static readonly DependencyProperty StretchForceProperty = DependencyProperty.Register("StretchForce", typeof(float), typeof(AlphaTab), new PropertyMetadata(1f, OnStretchForceChanged)); - - private static void OnStretchForceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - ((AlphaTab)d).InvalidateTracks(true); - } - - public float StretchForce - { - get { return (float)GetValue(StretchForceProperty); } - set { SetValue(StretchForceProperty, value); } - } - -#endregion - -#region StavesMode - - public static readonly DependencyProperty StavesModeProperty = DependencyProperty.Register("StavesMode", typeof(string), typeof(AlphaTab), new PropertyMetadata("score-tab", OnStavesModeChanged)); - - private static void OnStavesModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - ((AlphaTab)d).InvalidateTracks(true); - } - - public string StavesMode - { - get { return (string)GetValue(StavesModeProperty); } - set { SetValue(StavesModeProperty, value); } - } - -#endregion - -#region RenderPartials - - private static readonly DependencyPropertyKey RenderPartialsPropertyKey = DependencyProperty.RegisterReadOnly("RenderPartials", typeof(IEnumerable), typeof(AlphaTab), new PropertyMetadata(default(IEnumerable))); - public static readonly DependencyProperty RenderPartialsProperty = RenderPartialsPropertyKey.DependencyProperty; - - public IEnumerable RenderPartials - { - get { return (IEnumerable)GetValue(RenderPartialsProperty); } - private set { SetValue(RenderPartialsPropertyKey, value); } - } - -#endregion - -#region ActualScoreWidth - - private static readonly DependencyPropertyKey ActualScoreWidthPropertyKey = DependencyProperty.RegisterReadOnly("ActualScoreWidth", typeof(float), typeof(AlphaTab), new PropertyMetadata(default(float))); - public static readonly DependencyProperty ActualScoreWidthProperty = ActualScoreWidthPropertyKey.DependencyProperty; - - public float ActualScoreWidth - { - get { return (float)GetValue(ActualScoreWidthProperty); } - private set { SetValue(ActualScoreWidthPropertyKey, value); } - } - -#endregion - -#region ActualScoreHeight - - private static readonly DependencyPropertyKey ActualScoreHeightPropertyKey = DependencyProperty.RegisterReadOnly("ActualScoreHeight", typeof(float), typeof(AlphaTab), new PropertyMetadata(default(float))); - public static readonly DependencyProperty ActualScoreHeightProperty = ActualScoreWidthPropertyKey.DependencyProperty; - - public float ActualScoreHeight - { - get { return (float)GetValue(ActualScoreHeightProperty); } - private set { SetValue(ActualScoreHeightPropertyKey, value); } - } - -#endregion - -#region RenderEngine - - public static readonly DependencyProperty RenderEngineProperty = DependencyProperty.Register("RenderEngine", typeof(string), typeof(AlphaTab), new PropertyMetadata("gdi", OnRenderEngineChanged)); - - private static void OnRenderEngineChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - ((AlphaTab)d).InvalidateTracks(true); - } - - public string RenderEngine - { - get { return (string)GetValue(RenderEngineProperty); } - set { SetValue(RenderEngineProperty, value); } - } - - -#endregion - - private ScoreRenderer _renderer; - public IScoreRenderer Renderer => _renderer; - - public AlphaTab() - { - SnapsToDevicePixels = true; - - _images = new ObservableCollection(); - RenderPartials = _images; - - var settings = Settings.Defaults; - settings.Engine = "gdi"; - - _renderer = new ScoreRenderer(settings); - _renderer.PreRender += result => - { - Dispatcher.BeginInvoke(new Action(() => - { - _images.Clear(); - GC.Collect(); - AddPartialResult(result); - })); - }; - _renderer.PartialRenderFinished += result => - { - Dispatcher.BeginInvoke(new Action(() => - { - AddPartialResult(result); - })); - }; - _renderer.RenderFinished += result => - { - Dispatcher.BeginInvoke(new Action(() => - { - _initialRenderCompleted = true; - _isRendering = 0; - AddPartialResult(result); - OnRenderFinished(); - if (_redrawPending) - { - ResizeTracks(RenderWidth); - } - })); - }; - } - - public override void OnApplyTemplate() - { - base.OnApplyTemplate(); - _scrollView = (ScrollViewer)Template.FindName("PART_ScrollView", this); - _scrollView.ScrollChanged += OnScrollChanged; - InvalidateTracks(true); - } - - public void InvalidateTracks(bool force) - { - var trackArray = Tracks?.ToArray(); - if (trackArray == null || trackArray.Length == 0) return; - - var width = RenderWidth; - if (width > 0) - { - if (trackArray == _renderer.Tracks && !force) - { - return; - } - - var settings = _renderer.Settings; - settings.Width = width; - settings.Engine = RenderEngine; - settings.Scale = Scale; - settings.Layout.Mode = LayoutMode; - settings.StretchForce = StretchForce; - settings.Staves.Id = StavesMode; - _renderer.UpdateSettings(settings); - ModelUtils.ApplyPitchOffsets(settings, trackArray[0].Score); - - _initialRenderCompleted = false; - _isRendering = 1; - - Task.Factory.StartNew(() => - { - _renderer.Render(trackArray[0].Score, trackArray.Select(t => t.Index).ToArray()); - }); - } - else - { - _initialRenderCompleted = false; - _redrawPending = true; - _isRendering = 0; - } - } - - private int RenderWidth - { - get - { - return (int)(ScoreAutoSize ? _scrollView.ViewportWidth : ScoreWidth); - } - } - - private void OnScrollChanged(object sender, ScrollChangedEventArgs e) - { - if (Math.Abs(e.ViewportWidthChange) > 0 && ScoreAutoSize) - { - ResizeTracks(e.ViewportWidth); - } - } - - private void ResizeTracks(double width) - { - int newWidth = (int)width; - if (Interlocked.Exchange(ref _isRendering, 1) == 1) - { - _redrawPending = true; - } - else if (width > 0) - { - _redrawPending = false; - if (!_initialRenderCompleted) - { - InvalidateTracks(true); - } - else if (newWidth != _renderer.Settings.Width) - { - Task.Factory.StartNew(() => - { - _renderer.Resize(newWidth); - }); - } - else - { - _isRendering = 0; - } - } - } - - private void AddPartialResult(RenderFinishedEventArgs result) - { - ActualScoreWidth = result.TotalWidth; - ActualScoreHeight = result.TotalHeight; - if (result.RenderResult != null) - { - var bitmap = result.RenderResult as Bitmap; - if (bitmap != null) - { - using (bitmap) - { - _images.Add(GdiImageSource.Create(bitmap)); - } - } - else - { - using (result.RenderResult as IDisposable) - { - _images.Add(SkImageSource.Create(result.RenderResult)); - } - } - } - } - -#region RenderFinished - - public static readonly RoutedEvent RenderFinishedEvent = EventManager.RegisterRoutedEvent("RenderFinished", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(AlphaTab)); - public event RoutedEventHandler RenderFinished - { - add { AddHandler(RenderFinishedEvent, value); } - remove { RemoveHandler(RenderFinishedEvent, value); } - } - - protected virtual void OnRenderFinished() - { - RoutedEventArgs newEventArgs = new RoutedEventArgs(RenderFinishedEvent); - RaiseEvent(newEventArgs); - } - -#endregion - - } -} -#endif \ No newline at end of file diff --git a/Source/AlphaTab.CSharp/Platform/CSharp/Wpf/AlphaTabLayoutPanel.cs b/Source/AlphaTab.CSharp/Platform/CSharp/Wpf/AlphaTabLayoutPanel.cs deleted file mode 100644 index 57aaf4655..000000000 --- a/Source/AlphaTab.CSharp/Platform/CSharp/Wpf/AlphaTabLayoutPanel.cs +++ /dev/null @@ -1,69 +0,0 @@ -#if NET471 -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System.Windows; -using System.Windows.Controls; - -namespace AlphaTab.Platform.CSharp.Wpf -{ - public class AlphaTabLayoutPanel : Panel - { - protected override Size MeasureOverride(Size availableSize) - { - foreach (UIElement child in InternalChildren) - { - child.Measure(availableSize); - } - return new Size - { - Width = double.IsInfinity(availableSize.Width) ? MinWidth : availableSize.Width, - Height = double.IsInfinity(availableSize.Height) ? MinHeight : availableSize.Height - }; - } - - protected override Size ArrangeOverride(Size finalSize) - { - var xChild = 0.0; - var yChild = 0.0; - - var rowHeight = 0.0; - - foreach (UIElement child in InternalChildren) - { - child.Arrange(new Rect(xChild, yChild, child.DesiredSize.Width, child.DesiredSize.Height)); - - xChild += child.DesiredSize.Width; - if (child.DesiredSize.Height > rowHeight) - { - rowHeight = child.DesiredSize.Height; - } - - if (xChild >= finalSize.Width) - { - xChild = 0; - yChild += rowHeight; - rowHeight = 0; - } - } - - return finalSize; - } - } -} -#endif \ No newline at end of file diff --git a/Source/AlphaTab.CSharp/Platform/CSharp/Wpf/GdiImageSource.cs b/Source/AlphaTab.CSharp/Platform/CSharp/Wpf/GdiImageSource.cs deleted file mode 100644 index 71c30e543..000000000 --- a/Source/AlphaTab.CSharp/Platform/CSharp/Wpf/GdiImageSource.cs +++ /dev/null @@ -1,44 +0,0 @@ -#if NET471 -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System.Drawing; -using System.Drawing.Imaging; -using System.Windows.Media; -using System.Windows.Media.Imaging; - -namespace AlphaTab.Platform.CSharp.Wpf -{ - class GdiImageSource - { - public static BitmapSource Create(Bitmap image) - { - var bitmapData = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadOnly, - image.PixelFormat); - - var bitmapSource = BitmapSource.Create( - bitmapData.Width, bitmapData.Height, 96, 96, PixelFormats.Pbgra32, null, - bitmapData.Scan0, bitmapData.Stride * bitmapData.Height, bitmapData.Stride); - - image.UnlockBits(bitmapData); - - return bitmapSource; - } - } -} -#endif \ No newline at end of file diff --git a/Source/AlphaTab.CSharp/Platform/CSharp/Wpf/SkImageSource.cs b/Source/AlphaTab.CSharp/Platform/CSharp/Wpf/SkImageSource.cs deleted file mode 100644 index 06da26129..000000000 --- a/Source/AlphaTab.CSharp/Platform/CSharp/Wpf/SkImageSource.cs +++ /dev/null @@ -1,46 +0,0 @@ -#if NET471 -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System.Windows; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using SkiaSharp; - -namespace AlphaTab.Platform.CSharp.Wpf -{ - class SkImageSource - { - public static BitmapSource Create(object data) - { - var image = (SKImage) data; - var info = new SKImageInfo(image.Width, image.Height); - var bitmap = new WriteableBitmap(image.Width, image.Height, 96, 96, PixelFormats.Pbgra32, null); - bitmap.Lock(); - // copy - using (var pixmap = new SKPixmap(info, bitmap.BackBuffer, bitmap.BackBufferStride)) - { - image.ReadPixels(pixmap, 0, 0); - } - bitmap.AddDirtyRect(new Int32Rect(0, 0, info.Width, info.Height)); - bitmap.Unlock(); - return bitmap; - } - } -} -#endif \ No newline at end of file diff --git a/Source/AlphaTab.CSharp/Platform/CSharp/Wpf/Templates.xaml b/Source/AlphaTab.CSharp/Platform/CSharp/Wpf/Templates.xaml deleted file mode 100644 index 9138b79d8..000000000 --- a/Source/AlphaTab.CSharp/Platform/CSharp/Wpf/Templates.xaml +++ /dev/null @@ -1,35 +0,0 @@ - - - diff --git a/Source/AlphaTab.CSharp/Platform/CSharp/Xamarin/Android/AlphaTab.cs b/Source/AlphaTab.CSharp/Platform/CSharp/Xamarin/Android/AlphaTab.cs deleted file mode 100644 index e3704517f..000000000 --- a/Source/AlphaTab.CSharp/Platform/CSharp/Xamarin/Android/AlphaTab.cs +++ /dev/null @@ -1,242 +0,0 @@ -#if ANDROID -/* - * This file is part of alphaTab. - * Copyright © 2017, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using AlphaTab.Model; -using AlphaTab.Rendering; -using Android.App; -using Android.Bluetooth; -using Android.Content; -using Android.Graphics; -using Android.Util; -using Android.Widget; -using SkiaSharp; - -namespace AlphaTab.Platform.CSharp.Xamarin.Android -{ - public class AlphaTab : ScrollView - { - private AlphaTabLayoutPanel _contentPanel; - private bool _initialRenderCompleted; - private bool _isRendering; - private bool _redrawPending; - private float _displayDensity; - - public IEnumerable Tracks - { - get { return _tracks; } - set - { - if (_tracks == value) return; - _tracks = value; - InvalidateTracks(); - } - } - - private ScoreRenderer _renderer; - private IEnumerable _tracks; - - public AlphaTab(Context context) - : base(context) - { - Initialize(context); - } - - public AlphaTab(Context context, IAttributeSet attrs) - : base(context, attrs) - { - Initialize(context); - } - - private void Initialize(Context context) - { - using (var metrics = context.Resources.DisplayMetrics) - { - _displayDensity = metrics.Density; - } - - _contentPanel = new AlphaTabLayoutPanel(context); - AddView(_contentPanel); - - var settings = Settings.Defaults; - settings.Engine = "skia"; - settings.Width = 970; - settings.Scale = 0.8f; - settings.StretchForce = 0.8f; - - _renderer = new ScoreRenderer(settings); - _renderer.PreRender += result => - { - lock (this) - { - Post(() => - { - ClearPartialResults(); - AddPartialResult(result); - }); - } - }; - _renderer.PartialRenderFinished += result => - { - lock (this) - { - Post(() => - { - AddPartialResult(result); - }); - } - }; - _renderer.RenderFinished += result => - { - Post(() => - { - _initialRenderCompleted = true; - _isRendering = false; - AddPartialResult(result); - OnRenderFinished(result); - if (_redrawPending) - { - Resize((int)(Width / _displayDensity)); - } - }); - }; - } - - private void ClearPartialResults() - { - var childCount = _contentPanel.ChildCount; - while (childCount > 0) - { - var child = _contentPanel.GetChildAt(0); - var imageView = child as ImageView; - if (imageView != null) - { - var image = imageView.Drawable; - imageView.SetImageResource(0); - image.Dispose(); - imageView.DestroyDrawingCache(); - } - - _contentPanel.RemoveView(child); - child.Dispose(); - - childCount--; - } - _contentPanel.RemoveAllViews(); - } - - private void AddPartialResult(RenderFinishedEventArgs result) - { - lock (this) - { - _contentPanel.SetMinimumWidth((int)(result.TotalWidth * _displayDensity)); - _contentPanel.SetMinimumHeight((int)(result.TotalHeight * _displayDensity)); - - if (result.RenderResult != null) - { - using (var image = (SKImage)result.RenderResult) - { - byte[] imageBytes; - using (var data = image.Encode(SKEncodedImageFormat.Png, 100)) - { - imageBytes = data.ToArray(); - } - - var view = new ImageView(Context); - view.SetMinimumWidth((int)(result.Width * _displayDensity)); - view.SetMinimumHeight((int)(result.Height * _displayDensity)); - view.SetMaxWidth((int)(result.Width * _displayDensity)); - view.SetMaxHeight((int)(result.Width * _displayDensity)); - view.SetImageBitmap(BitmapFactory.DecodeByteArray(imageBytes, 0, imageBytes.Length)); - - _contentPanel.AddView(view); - } - } - } - } - - private void InvalidateTracks() - { - if (Tracks == null) return; - - if (Width > 0) - { - _renderer.Settings.Width = (int)(Width / _displayDensity); - - _initialRenderCompleted = false; - _isRendering = true; - var tracks = Tracks.ToArray(); - if (tracks.Length > 0) - { - ModelUtils.ApplyPitchOffsets(_renderer.Settings, tracks[0].Score); - Task.Factory.StartNew(() => - { - _renderer.Render(tracks[0].Score, tracks.Select(t => t.Index).ToArray()); - }); - } - - } - else - { - _initialRenderCompleted = false; - _redrawPending = true; - } - } - - protected override void OnSizeChanged(int w, int h, int oldw, int oldh) - { - Resize((int)(w / _displayDensity)); - base.OnSizeChanged(w, h, oldw, oldh); - } - - private void Resize(int width) - { - if (_isRendering) - { - _redrawPending = true; - } - else if (width > 0) - { - _redrawPending = false; - - if (!_initialRenderCompleted) - { - InvalidateTracks(); - } - else if (width != _renderer.Settings.Width) - { - _isRendering = true; - Task.Factory.StartNew(() => - { - _renderer.Resize(width); - }); - } - } - } - - public event EventHandler RenderFinished; - protected virtual void OnRenderFinished(RenderFinishedEventArgs e) - { - RenderFinished?.Invoke(this, e); - } - } -} -#endif \ No newline at end of file diff --git a/Source/AlphaTab.CSharp/Platform/CSharp/Xamarin/Android/AlphaTabLayoutPanel.cs b/Source/AlphaTab.CSharp/Platform/CSharp/Xamarin/Android/AlphaTabLayoutPanel.cs deleted file mode 100644 index 251ba9f55..000000000 --- a/Source/AlphaTab.CSharp/Platform/CSharp/Xamarin/Android/AlphaTabLayoutPanel.cs +++ /dev/null @@ -1,72 +0,0 @@ -#if ANDROID -/* - * This file is part of alphaTab. - * Copyright © 2017, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using Android.Content; -using Android.Views; - -namespace AlphaTab.Platform.CSharp.Xamarin.Android -{ - class AlphaTabLayoutPanel : ViewGroup - { - public AlphaTabLayoutPanel(Context context) : base(context) - { - } - - protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec) - { - var childCount = ChildCount; - for (int i = 0; i < childCount; i++) - { - var child = GetChildAt(i); - child.Measure(widthMeasureSpec, heightMeasureSpec); - } - - SetMeasuredDimension(MinimumWidth, MinimumHeight); - } - - protected override void OnLayout(bool changed, int l, int t, int r, int b) - { - var xChild = l; - var yChild = t; - - var rowHeight = 0; - var childCount = ChildCount; - - for (int i = 0; i < childCount; i++) - { - var child = GetChildAt(i); - child.Layout(xChild, yChild, xChild + child.MeasuredWidth, yChild + child.MeasuredHeight); - - xChild += child.MeasuredWidth; - if (child.MeasuredHeight > rowHeight) - { - rowHeight = child.MeasuredHeight; - } - - if (xChild >= r) - { - xChild = l; - yChild += rowHeight; - rowHeight = 0; - } - } - } - } -} -#endif \ No newline at end of file diff --git a/Source/AlphaTab.CSharp/Platform/CSharp/Xamarin/Forms/AlphaTab.cs b/Source/AlphaTab.CSharp/Platform/CSharp/Xamarin/Forms/AlphaTab.cs deleted file mode 100644 index d11baa19a..000000000 --- a/Source/AlphaTab.CSharp/Platform/CSharp/Xamarin/Forms/AlphaTab.cs +++ /dev/null @@ -1,198 +0,0 @@ -#if ANDROID -/* - * This file is part of alphaTab. - * Copyright © 2017, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using AlphaTab.Model; -using AlphaTab.Rendering; -using SkiaSharp; -using Xamarin.Forms; - -namespace AlphaTab.Platform.CSharp.Xamarin.Forms -{ - public class AlphaTab : ScrollView - { - private readonly AlphaTabLayoutPanel _contentPanel; - private bool _initialRenderCompleted; - private bool _isRendering; - private bool _redrawPending; - - public static readonly BindableProperty TracksProperty = BindableProperty.Create("Tracks", typeof(IEnumerable), typeof(AlphaTab), propertyChanged: OnTracksChanged); - private static void OnTracksChanged(BindableObject bindable, object oldvalue, object newvalue) - { - ((AlphaTab)bindable).InvalidateTracks(); - } - public IEnumerable Tracks - { - get { return (IEnumerable)GetValue(TracksProperty); } - set { SetValue(TracksProperty, value); } - } - - private readonly ScoreRenderer _renderer; - - public AlphaTab() - { - _contentPanel = new AlphaTabLayoutPanel(); - _contentPanel.HorizontalOptions = new LayoutOptions(LayoutAlignment.Start, true); - _contentPanel.VerticalOptions = new LayoutOptions(LayoutAlignment.Start, true); - - Orientation = ScrollOrientation.Both; - - Content = _contentPanel; - - var settings = Settings.Defaults; - settings.Engine = "skia"; - settings.Width = 970; - settings.Scale = 0.8f; - settings.StretchForce = 0.8f; - - _renderer = new ScoreRenderer(settings); - _renderer.PreRender += result => - { - lock (this) - { - Device.BeginInvokeOnMainThread(() => - { - ClearPartialResults(); - AddPartialResult(result); - }); - } - }; - _renderer.PartialRenderFinished += result => - { - lock (this) - { - Device.BeginInvokeOnMainThread(() => - { - AddPartialResult(result); - }); - } - }; - _renderer.RenderFinished += result => - { - Device.BeginInvokeOnMainThread(() => - { - _initialRenderCompleted = true; - _isRendering = false; - if (_redrawPending) - { - Resize((int)Width); - } - OnRenderFinished(result); - }); - }; - } - - private void ClearPartialResults() - { - _contentPanel.Children.Clear(); - } - - private void AddPartialResult(RenderFinishedEventArgs result) - { - lock (this) - { - _contentPanel.WidthRequest = result.TotalWidth; - _contentPanel.HeightRequest = result.TotalHeight; - - if (result.RenderResult != null) - { - using (var image = (SKImage)result.RenderResult) - { - _contentPanel.Children.Add(new Image - { - Source = new SkImageSource(image), - WidthRequest = result.Width, - HeightRequest = result.Height - }); - } - } - } - } - - private void InvalidateTracks() - { - if (Tracks == null) return; - - if (Width > 0) - { - _renderer.Settings.Width = (int)Width; - _initialRenderCompleted = false; - _isRendering = true; - var tracks = Tracks.ToArray(); - if (tracks.Length > 0) - { - ModelUtils.ApplyPitchOffsets(_renderer.Settings, tracks[0].Score); - Task.Factory.StartNew(() => - { - _renderer.Render(tracks[0].Score, tracks.Select(t => t.Index).ToArray()); - }); - } - } - else - { - _initialRenderCompleted = false; - _redrawPending = true; - } - } - - protected override void OnSizeAllocated(double width, double height) - { - Resize((int)width); - base.OnSizeAllocated(width, height); - } - - private void Resize(int width) - { - if (_isRendering) - { - _redrawPending = true; - } - else if (width > 0) - { - _redrawPending = false; - - if (!_initialRenderCompleted) - { - InvalidateTracks(); - } - else - { - if (width != _renderer.Settings.Width) - { - _renderer.Settings.Width = width; - _isRendering = true; - Task.Factory.StartNew(() => - { - _renderer.Resize(width); - }); - } - } - } - } - - public event EventHandler RenderFinished; - protected virtual void OnRenderFinished(RenderFinishedEventArgs e) - { - RenderFinished?.Invoke(this, e); - } - } -} -#endif \ No newline at end of file diff --git a/Source/AlphaTab.CSharp/Platform/CSharp/Xamarin/Forms/AlphaTabLayoutPanel.cs b/Source/AlphaTab.CSharp/Platform/CSharp/Xamarin/Forms/AlphaTabLayoutPanel.cs deleted file mode 100644 index 0e63a3906..000000000 --- a/Source/AlphaTab.CSharp/Platform/CSharp/Xamarin/Forms/AlphaTabLayoutPanel.cs +++ /dev/null @@ -1,61 +0,0 @@ -#if ANDROID -/* - * This file is part of alphaTab. - * Copyright © 2017, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using Xamarin.Forms; - -namespace AlphaTab.Platform.CSharp.Xamarin.Forms -{ - class AlphaTabLayoutPanel : Layout - { - protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint) - { - foreach (var child in Children) - { - child.Measure(double.PositiveInfinity, double.PositiveInfinity); - } - return new SizeRequest(new Size(WidthRequest, HeightRequest)); - } - - protected override void LayoutChildren(double x, double y, double width, double height) - { - var xChild = x; - var yChild = y; - - var rowHeight = 0.0; - - foreach (var child in Children) - { - LayoutChildIntoBoundingRegion(child, new Rectangle(new Point(xChild, yChild), new Size(child.WidthRequest, child.HeightRequest))); - - xChild += child.WidthRequest; - if (child.HeightRequest > rowHeight) - { - rowHeight = child.HeightRequest; - } - - if (xChild >= width) - { - xChild = x; - yChild += rowHeight; - rowHeight = 0; - } - } - } - } -} -#endif \ No newline at end of file diff --git a/Source/AlphaTab.CSharp/Platform/CSharp/Xamarin/Forms/SkImageSource.cs b/Source/AlphaTab.CSharp/Platform/CSharp/Xamarin/Forms/SkImageSource.cs deleted file mode 100644 index c08957026..000000000 --- a/Source/AlphaTab.CSharp/Platform/CSharp/Xamarin/Forms/SkImageSource.cs +++ /dev/null @@ -1,39 +0,0 @@ -#if ANDROID -/* - * This file is part of alphaTab. - * Copyright © 2017, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System.IO; -using System.Threading.Tasks; -using SkiaSharp; -using Xamarin.Forms; - -namespace AlphaTab.Platform.CSharp.Xamarin.Forms -{ - class SkImageSource : StreamImageSource - { - public SkImageSource(SKImage image) - { - byte[] imageBytes; - using (var data = image.Encode(SKEncodedImageFormat.Png, 100)) - { - imageBytes = data.ToArray(); - } - Stream = token => Task.FromResult((Stream)new MemoryStream(imageBytes)); - } - } -} -#endif \ No newline at end of file diff --git a/Source/AlphaTab.CSharp/Platform/Platform.cs b/Source/AlphaTab.CSharp/Platform/Platform.cs deleted file mode 100644 index c1603fc9d..000000000 --- a/Source/AlphaTab.CSharp/Platform/Platform.cs +++ /dev/null @@ -1,189 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Text; -using System.Text.RegularExpressions; -using AlphaTab.Audio.Synth; -using AlphaTab.IO; -using AlphaTab.Util; -using AlphaTab.Xml; - -namespace AlphaTab.Platform -{ - static partial class Platform - { - public static T As(this object s) - { - return (T)s; - } - - public static void Log(LogLevel logLevel, string category, string msg, object details = null) - { - Trace.WriteLine($"[AlphaTab][{category}][{logLevel}] {msg} {details}", "AlphaTab"); - } - - public static float ParseFloat(string s) - { - float f; - if (!Single.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out f)) - { - f = Single.NaN; - } - return f; - } - - public static float Log2(float s) - { - return (float)Math.Log(s, 2); - } - - public static int ParseInt(string s) - { - float f; - if (!float.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out f)) - { - return int.MinValue; - } - return (int)f; - } - - public static int[] CloneArray(int[] array) - { - return (int[])array.Clone(); - } - - public static void BlockCopy(byte[] src, int srcOffset, byte[] dst, int dstOffset, int count) - { - Buffer.BlockCopy(src, srcOffset, dst, dstOffset, count); - } - - public static bool IsNullOrWhiteSpace(this string s) - { - return String.IsNullOrWhiteSpace(s); - } - - public static string StringFromCharCode(int c) - { - return ((char)c).ToString(); - } - - public static void Foreach(IEnumerable e, Action c) - { - foreach (var t in e) - { - c(t); - } - } - - public static sbyte ReadSignedByte(this IReadable readable) - { - return unchecked((sbyte)(byte)readable.ReadByte()); - } - - public static string ToString(byte[] data, string encoding) - { - var detectedEncoding = DetectEncoding(data); - if (detectedEncoding != null) - { - encoding = detectedEncoding; - } - if (encoding == null) - { - encoding = "utf-8"; - } - - Encoding enc; - try - { - enc = Encoding.GetEncoding(encoding); - } - catch - { - enc = Encoding.UTF8; - } - return enc.GetString(data, 0, data.Length); - } - - public static bool InstanceOf(object value) - { - return value is T; - } - - public static string NewGuid() - { - return Guid.NewGuid().ToString(); - } - - public static bool IsException(Exception e) - { - return e is T; - } - - private static readonly Random Rnd = new Random(); - - public static int Random(int max) - { - return Rnd.Next(max); - } - - public static double RandomDouble() - { - return Rnd.NextDouble(); - } - - public static double ToDouble(byte[] bytes) - { - return BitConverter.ToDouble(bytes, 0); - } - public static float ToFloat(byte[] bytes) - { - return BitConverter.ToSingle(bytes, 0); - } - - public static void ClearIntArray(int[] array) - { - Array.Clear(array, 0, array.Length); - } - - public static void ClearShortArray(short[] array) - { - Array.Clear(array, 0, array.Length); - } - - public static void ArrayCopy(T[] src, int srcOffset, T[] dst, int dstOffset, int count) - { - Array.Copy(src, srcOffset, dst, dstOffset, count); - } - - public static void Reverse(byte[] array) - { - Array.Reverse(array); - } - - public static long GetCurrentMilliseconds() - { - return Stopwatch.GetTimestamp(); - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab.CSharp/Properties/AssemblyInfo.cs b/Source/AlphaTab.CSharp/Properties/AssemblyInfo.cs deleted file mode 100644 index 6e2908b83..000000000 --- a/Source/AlphaTab.CSharp/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("AlphaTab.Test.CSharp")] \ No newline at end of file diff --git a/Source/AlphaTab.CSharp/Settings.cs b/Source/AlphaTab.CSharp/Settings.cs deleted file mode 100644 index 8a0903dc7..000000000 --- a/Source/AlphaTab.CSharp/Settings.cs +++ /dev/null @@ -1,29 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab -{ - /// - /// This public class contains instance specific settings for alphaTab - /// - public partial class Settings - { - private static void SetDefaults(Settings settings) - { - } - } -} diff --git a/Source/AlphaTab.CSharp/Utils/UnionData.cs b/Source/AlphaTab.CSharp/Utils/UnionData.cs deleted file mode 100644 index 8a978f577..000000000 --- a/Source/AlphaTab.CSharp/Utils/UnionData.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Runtime.InteropServices; - -namespace AlphaTab.Utils -{ - [StructLayout(LayoutKind.Explicit)] - struct UnionData - { - //double values - [FieldOffset(0)] - public double Double1; - //float values - [FieldOffset(0)] - public float Float1; - [FieldOffset(4)] - public float Float2; - //int values - [FieldOffset(0)] - public int Int1; - [FieldOffset(4)] - public int Int2; - } -} diff --git a/Source/AlphaTab.JavaScript/AlphaTab.JavaScript.csproj b/Source/AlphaTab.JavaScript/AlphaTab.JavaScript.csproj deleted file mode 100644 index 65a463c25..000000000 --- a/Source/AlphaTab.JavaScript/AlphaTab.JavaScript.csproj +++ /dev/null @@ -1,24 +0,0 @@ - - - AlphaTab.JavaScript - AlphaTab - AlphaTab.JavaScript - net471 - $(NoWarn);0626;0824 - - - - - - - - - ..\..\Phase\Compiler\Phase.Core.dll - - - - - - - - \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Collections/FastDictionary.cs b/Source/AlphaTab.JavaScript/Collections/FastDictionary.cs deleted file mode 100644 index 8f19f5c60..000000000 --- a/Source/AlphaTab.JavaScript/Collections/FastDictionary.cs +++ /dev/null @@ -1,61 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System.Collections; -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using Phase; -using Phase.Attributes; - -namespace AlphaTab.Collections -{ - [Abstract("Dynamic")] - [ForeachMode(ForeachMode.GetEnumerator)] - public class FastDictionary : IEnumerable - { - [Inline] - public FastDictionary() => Script.AbstractThis = Platform.Platform.NewObject(); - - public TValue this[TKey index] - { - [Inline] - get => Script.Write("untyped this[index]"); - [Inline] - set => Script.Write("return untyped this[index] = value"); - } - - public int Count - { - [Inline] - get => Platform.Platform.JsonKeys(Script.AbstractThis).Length; - } - - [Inline] - public IEnumerator GetEnumerator() => Platform.Platform.JsonKeys(Script.AbstractThis).As>(); - [Inline] - public void Remove(TKey key) => Script.Write("untyped __js__(\"delete {0}[{1}]\", this, key)"); - [Inline] - public bool ContainsKey(TKey key) => Script.Write("untyped this.hasOwnProperty(key)"); - - [External] - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } -} diff --git a/Source/AlphaTab.JavaScript/Collections/FastList.cs b/Source/AlphaTab.JavaScript/Collections/FastList.cs deleted file mode 100644 index 1b6d59f1c..000000000 --- a/Source/AlphaTab.JavaScript/Collections/FastList.cs +++ /dev/null @@ -1,93 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using Haxe; -using Phase; -using Phase.Attributes; - -namespace AlphaTab.Collections -{ - [Abstract("Array", "Array", "Array")] - [ForeachMode(ForeachMode.GetEnumerator)] - public class FastList : IEnumerable - { - [Inline] - public FastList() => Script.AbstractThis = new HaxeArray(); - - public int Count - { - [Inline] - get => Script.This>().Length; - } - - public T this[int index] - { - [Inline] - get => Script.This>()[index]; - [Inline] - set => Script.This>()[index] = value; - } - - [Inline] - public void Add(T item) => Script.This>().Push(item); - - - [Inline] - public void Sort(Comparison comparison) => Script.This>().Sort((a, b) => comparison(a, b)); - - [Inline] - public FastList Clone() => Script.This>().Slice(0).As>(); - - [Inline] - public void RemoveAt(int index) - { - if (index != -1) - { - Script.This>().Splice(index, 1); - } - } - - [Inline] - public T[] ToArray() => FixedArray.FromArray(Script.This>()).As(); - - [Inline] - public IEnumerator GetEnumerator() => Script.AbstractThis.As>(); - - [Inline] - public int IndexOf(T item) => Script.This>().IndexOf(item); - - [Inline] - public void Reverse() => Script.This>().Reverse(); - - [Inline] - public IEnumerable ToEnumerable() => new IterableEnumerable(this); - - [Inline] - public void InsertAt(int insertPos, T item) => Script.This>().Insert(insertPos, item); - - [External] - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } -} diff --git a/Source/AlphaTab.JavaScript/Collections/SampleArray.cs b/Source/AlphaTab.JavaScript/Collections/SampleArray.cs deleted file mode 100644 index 797d3d090..000000000 --- a/Source/AlphaTab.JavaScript/Collections/SampleArray.cs +++ /dev/null @@ -1,65 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using Haxe.Js.Html; -using Phase; -using Phase.Attributes; - -namespace AlphaTab.Audio.Synth.Ds -{ - [Abstract("js.html.Float32Array")] - [NativeConstructors] - public class SampleArray - { - [Inline] - public SampleArray(int length) => Script.AbstractThis = new Float32Array(length); - - public Float32Array ToFloat32Array() => Script.AbstractThis.As(); - - public float this[int index] - { - [Inline] - get { return Script.This()[index]; } - [Inline] - set - { - Script.This()[index] = value; - } - } - - public int Length - { - [Inline] - get - { - return Script.This().Length; - } - } - - [Inline] - public void Clear() - { - Script.AbstractThis = new Float32Array(Length); - } - - [Inline] - public static void Blit(SampleArray src, int srcPos, SampleArray dest, int destPos, int len) - { - dest.ToFloat32Array().Set(src.ToFloat32Array().SubArray(srcPos, srcPos + len), destPos); - } - } -} diff --git a/Source/AlphaTab.JavaScript/Collections/StringBuilder.cs b/Source/AlphaTab.JavaScript/Collections/StringBuilder.cs deleted file mode 100644 index f9ce6403d..000000000 --- a/Source/AlphaTab.JavaScript/Collections/StringBuilder.cs +++ /dev/null @@ -1,57 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using Phase; -using Phase.Attributes; - -namespace AlphaTab.Collections -{ - [Abstract("String")] - [NativeConstructors] - class StringBuilder - { - [Inline] - public StringBuilder() - { - Script.Write("this = \"\";"); - } - - [Inline] - public void Append(object s) - { - Script.Write("this += Std.string(s);"); - } - - [Inline] - public void AppendChar(int i) - { - Script.Write("this += String.fromCharCode(i.ToHaxeInt());"); - } - - [Inline] - public void AppendLine(string s = "") - { - Script.Write("this += s + \"\\r\\n\";"); - } - - [Inline] - public override string ToString() - { - return Script.Write("this"); - } - } -} diff --git a/Source/AlphaTab.JavaScript/Environment.cs b/Source/AlphaTab.JavaScript/Environment.cs deleted file mode 100644 index 7f8f97459..000000000 --- a/Source/AlphaTab.JavaScript/Environment.cs +++ /dev/null @@ -1,293 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using AlphaTab.Collections; -using AlphaTab.Haxe.Js; -using AlphaTab.Haxe.Js.Html; -using AlphaTab.Platform; -using AlphaTab.Platform.JavaScript; -using AlphaTab.Platform.Svg; -using AlphaTab.Rendering.Glyphs; -using AlphaTab.Util; -using Haxe; -using Haxe.Js; -using Phase; -using Phase.Attributes; -using Phase.CompilerServices; -using StringBuilder = AlphaTab.Collections.StringBuilder; - -namespace AlphaTab -{ - /// - /// This public class represents the global alphaTab environment where - /// alphaTab looks for information like available layout engines - /// staves etc. - /// - partial class Environment - { - public static string ScriptFile { get; set; } - public static bool IsFontLoaded { get; set; } - - static void PlatformInit() - { - RenderEngines["svg"] = () => new CssFontSvgCanvas(); - RenderEngines["default"] = () => new CssFontSvgCanvas(); - RenderEngines["html5"] = () => new Platform.JavaScript.Html5Canvas(); - - RegisterJQueryPlugin(); - - Script.Write("untyped __js__(\"Math.log2 = Math.log2 || function(x) { return Math.log(x) * Math.LOG2E; };\");"); - - // try to build the find the alphaTab script url in case we are not in the webworker already - if (Lib.Global.document) - { - Script.Write("untyped __js__(\"window.AudioContext = window.AudioContext || window.webkitAudioContext;\");"); - - var document = Browser.Document; - /** - * VB Loader For IE - * This code is based on the code of - * http://nagoon97.com/reading-binary-files-using-ajax/ - * Copyright (c) 2008 Andy G.P. Na - * The source code is freely distributable under the terms of an MIT-style license. - */ - - var vbAjaxLoader = new StringBuilder(); - vbAjaxLoader.AppendLine("Function VbAjaxLoader(method, fileName)"); - vbAjaxLoader.AppendLine(" Dim xhr"); - vbAjaxLoader.AppendLine(" Set xhr = CreateObject(\"Microsoft.XMLHTTP\")"); - vbAjaxLoader.AppendLine(" xhr.Open method, fileName, False"); - vbAjaxLoader.AppendLine(" xhr.setRequestHeader \"Accept-Charset\", \"x-user-defined\""); - vbAjaxLoader.AppendLine(" xhr.send"); - vbAjaxLoader.AppendLine(" Dim byteArray()"); - vbAjaxLoader.AppendLine(" if xhr.Status = 200 Then"); - vbAjaxLoader.AppendLine(" Dim byteString"); - vbAjaxLoader.AppendLine(" Dim i"); - vbAjaxLoader.AppendLine(" byteString=xhr.responseBody"); - vbAjaxLoader.AppendLine(" ReDim byteArray(LenB(byteString))"); - vbAjaxLoader.AppendLine(" For i = 1 To LenB(byteString)"); - vbAjaxLoader.AppendLine(" byteArray(i-1) = AscB(MidB(byteString, i, 1))"); - vbAjaxLoader.AppendLine(" Next"); - vbAjaxLoader.AppendLine(" End If"); - vbAjaxLoader.AppendLine(" VbAjaxLoader=byteArray"); - vbAjaxLoader.AppendLine("End Function"); - - - var vbAjaxLoaderScript = (ScriptElement)document.CreateElement("script"); - vbAjaxLoaderScript.SetAttribute("type", "text/vbscript"); - var inlineScript = document.CreateTextNode(vbAjaxLoader.ToString()); - vbAjaxLoaderScript.AppendChild(inlineScript); - document.AddEventListener("DOMContentLoaded", new Action(() => - { - document.Body.AppendChild(vbAjaxLoaderScript); - }), false); - - ScriptElement scriptElement = (ScriptElement)document.CurrentScript; - if (!scriptElement.IsTruthy()) - { - // try to get javascript from exception stack - try - { - var error = new Error(); - var stack = error.Stack; - if (!stack.IsTruthy()) - { - throw error; - } - ScriptFile = ScriptFileFromStack(stack); - } - catch (Error e) - { - var stack = e.Stack; - if (!stack.IsTruthy()) - { - scriptElement = (ScriptElement)document.QuerySelector("script[data-alphatab]"); - } - else - { - ScriptFile = ScriptFileFromStack(stack); - } - } - } - - // failed to automatically resolve - if (string.IsNullOrEmpty(ScriptFile)) - { - if (!scriptElement.IsTruthy()) - { - Logger.Warning("Environment", "Could not automatically find alphaTab script file for worker, please add the data-alphatab attribute to the script tag that includes alphaTab or provide it when initializing alphaTab"); - } - else - { - ScriptFile = scriptElement.Src; - } - } - - CheckForFontAvailability(); - } - else - { - var isWebWorker = Script.Write("untyped __js__(\"typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope\")"); - if (isWebWorker) - { - AlphaTabWebWorker.Init(); - AlphaSynthWebWorker.Init(); - } - } - } - - private static void RegisterJQueryPlugin() - { - if (Platform.Platform.JsonExists(Lib.Global, "jQuery")) - { - dynamic jquery = Browser.Window.Member("jQuery"); - - - var api = new JQueryAlphaTab(); - jquery.fn.alphaTab = (Func)(method => - { - var _this = Script.Write>("untyped __js__(\"this\")"); - // if only a single element is affected, we use this - if (_this.Length == 1) - { - return api.Exec(_this[0], method, Script.Write("untyped __js__(\"Array.prototype.slice.call(arguments, 1)\")")); - } - // if multiple elements are affected we provide chaining - else - { - return Script.Write("untyped __js__(\"this\")") - .each((Action)(() => - { - api.Exec(Script.Write("untyped __js__(\"this\")"), method, - Script.Write("untyped __js__(\"Array.prototype.slice.call(arguments, 1)\")")); - })); - } - }); - jquery.alphaTab = new - { - restore = JQueryAlphaTab.Restore - }; - jquery.fn.alphaTab.fn = api; - } - - - } - - // based on https://github.com/JamesMGreene/currentExecutingScript - private static string ScriptFileFromStack(string stack) - { - var matches = stack.Match(@"(data:text\/javascript(?:;[^,]+)?,.+?|(?:|blob:)(?:http[s]?|file):\/\/[\/]?.+?\/[^:\)]*?)(?::\d+)(?::\d+)?"); - if (!matches.IsTruthy()) - { - matches = stack.Match(@"^(?:|[^:@]*@|.+\)@(?=data:text\/javascript|blob|http[s]?|file)|.+?\s+(?: at |@)(?:[^:\(]+ )*[\(]?)(data:text\/javascript(?:;[^,]+)?,.+?|(?:|blob:)(?:http[s]?|file):\/\/[\/]?.+?\/[^:\)]*?)(?::\d+)(?::\d+)?"); - if (!matches.IsTruthy()) - { - matches = stack.Match(@"\)@(data:text\/javascript(?:;[^,]+)?,.+?|(?:|blob:)(?:http[s]?|file):\/\/[\/]?.+?\/[^:\)]*?)(?::\d+)(?::\d+)?"); - if (!matches.IsTruthy()) - { - return null; - } - } - } - return matches[1]; - } - - public static void CheckForFontAvailability() - { - var isWorker = Script.Write("untyped __js__(\"typeof(WorkerGlobalScope) !== 'undefined' && self instanceof WorkerGlobalScope\")"); - if (isWorker) - { - // no web fonts in web worker - IsFontLoaded = false; - return; - } - - var cssFontLoadingModuleSupported = Browser.Document.Fonts.IsTruthy() && Browser.Document.Fonts.Member("load").IsTruthy(); - if (cssFontLoadingModuleSupported) - { - Action checkFont = null; - checkFont = () => - { - Browser.Document.Fonts.Load("1em alphaTab").Then(_ => - { - if (Browser.Document.Fonts.Check("1em alphaTab")) - { - Logger.Info("Rendering", "Font available"); - IsFontLoaded = true; - } - else - { - Browser.Window.SetTimeout((Action)(() => - { - checkFont(); - }), 250); - - } - return true; - }); - }; - checkFont(); - } - else - { - Action checkFont = null; - checkFont = () => - { - var document = Browser.Document; - var testItem = document.GetElementById("alphaTabFontChecker"); - - if (testItem == null) - { - // create a hidden element with the font style set - testItem = document.CreateElement("div"); - testItem.SetAttribute("id", "alphaTabFontChecker"); - testItem.Style.Opacity = "0"; - testItem.Style.Position = "absolute"; - testItem.Style.Left = "0"; - testItem.Style.Top = "0"; - testItem.Style.FontSize = "100px"; - testItem.ClassList.Add("at"); - testItem.InnerHTML = "&#" + (int)MusicFontSymbol.ClefG + ";"; - - document.Body.AppendChild(testItem); - } - - // get width - var width = testItem.OffsetWidth; - if (width > 30 && width < 100) - { - IsFontLoaded = true; - document.Body.RemoveChild(testItem); - } - else - { - Browser.Window.SetTimeout((Action)(() => - { - checkFont(); - }), 250); - } - }; - Browser.Window.AddEventListener("DOMContentLoaded", (Action)(() => - { - checkFont(); - })); - } - } - } -} diff --git a/Source/AlphaTab.JavaScript/Haxe/IO/HaxeBytes.cs b/Source/AlphaTab.JavaScript/Haxe/IO/HaxeBytes.cs deleted file mode 100644 index b1c790b49..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/IO/HaxeBytes.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Haxe.Js.Html; -using Phase.Attributes; - -namespace Haxe.IO -{ - [External] - [Name("haxe.io.Bytes")] - [NativeConstructors] - public class HaxeBytes - { - [Name("getData")] - public extern ArrayBuffer GetData(); - - [Name("alloc")] - public static extern HaxeBytes Alloc(HaxeInt size); - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/IO/HaxeBytesBuffer.cs b/Source/AlphaTab.JavaScript/Haxe/IO/HaxeBytesBuffer.cs deleted file mode 100644 index 7c5fe727d..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/IO/HaxeBytesBuffer.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Haxe; -using Phase.Attributes; - -namespace Haxe.IO -{ - [External] - [Name("haxe.io.BytesBuffer")] - [NativeConstructors] - public class HaxeBytesBuffer - { - [Name("addBytes")] - public extern void AddBytes(HaxeBytes src, HaxeInt pos, HaxeInt len); - - [Name("getBytes")] - public extern HaxeBytes GetBytes(); - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/IO/HaxeInput.cs b/Source/AlphaTab.JavaScript/Haxe/IO/HaxeInput.cs deleted file mode 100644 index 811f78155..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/IO/HaxeInput.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Phase.Attributes; - -namespace Haxe.IO -{ - [External] - [Name("haxe.io.Input")] - [NoConstructor] - public abstract class HaxeInput - { - [Name("readByte")] - public abstract HaxeInt ReadByte(); - - [Name("readBytes")] - public virtual extern HaxeInt ReadBytes(HaxeBytes s, HaxeInt pos, HaxeInt len); - - [Name("close")] - public virtual extern void Close(); - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/JSON.cs b/Source/AlphaTab.JavaScript/Haxe/JSON.cs deleted file mode 100644 index e82ec1bd5..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/JSON.cs +++ /dev/null @@ -1,36 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using Haxe; -using Phase.Attributes; - -namespace AlphaTab.Haxe -{ - [Name("haxe.Json")] - [External] - public class Json - { - [Name("parse")] - public static extern dynamic Parse(HaxeString text); - [Name("stringify")] - public static extern HaxeString Stringify(object value); - [Name("stringify")] - public static extern HaxeString Stringify(object value, Func replacer); - } -} diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Browser.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Browser.cs deleted file mode 100644 index 50232d320..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Browser.cs +++ /dev/null @@ -1,34 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Haxe.Js.Html; -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js -{ - [Name("js.Browser")] - [External] - public class Browser - { - [Name("document")] - public static extern HTMLDocument Document { get; } - [Name("window")] - public static extern Window Window { get; } - [Name("console")] - public static extern Console Console { get; } - } -} diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Error.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Error.cs deleted file mode 100644 index 8041ca1c2..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Error.cs +++ /dev/null @@ -1,35 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using Haxe; -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js -{ - [NativeConstructors] - [External] - [Name("js.Error")] - public class Error : Exception - { - [Name("stack")] - public extern HaxeString Stack { get;} - - public extern Error(); - public extern Error(string message); - } -} diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/AnchorElement.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/AnchorElement.cs deleted file mode 100644 index 20cc77403..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/AnchorElement.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Haxe; -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html -{ - [Name("js.html.AnchorElement")] - [External] - public class AnchorElement : Element - { - [Name("href")] - public extern HaxeString Href { get; set; } - [Name("download")] - public extern HaxeString Download { get; set; } - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/ArrayBuffer.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/ArrayBuffer.cs deleted file mode 100644 index 55862e28d..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/ArrayBuffer.cs +++ /dev/null @@ -1,40 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using Phase.Attributes; - -namespace Haxe.Js.Html -{ - [External] - [Name("js.html.ArrayBuffer")] - public class ArrayBuffer - { - [Name("isView")] - public static extern HaxeBool IsView(object value); - - [Name("byteLength")] - public HaxeInt ByteLength { get; } - - public extern ArrayBuffer(HaxeInt length); - - [Name("slice")] - public extern ArrayBuffer Slice(HaxeInt begin); - - [Name("slice")] - public extern ArrayBuffer Slice(HaxeInt begin, HaxeInt end); - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/ArrayBufferView.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/ArrayBufferView.cs deleted file mode 100644 index 99a7cdeb5..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/ArrayBufferView.cs +++ /dev/null @@ -1,35 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using Phase.Attributes; - -namespace Haxe.Js.Html -{ - [External] - [Name("haxe.js.ArrayBufferView")] - public class ArrayBufferView - { - [Name("buffer")] - public extern ArrayBuffer Buffer { get; } - - [Name("byteOffset")] - public extern HaxeInt ByteOffset { get; } - - [Name("byteLength")] - public extern HaxeInt ByteLength { get; } - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/Attr.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/Attr.cs deleted file mode 100644 index 93076c46d..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/Attr.cs +++ /dev/null @@ -1,32 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using Haxe; -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html -{ - [External] - [Name("js.html.Attr")] - public class Attr : Node - { - [Name("name")] - public extern HaxeString Name { get; } - [Name("value")] - public extern HaxeString Value { get; } - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/Audio/AudioBuffer.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/Audio/AudioBuffer.cs deleted file mode 100644 index 99d4cb519..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/Audio/AudioBuffer.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Haxe.Js.Html; -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html.Audio -{ - [External] - [Name("js.html.audio.AudioBuffer")] - [NativeConstructors] - public class AudioBuffer - { - [Name("getChannelData")] - public extern Float32Array GetChannelData(int channel); - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/Audio/AudioBufferSourceNode.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/Audio/AudioBufferSourceNode.cs deleted file mode 100644 index 9dd06d160..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/Audio/AudioBufferSourceNode.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Haxe; -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html.Audio -{ - [External] - [Name("js.html.audio.AudioBufferSourceNode")] - [NativeConstructors] - public class AudioBufferSourceNode : AudioNode - { - [Name("buffer")] - public AudioBuffer Buffer { get; set; } - [Name("loop")] - public HaxeBool Loop { get; set; } - - [Name("start")] - public extern void Start(HaxeFloat when); - [Name("stop")] - public extern void Stop(HaxeFloat when); - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/Audio/AudioContext.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/Audio/AudioContext.cs deleted file mode 100644 index 590a45a97..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/Audio/AudioContext.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Haxe; -using Haxe.Js.Html; -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html.Audio -{ - [External] - [Name("js.html.audio.AudioContext")] - [NativeConstructors] - public class AudioContext : EventTarget - { - [Name("sampleRate")] - public extern HaxeFloat SampleRate { get; set; } - - [Name("destination")] - public AudioNode Destination { get; } - - [Name("createBuffer")] - public extern AudioBuffer CreateBuffer(HaxeInt numberOfChannels, HaxeInt length, HaxeFloat sampleRate); - - [Name("createScriptProcessor")] - public extern ScriptProcessorNode CreateScriptProcessor(HaxeInt bufferSize, HaxeInt numberOfInputChannels, - HaxeInt numberOfOutputChannels); - - [Name("createBufferSource")] - public extern AudioBufferSourceNode CreateBufferSource(); - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/Audio/AudioNode.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/Audio/AudioNode.cs deleted file mode 100644 index 800f65f25..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/Audio/AudioNode.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Haxe; -using Haxe.Js.Html; -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html.Audio -{ - [External] - [Name("js.html.audio.AudioNode")] - [NativeConstructors] - public class AudioNode : EventTarget - { - [Name("connect")] - public extern void Connect(AudioNode destination); - [Name("connect")] - public extern void Connect(AudioNode destination, HaxeInt output, HaxeInt input); - [Name("disconnect")] - public extern void Disconnect(HaxeInt output); - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/Audio/AudioProcessingEvent.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/Audio/AudioProcessingEvent.cs deleted file mode 100644 index 20a59b44d..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/Audio/AudioProcessingEvent.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Haxe; -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html.Audio -{ - [External] - [Name("js.html.audio.AudioProcessingEvent")] - [NativeConstructors] - public class AudioProcessingEvent : Event - { - private extern AudioProcessingEvent(HaxeString type, dynamic eventInitDict); - - [Name("inputBuffer")] - public AudioBuffer InputBuffer { get; } - - [Name("outputBuffer")] - public AudioBuffer OutputBuffer { get; } - - [Name("playbackTime")] - public float PlaybackTime { get; } - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/Audio/ScriptProcessorNode.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/Audio/ScriptProcessorNode.cs deleted file mode 100644 index 6dcea996e..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/Audio/ScriptProcessorNode.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html.Audio -{ - [External] - [Name("js.html.audio.ScriptProcessorNode")] - [NativeConstructors] - public class ScriptProcessorNode : AudioNode - { - [Name("onaudioprocess")] - public extern Delegate OnAudioProcess { get; set; } - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/Blob.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/Blob.cs deleted file mode 100644 index 926d89087..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/Blob.cs +++ /dev/null @@ -1,31 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using Haxe; -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html -{ - [Name("js.html.Blob")] - [External] - [NativeConstructors] - public class Blob - { - public extern Blob(object worker); - public extern Blob(object worker, object properties); - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/BodyElement.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/BodyElement.cs deleted file mode 100644 index 7cf545a5c..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/BodyElement.cs +++ /dev/null @@ -1,28 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html -{ - [External] - [Name("js.html.BodyElement")] - [CastMode(CastMode.UnsafeCast)] - public class BodyElement : Element - { - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/CSSStyleDeclaration.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/CSSStyleDeclaration.cs deleted file mode 100644 index d1c122f01..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/CSSStyleDeclaration.cs +++ /dev/null @@ -1,58 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using Haxe; -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html -{ - [External] - [Name("js.html.CSSStyleDeclaration")] - public class CSSStyleDeclaration - { - [Name("opacity")] - public extern HaxeString Opacity { get; set; } - [Name("position")] - public extern HaxeString Position { get; set; } - [Name("left")] - public extern HaxeString Left { get; set; } - [Name("top")] - public extern HaxeString Top { get; set; } - [Name("fontSize")] - public extern HaxeString FontSize { get; set; } - [Name("fontFamily")] - public extern HaxeString FontFamily { get; set; } - [Name("width")] - public HaxeString Width { get; set; } - [Name("height")] - public HaxeString Height { get; set; } - [Name("overflow")] - public HaxeString Overflow { get; set; } - [Name("lineHeight")] - public HaxeString LineHeight { get; set; } - [Name("display")] - public HaxeString Display { get; set; } - [Name("textAlign")] - public HaxeString TextAlign { get; set; } - [Name("zIndex")] - public HaxeString ZIndex { get; set; } - [Name("transition")] - public HaxeString Transition { get; set; } - [Name("transitionDuration")] - public HaxeString TransitionDuration { get; set; } - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/CanvasElement.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/CanvasElement.cs deleted file mode 100644 index 78d9d5d4a..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/CanvasElement.cs +++ /dev/null @@ -1,34 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using Haxe; -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html -{ - [Name("js.html.CanvasElement")] - [External] - public class CanvasElement : Element - { - [Name("width")] - public extern HaxeInt Width { get; set; } - [Name("height")] - public extern HaxeInt Height { get; set; } - [Name("getContext")] - public extern object GetContext(HaxeString contextId); - } -} diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/CanvasRenderingContext2D.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/CanvasRenderingContext2D.cs deleted file mode 100644 index 448219a7c..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/CanvasRenderingContext2D.cs +++ /dev/null @@ -1,77 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using Haxe; -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html -{ - [External] - [Name("js.html.CanvasRenderingContext2D")] - public class CanvasRenderingContext2D - { - [Name("textBaseline")] - public extern HaxeString TextBaseline { get; set; } - [Name("strokeStyle")] - public extern HaxeString StrokeStyle { get; set; } - [Name("fillStyle")] - public extern HaxeString FillStyle { get; set; } - [Name("lineWidth")] - public HaxeFloat LineWidth { get; set; } - [Name("font")] - public extern HaxeString Font { get; set; } - [Name("textAlign")] - public extern HaxeString TextAlign { get; set; } - - [Name("fillRect")] - public extern void FillRect(HaxeFloat x, HaxeFloat y, HaxeFloat w, HaxeFloat h); - [Name("strokeRect")] - public extern void StrokeRect(HaxeFloat x, HaxeFloat y, HaxeFloat w, HaxeFloat h); - [Name("beginPath")] - public extern void BeginPath(); - [Name("closePath")] - public extern void ClosePath(); - [Name("moveTo")] - public extern void MoveTo(HaxeFloat x, HaxeFloat y); - [Name("lineTo")] - public extern void LineTo(HaxeFloat x, HaxeFloat y); - [Name("quadraticCurveTo")] - public extern void QuadraticCurveTo(HaxeFloat cpx, HaxeFloat cpy, HaxeFloat x, HaxeFloat y); - [Name("bezierCurveTo")] - public extern void BezierCurveTo(HaxeFloat cp1x, HaxeFloat cp1y, HaxeFloat cp2x, HaxeFloat cp2y, HaxeFloat x, HaxeFloat y); - [Name("arc")] - public extern void Arc(HaxeFloat x, HaxeFloat y, HaxeFloat radius, HaxeFloat startAngle, HaxeFloat endAngle); - [Name("arc")] - public extern void Arc(HaxeFloat x, HaxeFloat y, HaxeFloat radius, HaxeFloat startAngle, HaxeFloat endAngle, HaxeBool anticlockwise); - [Name("fill")] - public extern void Fill(); - [Name("stroke")] - public extern void Stroke(); - [Name("fillText")] - public extern void FillText(HaxeString text, HaxeFloat x, HaxeFloat y); - [Name("measureText")] - public extern TextMetrics MeasureText(HaxeString text); - [Name("save")] - public extern void Save(); - [Name("restore")] - public extern void Restore(); - [Name("translate")] - public extern void Translate(HaxeFloat x, HaxeFloat y); - [Name("rotate")] - public extern void Rotate(HaxeFloat angle); - } -} diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/Console.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/Console.cs deleted file mode 100644 index bf3b19889..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/Console.cs +++ /dev/null @@ -1,39 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html -{ - [External] - [Name("js.html.Console")] - public class Console - { - [Name("debug")] - [RawParams] - public extern void Debug(params object[] data); - [Name("info")] - [RawParams] - public extern void Info(params object[] data); - [Name("warn")] - [RawParams] - public extern void Warn(params object[] data); - [Name("error")] - [RawParams] - public extern void Error(params object[] data); - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/DOMElement.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/DOMElement.cs deleted file mode 100644 index 1c46095e6..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/DOMElement.cs +++ /dev/null @@ -1,88 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using Haxe; -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html -{ - [External] - [Name("js.html.DOMElement")] - public class DOMElement : Node - { - [Name("id")] - public HaxeString Id { get; set; } - - [Name("childElementCount")] - public extern HaxeInt ChildElementCount { get; } - [Name("lastChild")] - public extern Node LastChild { get; } - [Name("style")] - public extern CSSStyleDeclaration Style { get; } - [Name("classList")] - public extern DOMTokenList ClassList { get; } - [Name("outerHTML")] - public extern HaxeString OuterHTML { get; set; } - [Name("innerHTML")] - public extern HaxeString InnerHTML { get; set; } - [Name("innerText")] - public extern HaxeString InnerText { get; set; } - [Name("setAttribute")] - public extern void SetAttribute(HaxeString name, HaxeString value); - [Name("offsetWidth")] - public extern HaxeInt OffsetWidth { get; } - [Name("offsetHeight")] - public extern HaxeInt OffsetHeight { get; } - [Name("className")] - public extern HaxeString ClassName { get; set; } - [Name("dataset")] - public extern DOMStringMap Dataset { get; set; } - [Name("attributes")] - public extern NamedNodeMap Attributes { get; set; } - [Name("clientWidth")] - public extern HaxeInt ClientWidth { get; set; } - [Name("clientHeight")] - public extern HaxeInt ClientHeight { get; set; } - [Name("clientTop")] - public extern HaxeInt ClientTop { get; set; } - [Name("clientLeft")] - public extern HaxeInt ClientLeft { get; set; } - - [Name("scrollTop")] - public HaxeInt ScrollTop { get; set; } - [Name("scrollLeft")] - public HaxeInt ScrollLeft { get; set; } - - - [Name("querySelector")] - public extern Element QuerySelector(HaxeString selectors); - [Name("querySelectorAll")] - public extern NodeList QuerySelectorAll(HaxeString selectors); - - [Name("getElementsByClassName")] - public extern HTMLCollection GetElementsByClassName(HaxeString className); - - [Name("getClientRects")] - public extern DOMRectList GetClientRects(); - [Name("getBoundingClientRect")] - public extern DOMRect GetBoundingClientRect(); - - [Name("click")] - public extern void Click(); - - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/DOMPoint.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/DOMPoint.cs deleted file mode 100644 index d434453a8..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/DOMPoint.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html -{ - [External] - [Name("js.html.DOMPoint")] - [NativeConstructors] - public class DOMPoint : DOMPointReadonly - { - public extern DOMPoint(float x, float y, float z = 0, float w = 1); - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/DOMPointReadonly.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/DOMPointReadonly.cs deleted file mode 100644 index 94a65de44..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/DOMPointReadonly.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Haxe; -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html -{ - [External] - [Name("js.html.DOMPointReadonly")] - public class DOMPointReadonly - { - [Name("w")] - public extern HaxeFloat W { get; } - [Name("x")] - public extern HaxeFloat X { get; } - [Name("y")] - public extern HaxeFloat Y { get; } - [Name("z")] - public extern HaxeFloat Z { get; } - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/DOMRect.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/DOMRect.cs deleted file mode 100644 index 2ff5afb71..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/DOMRect.cs +++ /dev/null @@ -1,27 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html -{ - [External] - [Name("js.html.DOMRect")] - public class DOMRect : DOMRectReadOnly - { - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/DOMRectList.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/DOMRectList.cs deleted file mode 100644 index edb68acad..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/DOMRectList.cs +++ /dev/null @@ -1,30 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using Haxe; -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html -{ - [External] - [Name("js.html.DOMRectList")] - public class DOMRectList - { - [Name("length")] - public extern HaxeInt Length { get; } - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/DOMRectReadOnly.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/DOMRectReadOnly.cs deleted file mode 100644 index d6962c010..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/DOMRectReadOnly.cs +++ /dev/null @@ -1,44 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using Haxe; -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html -{ - [External] - [Name("js.html.DOMRectReadOnly")] - public class DOMRectReadOnly - { - [Name("top")] - public extern HaxeFloat Top { get; } - [Name("left")] - public extern HaxeFloat Left { get; } - [Name("right")] - public extern HaxeFloat Right { get; } - [Name("bottom")] - public extern HaxeFloat Bottom { get; } - [Name("x")] - public extern HaxeFloat X { get; } - [Name("y")] - public extern HaxeFloat Y { get; } - [Name("width")] - public extern HaxeFloat Width { get; } - [Name("height")] - public extern HaxeFloat Height { get; } - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/DOMStringMap.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/DOMStringMap.cs deleted file mode 100644 index a05bf8a1a..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/DOMStringMap.cs +++ /dev/null @@ -1,27 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html -{ - [External] - [Name("js.html.DOMStringMap")] - public class DOMStringMap - { - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/DOMTokenList.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/DOMTokenList.cs deleted file mode 100644 index 229da1cce..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/DOMTokenList.cs +++ /dev/null @@ -1,32 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using Haxe; -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html -{ - [External] - [Name("js.html.DOMTokenList")] - public class DOMTokenList - { - [Name("add")] - public extern void Add(HaxeString token); - [Name("remove")] - public extern void Remove(HaxeString token); - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/DataView.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/DataView.cs deleted file mode 100644 index f71bb1824..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/DataView.cs +++ /dev/null @@ -1,42 +0,0 @@ -using Phase.Attributes; - -namespace Haxe.Js.Html -{ - [External] - [Name("js.html.DataView")] - [NativeConstructors] - public class DataView : ArrayBufferView - { - public extern DataView(ArrayBuffer buffer); - public extern DataView(ArrayBuffer buffer, HaxeInt byteOffset, HaxeInt length); - - [Name("getInt8")] public extern HaxeInt GetInt8(HaxeInt byteOffset); - [Name("getUint8")] public extern HaxeInt GetUint8(HaxeInt byteOffset); - [Name("getInt16")] public extern HaxeInt GetInt16(HaxeInt byteOffset); - [Name("getInt16")] public extern HaxeInt GetInt16(HaxeInt byteOffset, HaxeBool littleEndian); - [Name("getUint16")] public extern HaxeInt GetUint16(HaxeInt byteOffset); - [Name("getUint16")] public extern HaxeInt GetUint16(HaxeInt byteOffset, HaxeBool littleEndian); - [Name("getInt32")] public extern HaxeInt GetInt32(HaxeInt byteOffset); - [Name("getInt32")] public extern HaxeInt GetInt32(HaxeInt byteOffset, HaxeBool littleEndian); - [Name("getUint32")] public extern HaxeInt GetUint32(HaxeInt byteOffset); - [Name("getUint32")] public extern HaxeInt GetUint32(HaxeInt byteOffset, HaxeBool littleEndian); - [Name("getFloat32")] public extern HaxeFloat GetFloat32(HaxeInt byteOffset); - [Name("getFloat32")] public extern HaxeFloat GetFloat32(HaxeInt byteOffset, HaxeBool littleEndian); - [Name("getFloat64")] public extern HaxeFloat GetFloat64(HaxeInt byteOffset); - [Name("getFloat64")] public extern HaxeFloat GetFloat64(HaxeInt byteOffset, HaxeBool littleEndian); - [Name("setInt8")] public extern void SetInt8(HaxeInt byteOffset, HaxeInt value); - [Name("setUint8")] public extern void SetUint8(HaxeInt byteOffset, HaxeInt value); - [Name("setInt16")] public extern void SetInt16(HaxeInt byteOffset, HaxeInt value); - [Name("setInt16")] public extern void SetInt16(HaxeInt byteOffset, HaxeInt value, HaxeBool littleEndian); - [Name("setUint16")] public extern void SetUint16(HaxeInt byteOffset, HaxeInt value); - [Name("setUint16")] public extern void SetUint16(HaxeInt byteOffset, HaxeInt value, HaxeBool littleEndian); - [Name("setInt32")] public extern void SetInt32(HaxeInt byteOffset, HaxeInt value); - [Name("setInt32")] public extern void SetInt32(HaxeInt byteOffset, HaxeInt value, HaxeBool littleEndian); - [Name("setUint32")] public extern void SetUint32(HaxeInt byteOffset, HaxeInt value); - [Name("setUint32")] public extern void SetUint32(HaxeInt byteOffset, HaxeInt value, HaxeBool littleEndian); - [Name("setFloat32")] public extern void SetFloat32(HaxeInt byteOffset, HaxeFloat value); - [Name("setFloat32")] public extern void SetFloat32(HaxeInt byteOffset, HaxeFloat value, HaxeBool littleEndian); - [Name("setFloat64")] public extern void SetFloat64(HaxeInt byteOffset, HaxeFloat value); - [Name("setFloat64")] public extern void SetFloat64(HaxeInt byteOffset, HaxeFloat value, HaxeBool littleEndian); - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/DedicatedWorkerGlobalScope.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/DedicatedWorkerGlobalScope.cs deleted file mode 100644 index 40e97e9ea..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/DedicatedWorkerGlobalScope.cs +++ /dev/null @@ -1,29 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html -{ - [External] - [Name("js.html.DedicatedWorkerGlobalScope")] - public class DedicatedWorkerGlobalScope : WorkerGlobalScope - { - [Name("postMessage")] - public extern void PostMessage(object message); - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/Document.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/Document.cs deleted file mode 100644 index 1ee00d808..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/Document.cs +++ /dev/null @@ -1,53 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using Haxe; -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html -{ - [External] - [Name("js.html.Document")] - public class Document : Node - { - [Name("currentScript")] - public extern Element CurrentScript { get; } - - [Name("fonts")] - public extern FontFaceSet Fonts { get; } - - [Name("querySelector")] - public extern Element QuerySelector(HaxeString selectors); - [Name("querySelectorAll")] - public extern NodeList QuerySelectorAll(HaxeString selectors); - - [Name("getElementById")] - public extern Element GetElementById(HaxeString id); - - [Name("createElement")] - public extern Element CreateElement(HaxeString localName); - - [Name("getElementsByTagName")] - public extern HTMLCollection GetElementsByTagName(HaxeString localName); - - [Name("createEvent")] - public extern Event CreateEvent(HaxeString interface_); - - [Name("defaultView")] - public Window DefaultView { get; set; } - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/Element.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/Element.cs deleted file mode 100644 index e5044e37f..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/Element.cs +++ /dev/null @@ -1,28 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html -{ - [External] - [Name("js.html.Element")] - [CastMode(CastMode.UnsafeCast)] - public class Element : DOMElement - { - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/Event.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/Event.cs deleted file mode 100644 index a66e4eca7..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/Event.cs +++ /dev/null @@ -1,56 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using Haxe; -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html -{ - [External] - [Name("js.html.Event")] - public class Event - { - public static readonly HaxeInt NONE; - public static readonly HaxeInt CAPTURING_PHASE; - public static readonly HaxeInt AT_TARGET; - public static readonly HaxeInt BUBBLING_PHASE; - public static readonly HaxeInt ALT_MASK; - public static readonly HaxeInt CONTROL_MASK; - public static readonly HaxeInt SHIFT_MASK; - public static readonly HaxeInt META_MASK; - - [Name("type")] public extern HaxeString Type { get; } - [Name("target")] public extern global::Haxe.Js.Html.EventTarget Target { get; } - [Name("currentTarget")] public extern global::Haxe.Js.Html.EventTarget CurrentTarget { get; } - [Name("eventPhase")] public extern HaxeInt EventPhase { get; } - [Name("bubbles")] public extern HaxeBool Bubbles { get; } - [Name("cancelable")] public extern HaxeBool Cancelable { get; } - [Name("defaultPrevented")] public extern HaxeBool DefaultPrevented { get; } - [Name("isTrusted")] public extern HaxeBool IsTrusted { get; } - [Name("timeStamp")] public extern HaxeFloat TimeStamp { get; } - [Name("originalTarget")] public extern global::Haxe.Js.Html.EventTarget OriginalTarget { get; } - [Name("explicitOriginalTarget")] public extern global::Haxe.Js.Html.EventTarget ExplicitOriginalTarget { get; } - - public extern Event(HaxeString type, dynamic eventInitDict); - - [Name("stopPropagation")] public extern void StopPropagation(); - [Name("stopImmediatePropagation")] public extern void StopImmediatePropagation(); - [Name("preventDefault")] public extern void PreventDefault(); - [Name("initEvent")] public extern void InitEvent(HaxeString type, HaxeBool bubbles, HaxeBool cancelable); - [Name("getPreventDefault")] public extern HaxeBool GetPreventDefault(); - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/EventListener.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/EventListener.cs deleted file mode 100644 index e9ba9b07c..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/EventListener.cs +++ /dev/null @@ -1,29 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Haxe.Js.Html; -using Phase.Attributes; - -namespace Haxe.Js.Html -{ - [External] - [Name("js.html.EventTarget")] - public abstract class EventListener - { - public abstract void HandleEvent(Event e); - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/EventTarget.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/EventTarget.cs deleted file mode 100644 index b9922e02f..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/EventTarget.cs +++ /dev/null @@ -1,43 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.Haxe.Js.Html; -using Phase.Attributes; - -namespace Haxe.Js.Html -{ - [External] - [Name("js.html.EventTarget")] - public class EventTarget - { - [Name("addEventListener")] public extern void AddEventListener(HaxeString type, EventListener listener); - [Name("addEventListener")] public extern void AddEventListener(HaxeString type, EventListener listener, HaxeBool capture); - [Name("addEventListener")] public extern void AddEventListener(HaxeString type, EventListener listener, HaxeBool capture, HaxeBool wantsUntrusted); - - [Name("addEventListener")] public extern void AddEventListener(HaxeString type, Delegate listener); - [Name("addEventListener")] public extern void AddEventListener(HaxeString type, Delegate listener, HaxeBool capture); - [Name("addEventListener")] public extern void AddEventListener(HaxeString type, Delegate listener, HaxeBool capture, bool wantsUntrusted); - - [Name("removeEventListener")] public extern void RemoveEventListener(HaxeString type, EventListener listener); - [Name("removeEventListener")] public extern void RemoveEventListener(HaxeString type, EventListener listener, HaxeBool capture); - [Name("removeEventListener")] public extern void RemoveEventListener(HaxeString type, Delegate listener); - [Name("removeEventListener")] public extern void RemoveEventListener(HaxeString type, Delegate listener, HaxeBool capture); - - [Name("dispatchEvent")] public extern void DispatchEvent(Event e); - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/Float32Array.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/Float32Array.cs deleted file mode 100644 index 7b6bbe280..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/Float32Array.cs +++ /dev/null @@ -1,42 +0,0 @@ -using Phase.Attributes; - -namespace Haxe.Js.Html -{ - [External] - [Name("js.html.Float32Array")] - [NativeConstructors] - public class Float32Array : ArrayBufferView - { - public static readonly HaxeInt BYTES_PER_ELEMENT; - - [Name("length")] - public extern HaxeInt Length { get; } - - public extern Float32Array(HaxeInt length); - public extern Float32Array(Float32Array array); - public extern Float32Array(HaxeFloat[] array); - public extern Float32Array(ArrayBuffer buffer); - public extern Float32Array(ArrayBuffer buffer, HaxeInt byteOffset, HaxeInt length); - - [NativeIndexer] - public extern HaxeFloat this[HaxeInt index] - { - get; - set; - } - - [Name("set")] - public extern void Set(Float32Array buffer); - [Name("set")] - public extern void Set(Float32Array buffer, HaxeInt offset); - [Name("set")] - public extern void Set(HaxeFloat[] buffer); - [Name("set")] - public extern void Set(HaxeFloat[] buffer, HaxeInt offset); - - [Name("subarray")] - public extern Float32Array SubArray(HaxeInt start); - [Name("subarray")] - public extern Float32Array SubArray(HaxeInt start, HaxeInt end); - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/Float64Array.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/Float64Array.cs deleted file mode 100644 index 0e7238aac..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/Float64Array.cs +++ /dev/null @@ -1,59 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using Phase.Attributes; - -namespace Haxe.Js.Html -{ - [External] - [Name("js.html.Float64Array")] - [NativeConstructors] - public class Float64Array : ArrayBufferView - { - public static readonly HaxeInt BYTES_PER_ELEMENT; - - [Name("length")] - public extern HaxeInt Length { get; } - - public extern Float64Array(HaxeInt length); - public extern Float64Array(Float64Array array); - public extern Float64Array(HaxeFloat[] array); - public extern Float64Array(ArrayBuffer buffer); - public extern Float64Array(ArrayBuffer buffer, HaxeInt byteOffset, HaxeInt length); - - [NativeIndexer] - public extern HaxeFloat this[HaxeInt index] - { - get; - set; - } - - [Name("set")] - public extern void Set(Float64Array buffer); - [Name("set")] - public extern void Set(Float64Array buffer, HaxeInt offset); - [Name("set")] - public extern void Set(HaxeFloat[] buffer); - [Name("set")] - public extern void Set(HaxeFloat[] buffer, HaxeInt offset); - - [Name("subarray")] - public extern Float64Array SubArray(HaxeInt start); - [Name("subarray")] - public extern Float64Array SubArray(HaxeInt start, HaxeInt end); - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/FontFace.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/FontFace.cs deleted file mode 100644 index 95c31f69d..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/FontFace.cs +++ /dev/null @@ -1,27 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html -{ - [External] - [Name("js.html.FontFace")] - public class FontFace - { - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/FontFaceSet.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/FontFaceSet.cs deleted file mode 100644 index b8193c128..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/FontFaceSet.cs +++ /dev/null @@ -1,34 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using Haxe; -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html -{ - [External] - [Name("js.html.FontFaceSet")] - public class FontFaceSet - { - [Name("load")] - public extern Promise> Load(HaxeString font); - [Name("load")] - public extern Promise> Load(HaxeString font, HaxeString text); - [Name("check")] - public extern bool Check(HaxeString font); - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/HTMLCollection.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/HTMLCollection.cs deleted file mode 100644 index 71667522b..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/HTMLCollection.cs +++ /dev/null @@ -1,34 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using Haxe; -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html -{ - [External] - [Name("js.html.HTMLCollection")] - public class HTMLCollection - { - [Name("length")] - public extern HaxeInt Length { get; } - - [Name("item")] - public extern Element Item(HaxeInt index); - - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/HTMLDocument.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/HTMLDocument.cs deleted file mode 100644 index 36b202e51..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/HTMLDocument.cs +++ /dev/null @@ -1,37 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using Haxe; -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html -{ - [External] - [Name("js.html.HTMLDocument")] - public class HTMLDocument : Document - { - [Name("write")] - public extern void Write(HaxeString s); - [Name("body")] - public extern BodyElement Body { get; set; } - [Name("documentElement")] - public extern Element DocumentElement { get; set; } - [Name("createTextNode")] - public extern Node CreateTextNode(HaxeString text); - } -} diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/Location.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/Location.cs deleted file mode 100644 index 1485c9640..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/Location.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Haxe; -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html -{ - [External] - [Name("js.html.Location")] - public class Location - { - [Name("hash")] - public extern HaxeString Hash { get; set; } - [Name("host")] - public extern HaxeString Host { get; set; } - [Name("hostname")] - public extern HaxeString Hostname { get; set; } - [Name("href")] - public extern HaxeString HRef { get; set; } - [Name("origin")] - public extern HaxeString Origin { get; } - [Name("pathname")] - public extern HaxeString PathName { get; set; } - [Name("port")] - public extern HaxeString Port { get; set; } - [Name("protocol")] - public extern HaxeString Protocol { get; set; } - [Name("search")] - public extern HaxeString Search { get; set; } - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/MessageEvent.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/MessageEvent.cs deleted file mode 100644 index 9d9e1a522..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/MessageEvent.cs +++ /dev/null @@ -1,31 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using Haxe; -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html -{ - [External] - [Name("js.html.MessageEvent")] - public class MessageEvent : Event - { - [Name("data")] - public dynamic Data { get; } - public extern MessageEvent(HaxeString type, dynamic eventInitDict); - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/MouseEvent.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/MouseEvent.cs deleted file mode 100644 index 28524241a..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/MouseEvent.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Haxe; -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html -{ - [External] - [Name("js.html.MouseEvent")] - public class MouseEvent: UIEvent - { - [Name("button")] - public extern int Button { get; set; } - - public extern MouseEvent(HaxeString type, dynamic eventInitDict); - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/NamedNodeMap.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/NamedNodeMap.cs deleted file mode 100644 index 43dc0d65d..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/NamedNodeMap.cs +++ /dev/null @@ -1,34 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using Haxe; -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html -{ - [External] - [Name("js.html.NamedNodeMap")] - public class NamedNodeMap - { - [Name("length")] - public extern HaxeInt Length { get; } - [Name("getNamedItem")] - public extern Attr GetNamedItem(HaxeString name); - [Name("item")] - public extern Attr Item(HaxeInt index); - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/Node.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/Node.cs deleted file mode 100644 index 79a102474..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/Node.cs +++ /dev/null @@ -1,51 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using Haxe; -using Haxe.Js.Html; -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html -{ - [External] - [Name("js.html.Node")] - public class Node : EventTarget - { - [Name("nodeName")] - public extern HaxeString NodeName { get; } - [Name("nodeValue")] - public extern HaxeString NodeValue { get; } - [Name("appendChild")] - public extern void AppendChild(Node node); - [Name("removeChild")] - public extern void RemoveChild(Node node); - [Name("childNodes")] - public extern NodeList ChildNodes { get; } - [Name("firstChild")] - public extern Node FirstChild { get; set; } - - - [Name("ownerDocument")] - public extern HTMLDocument OwnerDocument { get; set; } - - [Name("replaceChild")] - public extern void ReplaceChild(Node node, Node child); - - [Name("insertBefore")] - public extern void InsertBefore(Node node, Node child); - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/NodeList.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/NodeList.cs deleted file mode 100644 index ef4679f5d..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/NodeList.cs +++ /dev/null @@ -1,33 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using Haxe; -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html -{ - [External] - [Name("js.html.NodeList")] - public class NodeList - { - [Name("length")] - public extern HaxeInt Length { get; } - - [Name("item")] - public extern Node Item(HaxeInt index); - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/Screen.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/Screen.cs deleted file mode 100644 index f154f5654..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/Screen.cs +++ /dev/null @@ -1,37 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using Haxe; -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html -{ - [External] - [Name("js.html.Screen")] - public class Screen - { - [Name("top")] - public extern HaxeInt Top { get; } - [Name("left")] - public extern HaxeInt Left { get; } - [Name("width")] - public extern HaxeInt Width { get; } - [Name("height")] - public extern HaxeInt Height { get; } - - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/ScriptElement.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/ScriptElement.cs deleted file mode 100644 index 8b0ad87d6..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/ScriptElement.cs +++ /dev/null @@ -1,31 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using Haxe; -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html -{ - [External] - [Name("js.html.ScriptElement")] - [CastMode(CastMode.UnsafeCast)] - public class ScriptElement : Element - { - [Name("src")] - public extern HaxeString Src { get; set; } - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/StyleElement.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/StyleElement.cs deleted file mode 100644 index c4a86af57..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/StyleElement.cs +++ /dev/null @@ -1,31 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using Haxe; -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html -{ - [External] - [Name("js.html.StyleElement")] - [CastMode(CastMode.UnsafeCast)] - public class StyleElement : Element - { - [Name("type")] - public extern HaxeString Type { get; set; } - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/TextMetrics.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/TextMetrics.cs deleted file mode 100644 index fa35fff1d..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/TextMetrics.cs +++ /dev/null @@ -1,30 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using Haxe; -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html -{ - [External] - [Name("js.html.TextMetrics")] - public class TextMetrics - { - [Name("width")] - public extern HaxeFloat Width { get; } - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/UIEvent.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/UIEvent.cs deleted file mode 100644 index f9abe90a8..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/UIEvent.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Haxe; -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html -{ - [External] - [Name("js.html.UIEvent")] - public class UIEvent : Event - { - [Name("pageX")] - public extern int PageX { get; } - [Name("pageY")] - public extern int PageY { get; } - - - - public extern UIEvent(HaxeString type, dynamic eventInitDict); - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/URL.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/URL.cs deleted file mode 100644 index 07dcda6f5..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/URL.cs +++ /dev/null @@ -1,31 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using Haxe; -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html -{ - [External] - [Name("js.html.URL")] - public class URL - { - [Name("createObjectURL")] - public static extern HaxeString CreateObjectURL(Blob blob); - } -} diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/Uint16Array.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/Uint16Array.cs deleted file mode 100644 index 0c7e51fd4..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/Uint16Array.cs +++ /dev/null @@ -1,55 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using Phase.Attributes; - -namespace Haxe.Js.Html -{ - [External] - [Name("js.html.Uint16Array")] - [NativeConstructors] - public class Uint16Array : ArrayBufferView - { - public static readonly HaxeInt BYTES_PER_ELEMENT; - - [Name("length")] - public extern HaxeInt Length { get; } - - public extern Uint16Array(HaxeInt length); - public extern Uint16Array(Uint8Array array); - public extern Uint16Array(HaxeInt[] array); - public extern Uint16Array(ArrayBuffer buffer); - public extern Uint16Array(ArrayBuffer buffer, HaxeInt byteOffset, HaxeInt length); - - [NativeIndexer] - public extern HaxeInt this[HaxeInt index] { get; set; } - - [Name("set")] - public extern void Set(Uint8Array buffer); - [Name("set")] - public extern void Set(Uint8Array buffer, HaxeInt offset); - [Name("set")] - public extern void Set(HaxeInt[] buffer); - [Name("set")] - public extern void Set(HaxeInt[] buffer, HaxeInt offset); - - [Name("subarray")] - public extern Uint8Array SubArray(HaxeInt start); - [Name("subarray")] - public extern Uint8Array SubArray(HaxeInt start, HaxeInt end); - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/Uint8Array.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/Uint8Array.cs deleted file mode 100644 index 8dc05b55c..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/Uint8Array.cs +++ /dev/null @@ -1,52 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using Phase.Attributes; - -namespace Haxe.Js.Html -{ - [External] - [Name("js.html.Uint8Array")] - [NativeConstructors] - public class Uint8Array : ArrayBufferView - { - public static readonly HaxeInt BYTES_PER_ELEMENT; - - [Name("length")] - public extern HaxeInt Length { get; } - - public extern Uint8Array(HaxeInt length); - public extern Uint8Array(Uint8Array array); - public extern Uint8Array(HaxeInt[] array); - public extern Uint8Array(ArrayBuffer buffer); - public extern Uint8Array(ArrayBuffer buffer, HaxeInt byteOffset, HaxeInt length); - - [Name("set")] - public extern void Set(Uint8Array buffer); - [Name("set")] - public extern void Set(Uint8Array buffer, HaxeInt offset); - [Name("set")] - public extern void Set(HaxeInt[] buffer); - [Name("set")] - public extern void Set(HaxeInt[] buffer, HaxeInt offset); - - [Name("subarray")] - public extern Uint8Array SubArray(HaxeInt start); - [Name("subarray")] - public extern Uint8Array SubArray(HaxeInt start, HaxeInt end); - } -} diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/Window.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/Window.cs deleted file mode 100644 index ed5f80f11..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/Window.cs +++ /dev/null @@ -1,79 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using Haxe; -using Haxe.Js.Html; -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html -{ - [External] - [Name("js.html.Window")] - public class Window : EventTarget - { - [Name("document")] - public extern HTMLDocument Document { get; } - [Name("screen")] - public extern Screen Screen { get; } - [Name("location")] - public extern Location Location { get; } - - [Name("setTimeout")] - public extern HaxeInt SetTimeout(Delegate handler, HaxeInt timeout); - - [Name("getComputedStyle")] - public extern CSSStyleDeclaration GetComputedStyle(Element elt); - - [Name("clearTimeout")] - public extern void ClearTimeout(HaxeInt timeoutId); - - [Name("setInterval")] - public extern HaxeInt SetInterval(Delegate handler, HaxeInt interval); - - [Name("clearInterval")] - public extern void ClearInterval(HaxeInt intervalId); - - [Name("innerHeight")] - public extern HaxeInt InnerHeight { get; } - - [Name("innerWidth")] - public extern HaxeInt InnerWidth { get; } - - [Name("pageYOffset")] - public HaxeInt PageYOffset { get; } - - [Name("pageXOffset")] - public HaxeInt PageXOffset { get; } - - [Name("open")] - public extern Window Open(HaxeString url, HaxeString target, HaxeString features); - [Name("resizeTo")] - public extern void ResizeTo(HaxeInt x, HaxeInt y); - - [Name("moveTo")] - public extern void MoveTo(HaxeInt x, HaxeInt y); - [Name("focus")] - public extern void Focus(); - - [Name("print")] - public extern void Print(); - - [Name("requestAnimationFrame")] - public extern void RequestAnimationFrame(Action function); - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/Worker.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/Worker.cs deleted file mode 100644 index a696b2d17..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/Worker.cs +++ /dev/null @@ -1,35 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using Haxe; -using Haxe.Js.Html; -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html -{ - [Name("js.html.Worker")] - [External] - public class Worker : EventTarget - { - public extern Worker(HaxeString worker); - [Name("postMessage")] - public extern void PostMessage(object message); - [Name("terminate")] - public extern void Terminate(); - } -} diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/WorkerGlobalScope.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/WorkerGlobalScope.cs deleted file mode 100644 index 1431a7451..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/WorkerGlobalScope.cs +++ /dev/null @@ -1,28 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using Haxe.Js.Html; -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js.Html -{ - [External] - [Name("js.html.WorkerGlobalScope")] - public class WorkerGlobalScope : EventTarget - { - } -} diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/XMLHttpRequest.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/XMLHttpRequest.cs deleted file mode 100644 index 68d5a8ded..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/XMLHttpRequest.cs +++ /dev/null @@ -1,89 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using Phase.Attributes; - -namespace Haxe.Js.Html -{ - [External] - [Name("js.html.XMLHttpRequest")] - [NativeConstructors] - public class XMLHttpRequest : XMLHttpRequestEventTarget - { - public static readonly HaxeInt UNSENT; - public static readonly HaxeInt OPENED; - public static readonly HaxeInt HEADERS_RECEIVED; - public static readonly HaxeInt LOADING; - public static readonly HaxeInt DONE; - - [Name("onreadystatechange")] public Delegate OnReadyStateChange; - [Name("readyState")] public extern HaxeInt ReadyState { get; } - [Name("withCredentials")] public extern HaxeBool WithCredentials { get; } - [Name("upload")] public extern XMLHttpRequestUpload Upload { get; } - [Name("responseURL")] public extern HaxeString ResponseURL { get; } - [Name("status")] public extern HaxeInt Status { get; } - [Name("statusText")] public extern HaxeString StatusText { get; } - [Name("responseType")] public extern XMLHttpRequestResponseType ResponseType { get; set; } - [Name("response")] public extern dynamic Response { get; } - [Name("responseText")] public extern HaxeString ResponseText { get; } - //[Name("responseXML")] public extern HTMLDocument ResponseXML { get; } - - public extern XMLHttpRequest(); - public extern XMLHttpRequest(object d); - public extern XMLHttpRequest(HaxeString s); - - [Name("open")] - public extern void Open(HaxeString method, HaxeString url); - [Name("open")] - public extern void Open(HaxeString method, HaxeString url, HaxeBool async, HaxeString user = null, HaxeString password = null); - - [Name("setRequestHeader")] - public extern void SetRequestHeader(HaxeString header, HaxeString value); - - [Name("send")] - public extern void Send(); - - //[Name("Send")] - //public extern void Send(ArrayBuffer data); - - //[Name("Send")] - //public extern void Send(ArrayBufferView data); - - //[Name("Send")] - //public extern void Send(Blob data); - - //[Name("Send")] - //public extern void Send(HTMLDocument data); - - [Name("Send")] - public extern void Send(HaxeString data); - - //[Name("Send")] - //public extern void Send(FormData data); - - [Name("abort")] - public extern void Abort(); - - [Name("getResponseHeader")] - public extern HaxeString GetResponseHeader(HaxeString header); - [Name("getAllResponseHeaders")] - public extern HaxeString GetAllResponseHeaders(); - [Name("overrideMimeType")] - public extern void OverrideMimeType(HaxeString mime); - } -} diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/XMLHttpRequestEventTarget.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/XMLHttpRequestEventTarget.cs deleted file mode 100644 index d41d9ea2c..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/XMLHttpRequestEventTarget.cs +++ /dev/null @@ -1,35 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using Phase.Attributes; - -namespace Haxe.Js.Html -{ - [External] - [Name("js.html.XMLHttpRequestEventTarget")] - public class XMLHttpRequestEventTarget : EventTarget - { - [Name("onloadstart")] public Delegate OnLoadStart; - [Name("onprogress")] public Delegate OnProgress; - [Name("onabort")] public Delegate OnAbort; - [Name("onerror")] public Delegate OnError; - [Name("onload")] public Delegate OnLoad; - [Name("ontimeout")] public Delegate OnTimeout; - [Name("onloadend")] public Delegate OnLoadEnd; - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/XMLHttpRequestResponseType.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/XMLHttpRequestResponseType.cs deleted file mode 100644 index f1cd8fe39..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/XMLHttpRequestResponseType.cs +++ /dev/null @@ -1,35 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using Phase.Attributes; - -namespace Haxe.Js.Html -{ - [External] - [Name("js.html.XMLHttpRequestResponseType")] - public class XMLHttpRequestResponseType - { - public static readonly XMLHttpRequestResponseType NON; - public static readonly XMLHttpRequestResponseType ARRAYBUFFER; - public static readonly XMLHttpRequestResponseType BLOB; - public static readonly XMLHttpRequestResponseType DOCUMENT; - public static readonly XMLHttpRequestResponseType JSON; - public static readonly XMLHttpRequestResponseType TEXT; - - private extern XMLHttpRequestResponseType(); - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Html/XMLHttpRequestUpload.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Html/XMLHttpRequestUpload.cs deleted file mode 100644 index 413481126..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Html/XMLHttpRequestUpload.cs +++ /dev/null @@ -1,27 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using Phase.Attributes; - -namespace Haxe.Js.Html -{ - [External] - [Name("js.html.XMLHttpRequestUpload")] - public class XMLHttpRequestUpload : XMLHttpRequestEventTarget - { - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Lib.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Lib.cs deleted file mode 100644 index 1296d0862..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Lib.cs +++ /dev/null @@ -1,30 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js -{ - [Name("js.Lib")] - [External] - public class Lib - { - [Name("global")] - public static extern dynamic Global { get; } - } -} diff --git a/Source/AlphaTab.JavaScript/Haxe/Js/Promise.cs b/Source/AlphaTab.JavaScript/Haxe/Js/Promise.cs deleted file mode 100644 index 884a0f559..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Js/Promise.cs +++ /dev/null @@ -1,33 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using Phase.Attributes; - -namespace AlphaTab.Haxe.Js -{ - [External] - [Name("js.PromiseCallback")] - public delegate TOut PromiseCallback(T param); - - [External] - [Name("js.Promise")] - public class Promise - { - [Name("then")] - public extern void Then(PromiseCallback fulfillCallback); - } -} diff --git a/Source/AlphaTab.JavaScript/Haxe/Zip/Reader.cs b/Source/AlphaTab.JavaScript/Haxe/Zip/Reader.cs deleted file mode 100644 index 831cb527f..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/Zip/Reader.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Haxe.IO; -using Phase.Attributes; - -namespace Haxe.Zip -{ - [External] - [Name("haxe.zip.Reader")] - [NativeConstructors] - public class Reader - { - public extern Reader(HaxeInput input); - - [Name("read")] - public extern HaxeList Read(); - } - - [External] - [Name("List")] - [NativeConstructors] - [ForeachMode(ForeachMode.Native)] - public class HaxeList : IEnumerable - { - extern IEnumerator IEnumerable.GetEnumerator(); - extern IEnumerator IEnumerable.GetEnumerator(); - } - - [External] - [Name("haxe.zip.Entry")] - public class Entry - { - [Name("fileName")] - public HaxeString FileName { get; set; } - [Name("data")] - public HaxeBytes Data { get; set; } - } -} diff --git a/Source/AlphaTab.JavaScript/Haxe/jQuery/JQuery.cs b/Source/AlphaTab.JavaScript/Haxe/jQuery/JQuery.cs deleted file mode 100644 index 230efa2bd..000000000 --- a/Source/AlphaTab.JavaScript/Haxe/jQuery/JQuery.cs +++ /dev/null @@ -1,61 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Haxe.Js.Html; -using Phase.Attributes; - -namespace AlphaTab.Haxe.jQuery -{ - [Name("js.jquery.JQuery")] - [NativeConstructors] - [External] - public class JQuery - { - public extern JQuery(); - public extern JQuery(Element element); - public extern JQuery(JQuery selection); - public extern JQuery(object obj); - public extern JQuery(string html, object attributes); - public extern JQuery(string html, Document ownerDocument); - public extern JQuery(string selector); - - [Name("context")] - public extern Element Context { get; } - - [Name("length")] - public extern int Length { get; } - - [Name("data")] - public extern object Data(string key); - - [Name("data")] - public extern object Data(string key, object value); - - [Name("removeData")] - public extern void RemoveData(string key); - - [Name("empty")] - public extern JQuery Empty(); - - [NativeIndexer] - public extern Element this[int index] - { - get; - set; - } - } -} diff --git a/Source/AlphaTab.JavaScript/IO/ReadableInput.cs b/Source/AlphaTab.JavaScript/IO/ReadableInput.cs deleted file mode 100644 index 86b545b84..000000000 --- a/Source/AlphaTab.JavaScript/IO/ReadableInput.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Haxe.IO; -using Haxe; -using Phase; - -namespace AlphaTab.IO -{ - class ReadableInput : HaxeInput - { - private readonly IReadable _readable; - - public ReadableInput(IReadable readable) - { - _readable = readable; - } - - public override HaxeInt ReadByte() - { - return _readable.ReadByte(); - } - - public override HaxeInt ReadBytes(HaxeBytes s, HaxeInt pos, HaxeInt len) - { - byte[] data = Script.Write("s.getData()"); - return _readable.Read(data, pos, len); - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/IO/ZipFile.cs b/Source/AlphaTab.JavaScript/IO/ZipFile.cs deleted file mode 100644 index bf07705fc..000000000 --- a/Source/AlphaTab.JavaScript/IO/ZipFile.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Haxe.Zip; -using Phase; - -namespace AlphaTab.IO -{ - partial class ZipFile - { - /// - /// Load a complete ZipFile to the memory. - /// - /// the binary source to read from. - /// - public void Load(IReadable s) - { - var haxeInput = new ReadableInput(s); - var reader = new Reader(haxeInput); - var entries = reader.Read(); - foreach (var entry in entries) - { - string fullName = entry.FileName; - if (FileFilter == null || FileFilter(fullName)) - { - var i = fullName.LastIndexOf("/"); - string name = i >= 0 ? fullName.Substring(i + 1) : fullName; - var data = entry.Data.GetData(); - - Entries.Add(new ZipEntry - { - FullName = fullName, - FileName = name, - Data = Script.Write("data") - }); - } - } - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Importer/FileLoadException.cs b/Source/AlphaTab.JavaScript/Importer/FileLoadException.cs deleted file mode 100644 index 6df1a1945..000000000 --- a/Source/AlphaTab.JavaScript/Importer/FileLoadException.cs +++ /dev/null @@ -1,33 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using Haxe.Js.Html; - -namespace AlphaTab.Importer -{ - public class FileLoadException : AlphaTabException - { - public XMLHttpRequest Xhr { get; set; } - - public FileLoadException(string message, XMLHttpRequest xhr) - : base(message) - { - Xhr = xhr; - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Importer/Model/JsonConverter.cs b/Source/AlphaTab.JavaScript/Importer/Model/JsonConverter.cs deleted file mode 100644 index 02b5fd796..000000000 --- a/Source/AlphaTab.JavaScript/Importer/Model/JsonConverter.cs +++ /dev/null @@ -1,453 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Audio.Synth.Midi; -using AlphaTab.Audio.Synth.Midi.Event; -using AlphaTab.Collections; -using AlphaTab.Haxe; -using Haxe.Js.Html; -using Phase; - -namespace AlphaTab.Model -{ - /// - /// This class can convert a full instance to a simple JavaScript object and back for further - /// JSON serialization. - /// - class JsonConverter - { - /// - /// Converts the given score into a JSON encoded string. - /// - /// The score to serialize. - /// A JSON encoded string that can be used togehter with for conversion. - public static string ScoreToJson(Score score) - { - var obj = ScoreToJsObject(score); - return Json.Stringify(obj, (k, v) => - { - if (ArrayBuffer.IsView(v)) - { - return Script.Write("untyped __js__(\"Array.apply([], {0})\", v)"); - } - return v; - }); - } - - /// - /// Converts the given JSON string back to a object. - /// - /// The JSON string that was created via - /// The settings to use during conversion. - /// The converted score object. - public static Score JsonToScore(string json, Settings settings = null) - { - return JsObjectToScore(JsObjectToScore(Json.Parse(json), settings)); - } - - /// - /// Converts the score into a JavaScript object without circular dependencies. - /// - /// The score object to serialize - /// A serialized score object without ciruclar dependencies that can be used for further serializations. - public static object ScoreToJsObject(Score score) - { - Score score2 = Platform.Platform.NewObject(); - Score.CopyTo(score, score2); - score2.MasterBars = new FastList(); - score2.Tracks = new FastList(); - - score2.Stylesheet = Platform.Platform.NewObject(); - RenderStylesheet.CopyTo(score.Stylesheet, score2.Stylesheet); - - #region MasterBars - - for (var i = 0; i < score.MasterBars.Count; i++) - { - MasterBar masterBar = score.MasterBars[i]; - MasterBar masterBar2 = Platform.Platform.NewObject(); - MasterBar.CopyTo(masterBar, masterBar2); - if (masterBar.TempoAutomation != null) - { - masterBar2.TempoAutomation = Platform.Platform.NewObject(); - Automation.CopyTo(masterBar.TempoAutomation, masterBar2.TempoAutomation); - } - if (masterBar.Section != null) - { - masterBar2.Section = Platform.Platform.NewObject(); - Section.CopyTo(masterBar.Section, masterBar2.Section); - } - - masterBar2.Fermata = Platform.Platform.NewObject(); - foreach (var offset in masterBar.Fermata) - { - var fermata = masterBar.Fermata[offset]; - var fermata2 = masterBar2.Fermata[offset] = Platform.Platform.NewObject(); - Fermata.CopyTo(fermata, fermata2); - } - - score2.MasterBars.Add(masterBar2); - } - - #endregion - - #region Tracks - - for (int t = 0; t < score.Tracks.Count; t++) - { - var track = score.Tracks[t]; - Track track2 = Platform.Platform.NewObject(); - track2.Color = Platform.Platform.NewObject(); - Track.CopyTo(track, track2); - - track2.PlaybackInfo = Platform.Platform.NewObject(); - PlaybackInformation.CopyTo(track.PlaybackInfo, track2.PlaybackInfo); - - - #region Staves - track2.Staves = new FastList(); - - for (int s = 0; s < track.Staves.Count; s++) - { - var staff = track.Staves[s]; - Staff staff2 = Platform.Platform.NewObject(); - Staff.CopyTo(staff, staff2); - - staff2.Chords = new FastDictionary(); - foreach (var key in staff.Chords) - { - var chord = staff.Chords[key]; - Chord chord2 = Platform.Platform.NewObject(); - Chord.CopyTo(chord, chord2); - staff2.Chords[key] = chord; - } - - #region Bars - - staff2.Bars = new FastList(); - for (int b = 0; b < staff.Bars.Count; b++) - { - var bar = staff.Bars[b]; - Bar bar2 = Platform.Platform.NewObject(); - Bar.CopyTo(bar, bar2); - - #region Voices - - bar2.Voices = new FastList(); - for (int v = 0; v < bar.Voices.Count; v++) - { - var voice = bar.Voices[v]; - Voice voice2 = Platform.Platform.NewObject(); - Voice.CopyTo(voice, voice2); - - #region Beats - - voice2.Beats = new FastList(); - for (int bb = 0; bb < voice.Beats.Count; bb++) - { - var beat = voice.Beats[bb]; - Beat beat2 = Platform.Platform.NewObject(); - Beat.CopyTo(beat, beat2); - - beat2.Automations = new FastList(); - for (int a = 0; a < beat.Automations.Count; a++) - { - Automation automation = Platform.Platform.NewObject(); - Automation.CopyTo(beat.Automations[a], automation); - beat2.Automations.Add(automation); - } - - beat2.WhammyBarPoints = new FastList(); - for (int i = 0; i < beat.WhammyBarPoints.Count; i++) - { - BendPoint point = Platform.Platform.NewObject(); - BendPoint.CopyTo(beat.WhammyBarPoints[i], point); - beat2.WhammyBarPoints.Add(point); - } - - #region Notes - - beat2.Notes = new FastList(); - for (int n = 0; n < beat.Notes.Count; n++) - { - var note = beat.Notes[n]; - Note note2 = Platform.Platform.NewObject(); - Note.CopyTo(note, note2); - - note2.BendPoints = new FastList(); - for (int i = 0; i < note.BendPoints.Count; i++) - { - BendPoint point = Platform.Platform.NewObject(); - BendPoint.CopyTo(note.BendPoints[i], point); - note2.BendPoints.Add(point); - } - - beat2.Notes.Add(note2); - } - - #endregion - - voice2.Beats.Add(beat2); - } - - #endregion - - bar2.Voices.Add(voice2); - } - - #endregion - - staff2.Bars.Add(bar2); - } - - #endregion - track2.Staves.Add(staff2); - } - - #endregion - - score2.Tracks.Add(track2); - } - - #endregion - - return score2; - } - - /// - /// Converts the given JavaScript object into a score object. - /// - /// The javascript object created via - /// The settings to use during conversion. - /// The converted score object. - public static Score JsObjectToScore(object jsObject, Settings settings = null) - { - Score score = jsObject.As(); - var score2 = new Score(); - Score.CopyTo(score, score2); - RenderStylesheet.CopyTo(score.Stylesheet, score2.Stylesheet); - - #region MasterBars - - for (var i = 0; i < score.MasterBars.Count; i++) - { - var masterBar = score.MasterBars[i]; - var masterBar2 = new MasterBar(); - MasterBar.CopyTo(masterBar, masterBar2); - if (masterBar.TempoAutomation != null) - { - masterBar2.TempoAutomation = new Automation(); - Automation.CopyTo(masterBar.TempoAutomation, masterBar2.TempoAutomation); - } - if (masterBar.Section != null) - { - masterBar2.Section = new Section(); - Section.CopyTo(masterBar.Section, masterBar2.Section); - } - - foreach (var offset in masterBar.Fermata) - { - var fermata = masterBar.Fermata[offset]; - var fermata2 = new Fermata(); - Fermata.CopyTo(fermata, fermata2); - masterBar2.AddFermata(offset, fermata2); - } - - - score2.AddMasterBar(masterBar2); - } - - #endregion - - #region Tracks - - for (int t = 0; t < score.Tracks.Count; t++) - { - var track = score.Tracks[t]; - var track2 = new Track(track.Staves.Count); - Track.CopyTo(track, track2); - score2.AddTrack(track2); - - PlaybackInformation.CopyTo(track.PlaybackInfo, track2.PlaybackInfo); - - - #region Staves - - for (var s = 0; s < track.Staves.Count; s++) - { - var staff = track.Staves[s]; - var staff2 = track2.Staves[s]; - Staff.CopyTo(staff, staff2); - - - foreach (var key in staff.Chords) - { - var chord = staff.Chords[key]; - var chord2 = new Chord(); - Chord.CopyTo(chord, chord2); - staff2.Chords[key] = chord2; - } - #region Bars - - for (int b = 0; b < staff.Bars.Count; b++) - { - var bar = staff.Bars[b]; - var bar2 = new Bar(); - Bar.CopyTo(bar, bar2); - staff2.AddBar(bar2); - - #region Voices - - for (int v = 0; v < bar.Voices.Count; v++) - { - var voice = bar.Voices[v]; - var voice2 = new Voice(); - Voice.CopyTo(voice, voice2); - bar2.AddVoice(voice2); - - #region Beats - - for (int bb = 0; bb < voice.Beats.Count; bb++) - { - var beat = voice.Beats[bb]; - var beat2 = new Beat(); - Beat.CopyTo(beat, beat2); - voice2.AddBeat(beat2); - - for (int a = 0; a < beat.Automations.Count; a++) - { - var automation = new Automation(); - Automation.CopyTo(beat.Automations[a], automation); - beat2.Automations.Add(automation); - } - - for (int i = 0; i < beat.WhammyBarPoints.Count; i++) - { - var point = new BendPoint(); - BendPoint.CopyTo(beat.WhammyBarPoints[i], point); - beat2.AddWhammyBarPoint(point); - } - - #region Notes - - for (int n = 0; n < beat.Notes.Count; n++) - { - var note = beat.Notes[n]; - var note2 = new Note(); - Note.CopyTo(note, note2); - beat2.AddNote(note2); - - for (int i = 0; i < note.BendPoints.Count; i++) - { - var point = new BendPoint(); - BendPoint.CopyTo(note.BendPoints[i], point); - note2.AddBendPoint(point); - } - } - - #endregion - } - - #endregion - } - - #endregion - } - - #endregion - - } - #endregion - } - - #endregion - - score2.Finish(settings); - return score2; - } - - internal static MidiFile JsObjectToMidiFile(dynamic midi) - { - var midi2 = new MidiFile(); - midi2.Division = midi.Division; - - FastList midiEvents = midi.Events; - foreach (var midiEvent in midiEvents) - { - int tick = midiEvent.Tick; - int message = midiEvent.Message; - MidiEvent midiEvent2; - switch (midiEvent.Type) - { - case "alphaTab.audio.synth.midi.event.SystemExclusiveEvent": - midiEvent2 = new SystemExclusiveEvent(tick, 0, 0, midiEvent.Data); - midiEvent2.Message = message; - break; - case "alphaTab.audio.synth.midi.event.MetaDataEvent": - midiEvent2 = new MetaDataEvent(tick, 0, 0, midiEvent.Data); - midiEvent2.Message = message; - break; - case "alphaTab.audio.synth.midi.event.MetaNumberEvent": - midiEvent2 = new MetaNumberEvent(tick, 0, 0, midiEvent.Value); - midiEvent2.Message = message; - break; - default: - midiEvent2 = new MidiEvent(tick, 0, 0, 0); - midiEvent2.Message = message; - break; - } - midi2.Events.Add(midiEvent2); - } - - return midi2; - } - - internal static object MidiFileToJsObject(MidiFile midi) - { - var midi2 = Platform.Platform.NewObject(); - midi2.Division = midi.Division; - var midiEvents = new FastList(); - midi2.Events = midiEvents; - foreach (var midiEvent in midi.Events) - { - var midiEvent2 = Platform.Platform.NewObject(); - midiEvents.Add(midiEvent2); - midiEvent2.Type = Platform.Platform.GetTypeName(midiEvent); - midiEvent2.Tick = midiEvent.Tick; - midiEvent2.Message = midiEvent.Message; - switch (midiEvent2.Type) - { - case "alphaTab.audio.synth.midi.event.SystemExclusiveEvent": - SystemExclusiveEvent sysex = (SystemExclusiveEvent)midiEvent; - midiEvent2.Data = sysex.Data; - break; - case "alphaTab.audio.synth.midi.event.MetaDataEvent": - MetaDataEvent metadata = (MetaDataEvent)midiEvent; - midiEvent2.Data = metadata.Data; - break; - case "alphaTab.audio.synth.midi.event.MetaNumberEvent": - MetaNumberEvent metanumber = (MetaNumberEvent)midiEvent; - midiEvent2.Value = metanumber.Value; - break; - } - } - - return midi2; - } - } -} diff --git a/Source/AlphaTab.JavaScript/Importer/ScoreLoader.cs b/Source/AlphaTab.JavaScript/Importer/ScoreLoader.cs deleted file mode 100644 index e468dc96c..000000000 --- a/Source/AlphaTab.JavaScript/Importer/ScoreLoader.cs +++ /dev/null @@ -1,125 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.Collections; -using AlphaTab.Model; -using AlphaTab.Platform; -using Haxe.Js.Html; -using Phase; -using Phase.Attributes; - -namespace AlphaTab.Importer -{ - /// - /// The ScoreLoader enables you easy loading of Scores using all - /// available importers - /// - public partial class ScoreLoader - { - /// - /// Loads a score asynchronously from the given datasource - /// - /// the source path to load the binary file from - /// this function is called if the Score was successfully loaded from the datasource - /// this function is called if any error during the loading occured. - /// settings for the score import - public static void LoadScoreAsync(string path, Action success, Action error, Settings settings = null) - { - var xhr = new XMLHttpRequest(); - xhr.Open("GET", path, true); - xhr.ResponseType = XMLHttpRequestResponseType.ARRAYBUFFER; - xhr.OnReadyStateChange = (Action)(() => - { - if (xhr.ReadyState == XMLHttpRequest.DONE) - { - object response = xhr.Response; - if (xhr.Status == 200 || (xhr.Status == 0 && response.IsTruthy())) - { - try - { - ArrayBuffer buffer = xhr.Response; - var reader = new Uint8Array(buffer); - var score = LoadScoreFromBytes(reader.As(), settings); - success(score); - } - catch (Exception exception) - { - error(exception); - } - } - // Error handling - else if (xhr.Status == 0) - { - error(new FileLoadException("You are offline!!\n Please Check Your Network.", xhr)); - } - else if (xhr.Status == 404) - { - error(new FileLoadException("Requested URL not found.", xhr)); - } - else if (xhr.Status == 500) - { - error(new FileLoadException("Internel Server Error.", xhr)); - } - else if (xhr.StatusText == "parsererror") - { - error(new FileLoadException("Error.\nParsing JSON Request failed.", xhr)); - } - else if (xhr.StatusText == "timeout") - { - error(new FileLoadException("Request Time out.", xhr)); - } - else - { - error(new FileLoadException("Unknow Error: " + xhr.ResponseText, xhr)); - } - } - }); - // IE fallback - if (xhr.ResponseType != XMLHttpRequestResponseType.ARRAYBUFFER) - { - // use VB Loader to load binary array - dynamic vbArr = Script.Write("untyped VbAjaxLoader(\"GET\", path)"); - var fileContents = vbArr.toArray(); - - // decode byte array to string - var data = new StringBuilder(); - var i = 0; - while (i < (fileContents.length - 1)) - { - data.Append(((char)(fileContents[i]))); - i++; - } - - var reader = GetBytesFromString(data.ToString()); - var score = LoadScoreFromBytes(reader, settings); - success(score); - } - xhr.Send(); - } - - private static byte[] GetBytesFromString(string s) - { - byte[] b = new byte[s.Length]; - for (int i = 0; i < s.Length; i++) - { - b[i] = (byte)s[i]; - } - return b; - } - } -} diff --git a/Source/AlphaTab.JavaScript/Platform/JavaScript/AlphaSynthFlashOutput.cs b/Source/AlphaTab.JavaScript/Platform/JavaScript/AlphaSynthFlashOutput.cs deleted file mode 100644 index f3c6803e6..000000000 --- a/Source/AlphaTab.JavaScript/Platform/JavaScript/AlphaSynthFlashOutput.cs +++ /dev/null @@ -1,164 +0,0 @@ -using System; -using AlphaTab.Audio.Synth; -using AlphaTab.Audio.Synth.Ds; -using AlphaTab.Collections; -using AlphaTab.Haxe.Js; -using AlphaTab.Haxe.Js.Html; -using AlphaTab.Util; -using Haxe.Js.Html; -using Phase; -using Phase.Attributes; - -namespace AlphaTab.Platform.JavaScript -{ - // NOTE: we prefix all ISynthOutput methods with "AlphaSynth" to ensure - // the ExternalInterface callbacks are called (play, stop etc. might control. the main movie) - interface IFlashSynthOutput - { - void AlphaSynthSequencerFinished(); - void AlphaSynthPlay(); - void AlphaSynthPause(); - void AlphaSynthResetSamples(); - void AlphaSynthAddSamples(string base64Samples); - } - - class AlphaSynthFlashOutput : ISynthOutput - { - public const int PreferredSampleRate = 44100; - - private readonly string _alphaSynthRoot; - private const string Id = "alphaSynthFlashPlayer"; - private static readonly FastDictionary Lookup; - - static AlphaSynthFlashOutput() - { - Lookup = new FastDictionary(); - } - - private static int NextId; - - private string _id; - private string _swfId; - private Element _swfContainer; - - public int SampleRate - { - get { return PreferredSampleRate; } - } - - public AlphaSynthFlashOutput(string alphaSynthRoot) - { - _alphaSynthRoot = alphaSynthRoot; - - var lastSlash = _alphaSynthRoot.LastIndexOf("/"); - if (lastSlash != -1) - { - _alphaSynthRoot = _alphaSynthRoot.Substring(0, lastSlash + 1); - } - } - - public void Open() - { - _id = Id + NextId; - _swfId = _id + "swf"; - Lookup[_id] = this; - NextId++; - - var document = Browser.Document; - _swfContainer = document.CreateElement("div"); - _swfContainer.ClassName = Id; - _swfContainer.SetAttribute("id", _id); - document.Body.AppendChild(_swfContainer); - - var swf = Lib.Global.swfobject; - if (swf) - { - Action embedSwf = swf.embedSWF; - embedSwf( - _alphaSynthRoot + "AlphaSynth.FlashOutput.swf", - _id, "1px", "1px", "9.0.0", - null, - new { id = _id, sampleRate = PreferredSampleRate }, new { allowScriptAccess = "always" }, new { id = _swfId } - ); - } - else - { - Logger.Error("Player", "swfobject not found, player will not work"); - } - } - - private IFlashSynthOutput FlashOutput - { - [Inline] - get - { - var element = Browser.Document.GetElementById(_swfId); - return Script.Write("untyped __js__(\"{0}\", element)"); - } - } - - public void Play() - { - FlashOutput.AlphaSynthPlay(); - } - - public void Pause() - { - FlashOutput.AlphaSynthPause(); - } - - public void SequencerFinished() - { - FlashOutput.AlphaSynthSequencerFinished(); - } - - public void AddSamples(SampleArray samples) - { - var uint8 = new Uint8Array(samples.ToFloat32Array().Buffer); - var b64 = Script.Write("untyped __js__(\"window.btoa(String.fromCharCode.apply(null, {0}))\", uint8)"); - FlashOutput.AlphaSynthAddSamples(b64); - } - - - public void ResetSamples() - { - FlashOutput.AlphaSynthResetSamples(); - } - - public event Action Ready; - public static void OnReady(string id) - { - if (Lookup.ContainsKey(id)) - { - Lookup[id].Ready(); - } - } - - public event Action SampleRequest; - public static void OnSampleRequest(string id) - { - if (Lookup.ContainsKey(id)) - { - Lookup[id].SampleRequest(); - } - } - - public event Action Finished; - public static void OnFinished(string id) - { - if (Lookup.ContainsKey(id) && Lookup[id].Finished != null) - { - Lookup[id].Finished(); - } - } - - public event Action SamplesPlayed; - public static void OnSamplesPlayed(string id, int samples) - { - if (Lookup.ContainsKey(id)) - { - Lookup[id].SamplesPlayed(samples); - } - } - } -} diff --git a/Source/AlphaTab.JavaScript/Platform/JavaScript/AlphaSynthWebAudioOutput.cs b/Source/AlphaTab.JavaScript/Platform/JavaScript/AlphaSynthWebAudioOutput.cs deleted file mode 100644 index 7faa813a9..000000000 --- a/Source/AlphaTab.JavaScript/Platform/JavaScript/AlphaSynthWebAudioOutput.cs +++ /dev/null @@ -1,179 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using AlphaTab.Audio.Synth; -using AlphaTab.Audio.Synth.Ds; -using AlphaTab.Haxe.Js; -using AlphaTab.Haxe.Js.Html; -using AlphaTab.Haxe.Js.Html.Audio; - -namespace AlphaTab.Platform.JavaScript -{ - /// - /// This class implements a HTML5 Web Audio API based audio output device - /// for alphaSynth. It can be controlled via a JS API. - /// - class AlphaSynthWebAudioOutput : ISynthOutput - { - private const int BufferSize = 4096; - private const int BufferCount = 10; - - private AudioContext _context; - private AudioBuffer _buffer; - private AudioBufferSourceNode _source; - private ScriptProcessorNode _audioNode; - - private CircularSampleBuffer _circularBuffer; - - private bool _finished; - - public int SampleRate - { - get { return (int)_context.SampleRate; } - } - - public void Open() - { - _finished = false; - - _circularBuffer = new CircularSampleBuffer(BufferSize * BufferCount); - _context = new AudioContext(); - - // possible fix for Web Audio in iOS 9 (issue #4) - dynamic ctx = _context; - if (ctx.state == "suspended") - { - Action resume = null; - resume = e => - { - ctx.resume(); - Browser.Window.SetTimeout((Action)(() => - { - if (ctx.state == "running") - { - Browser.Document.Body.RemoveEventListener("touchend", resume, false); - } - }), 0); - }; - Browser.Document.Body.AddEventListener("touchend", resume, false); - } - - Ready(); - } - - public void Play() - { - dynamic ctx = _context; - if (ctx.state == "suspended" || ctx.state == "interrupted") - { - ctx.resume(); - } - - // create an empty buffer source (silence) - _buffer = _context.CreateBuffer(2, BufferSize, _context.SampleRate); - - // create a script processor node which will replace the silence with the generated audio - _audioNode = _context.CreateScriptProcessor(BufferSize, 0, 2); - _audioNode.OnAudioProcess = (Action)GenerateSound; - - _circularBuffer.Clear(); - - RequestBuffers(); - _finished = false; - _source = _context.CreateBufferSource(); - _source.Buffer = _buffer; - _source.Loop = true; - _source.Connect(_audioNode, 0, 0); - _source.Start(0); - _audioNode.Connect(_context.Destination, 0, 0); - } - - public void Pause() - { - if (_source != null) - { - _source.Stop(0); - _source.Disconnect(0); - } - _source = null; - if (_audioNode != null) - { - _audioNode.Disconnect(0); - } - _audioNode = null; - } - - public void SequencerFinished() - { - _finished = true; - } - - - public void AddSamples(SampleArray f) - { - _circularBuffer.Write(f, 0, f.Length); - } - - public void ResetSamples() - { - _circularBuffer.Clear(); - } - - private void RequestBuffers() - { - // if we fall under the half of buffers - // we request one half - const int count = (BufferCount / 2) * BufferSize; - if (_circularBuffer.Count < count && SampleRequest != null) - { - for (int i = 0; i < BufferCount / 2; i++) - { - SampleRequest(); - } - } - } - - private void GenerateSound(AudioProcessingEvent e) - { - var left = e.OutputBuffer.GetChannelData(0); - var right = e.OutputBuffer.GetChannelData(1); - var samples = left.Length + right.Length; - if (_circularBuffer.Count < samples) - { - if (_finished) - { - Finished(); - } - } - else - { - var buffer = new SampleArray(samples); - _circularBuffer.Read(buffer, 0, buffer.Length); - - var s = 0; - for (int i = 0; i < left.Length; i++) - { - left[i] = buffer[s++]; - right[i] = buffer[s++]; - } - - SamplesPlayed(left.Length); - } - - - - if (!_finished) - { - RequestBuffers(); - } - } - - - public event Action Ready; - public event Action SamplesPlayed; - public event Action SampleRequest; - public event Action Finished; - } -} diff --git a/Source/AlphaTab.JavaScript/Platform/JavaScript/AlphaSynthWebWorker.cs b/Source/AlphaTab.JavaScript/Platform/JavaScript/AlphaSynthWebWorker.cs deleted file mode 100644 index 0420faa78..000000000 --- a/Source/AlphaTab.JavaScript/Platform/JavaScript/AlphaSynthWebWorker.cs +++ /dev/null @@ -1,271 +0,0 @@ -using System; -using AlphaTab.Audio.Synth; -using AlphaTab.Audio.Synth.Midi; -using AlphaTab.Audio.Synth.Synthesis; -using AlphaTab.Haxe; -using AlphaTab.Haxe.Js; -using AlphaTab.Haxe.Js.Html; -using AlphaTab.Model; -using AlphaTab.Util; -using Phase; - -namespace AlphaTab.Platform.JavaScript -{ - /// - /// This class implements a HTML5 WebWorker based version of alphaSynth - /// which can be controlled via WebWorker messages. - /// - class AlphaSynthWebWorker - { - #region Commands - - public const string CmdPrefix = "alphaSynth."; - - // Main -> Worker - public const string CmdInitialize = CmdPrefix + "initialize"; - - public const string CmdSetLogLevel = CmdPrefix + "setLogLevel"; - public const string CmdSetMasterVolume = CmdPrefix + "setMasterVolume"; - public const string CmdSetMetronomeVolume = CmdPrefix + "setMetronomeVolume"; - public const string CmdSetPlaybackSpeed = CmdPrefix + "setPlaybackSpeed"; - public const string CmdSetTickPosition = CmdPrefix + "setTickPosition"; - public const string CmdSetTimePosition = CmdPrefix + "setTimePosition"; - public const string CmdSetPlaybackRange = CmdPrefix + "setPlaybackRange"; - public const string CmdSetIsLooping = CmdPrefix + "setIsLooping"; - - public const string CmdPlay = CmdPrefix + "play"; - public const string CmdPause = CmdPrefix + "pause"; - public const string CmdPlayPause = CmdPrefix + "playPause"; - public const string CmdStop = CmdPrefix + "stop"; - public const string CmdLoadSoundFontBytes = CmdPrefix + "loadSoundFontBytes"; - public const string CmdLoadMidi = CmdPrefix + "loadMidi"; - public const string CmdSetChannelMute = CmdPrefix + "setChannelMute"; - public const string CmdSetChannelSolo = CmdPrefix + "setChannelSolo"; - public const string CmdSetChannelVolume = CmdPrefix + "setChannelVolume"; - public const string CmdSetChannelProgram = CmdPrefix + "setChannelProgram"; - public const string CmdResetChannelStates = CmdPrefix + "resetChannelStates"; - - // Worker -> Main - public const string CmdReady = CmdPrefix + "ready"; - public const string CmdReadyForPlayback = CmdPrefix + "readyForPlayback"; - public const string CmdPositionChanged = CmdPrefix + "positionChanged"; - public const string CmdPlayerStateChanged = CmdPrefix + "playerStateChanged"; - public const string CmdFinished = CmdPrefix + "finished"; - public const string CmdSoundFontLoaded = CmdPrefix + "soundFontLoaded"; - public const string CmdSoundFontLoadFailed = CmdPrefix + "soundFontLoadFailed"; - public const string CmdMidiLoaded = CmdPrefix + "midiLoaded"; - public const string CmdMidiLoadFailed = CmdPrefix + "midiLoadFailed"; - public const string CmdLog = CmdPrefix + "log"; - - #endregion - - private readonly Audio.Synth.AlphaSynth _player; - private readonly DedicatedWorkerGlobalScope _main; - - public AlphaSynthWebWorker(DedicatedWorkerGlobalScope main, string id) - { - _main = main; - _main.AddEventListener("message", (Action)HandleMessage); - - _player = new Audio.Synth.AlphaSynth(new AlphaSynthWorkerSynthOutput()); - _player.PositionChanged += OnPositionChanged; - _player.PlayerStateChanged += OnPlayerStateChanged; - _player.Finished += OnFinished; - _player.SoundFontLoaded += OnSoundFontLoaded; - _player.SoundFontLoadFailed += OnSoundFontLoadFailed; - _player.SoundFontLoadFailed += OnSoundFontLoadFailed; - _player.MidiLoaded += OnMidiLoaded; - _player.MidiLoadFailed += OnMidiLoadFailed; - _player.ReadyForPlayback += OnReadyForPlayback; - - _main.PostMessage(new { cmd = CmdReady }); - } - - public static void Init() - { - DedicatedWorkerGlobalScope main = Lib.Global; - main.AddEventListener("message", (Action)(e => - { - var data = e.Data; - string cmd = data.cmd; - switch (cmd) - { - case CmdInitialize: - AlphaSynthWorkerSynthOutput.PreferredSampleRate = data.sampleRate; - Logger.LogLevel = data.logLevel; - new AlphaSynthWebWorker(main, data.id); - break; - } - })); - } - - public void HandleMessage(MessageEvent e) - { - var data = e.Data; - string cmd = data.cmd; - switch (cmd) - { - case CmdSetLogLevel: - Logger.LogLevel = data.value; - break; - case CmdSetMasterVolume: - _player.MasterVolume = data.value; - break; - case CmdSetMetronomeVolume: - _player.MetronomeVolume = data.value; - break; - case CmdSetPlaybackSpeed: - _player.PlaybackSpeed = data.value; - break; - case CmdSetTickPosition: - _player.TickPosition = data.value; - break; - case CmdSetTimePosition: - _player.TimePosition = data.value; - break; - case CmdSetPlaybackRange: - _player.PlaybackRange = data.value; - break; - case CmdSetIsLooping: - _player.IsLooping = data.value; - break; - case CmdPlay: - _player.Play(); - break; - case CmdPause: - _player.Pause(); - break; - case CmdPlayPause: - _player.PlayPause(); - break; - case CmdStop: - _player.Stop(); - break; - case CmdLoadSoundFontBytes: - _player.LoadSoundFont(data.data); - break; - case CmdLoadMidi: - _player.LoadMidi(JsonConverter.JsObjectToMidiFile(data.midi)); - break; - case CmdSetChannelMute: - _player.SetChannelMute(data.channel, data.mute); - break; - case CmdSetChannelSolo: - _player.SetChannelSolo(data.channel, data.solo); - break; - case CmdSetChannelVolume: - _player.SetChannelVolume(data.channel, data.volume); - break; - case CmdSetChannelProgram: - _player.SetChannelProgram(data.channel, data.program); - break; - case CmdResetChannelStates: - _player.ResetChannelStates(); - break; - } - } - - - public void OnPositionChanged(PositionChangedEventArgs e) - { - _main.PostMessage(new - { - cmd = CmdPositionChanged, - currentTime = e.CurrentTime, - endTime = e.EndTime, - currentTick = e.CurrentTick, - endTick = e.EndTick - }); - } - - public void OnPlayerStateChanged(PlayerStateChangedEventArgs e) - { - _main.PostMessage(new - { - cmd = CmdPlayerStateChanged, - state = e.State - }); - } - - public void OnFinished(bool isLooping) - { - _main.PostMessage(new - { - cmd = CmdFinished, - isLooping = isLooping - }); - } - - public void OnSoundFontLoaded() - { - _main.PostMessage(new - { - cmd = CmdSoundFontLoaded - }); - } - - public void OnSoundFontLoadFailed(Exception e) - { - _main.PostMessage(new - { - cmd = CmdSoundFontLoadFailed, - error = SerializeException(e) - }); - } - - private object SerializeException(Exception e) - { - dynamic error = Json.Parse(Json.Stringify(e)); - dynamic e2 = e; - if (e2.message) - { - error.message = e2.message; - } - if (e2.stack) - { - error.stack = e2.stack; - } - if (e2.constructor && e2.constructor.name) - { - error.type = e2.constructor.name; - } - - return error; - } - - public void OnMidiLoaded() - { - _main.PostMessage(new - { - cmd = CmdMidiLoaded - }); - } - - public void OnMidiLoadFailed(Exception e) - { - _main.PostMessage(new - { - cmd = CmdMidiLoaded, - error = SerializeException(e) - }); - } - - public void OnReadyForPlayback() - { - _main.PostMessage(new - { - cmd = CmdReadyForPlayback - }); - } - - public void SendLog(LogLevel level, string s) - { - _main.PostMessage(new - { - cmd = CmdLog, - level = level, - message = s - }); - } - } -} diff --git a/Source/AlphaTab.JavaScript/Platform/JavaScript/AlphaSynthWebWorkerApi.cs b/Source/AlphaTab.JavaScript/Platform/JavaScript/AlphaSynthWebWorkerApi.cs deleted file mode 100644 index 1816cb0b6..000000000 --- a/Source/AlphaTab.JavaScript/Platform/JavaScript/AlphaSynthWebWorkerApi.cs +++ /dev/null @@ -1,450 +0,0 @@ -using System; -using AlphaTab.Audio.Synth; -using AlphaTab.Audio.Synth.Ds; -using AlphaTab.Audio.Synth.Midi; -using AlphaTab.Audio.Synth.Synthesis; -using AlphaTab.Audio.Synth.Util; -using AlphaTab.Collections; -using AlphaTab.Haxe.Js.Html; -using AlphaTab.Model; -using AlphaTab.Util; -using Haxe; -using Haxe.Js.Html; -using Phase; - -namespace AlphaTab.Platform.JavaScript -{ - /// - /// a WebWorker based alphaSynth which uses the given player as output. - /// - class AlphaSynthWebWorkerApi : IAlphaSynth - { - private readonly Worker _synth; - private readonly ISynthOutput _output; - - private readonly FastDictionary> _events; - - private bool _workerIsReadyForPlayback; - private bool _workerIsReady; - private bool _outputIsReady; - private PlayerState _state; - private float _masterVolume; - private float _metronomeVolume; - private double _playbackSpeed; - private int _tickPosition; - private double _timePosition; - private bool _isLooping; - private PlaybackRange _playbackRange; - - /// - public bool IsReady - { - get { return _workerIsReady && _outputIsReady; } - } - - /// - public bool IsReadyForPlayback - { - get { return _workerIsReadyForPlayback; } - } - - /// - public PlayerState State - { - get { return _state; } - } - - /// - public LogLevel LogLevel - { - get { return Logger.LogLevel; } - set - { - Logger.LogLevel = value; - _synth.PostMessage(new { cmd = AlphaSynthWebWorker.CmdSetLogLevel, value = value }); - } - } - - /// - public float MasterVolume - { - get { return _masterVolume; } - set - { - value = SynthHelper.ClampF(value, SynthConstants.MinVolume, SynthConstants.MaxVolume); - _masterVolume = value; - _synth.PostMessage(new { cmd = AlphaSynthWebWorker.CmdSetMasterVolume, value = value }); - } - } - - /// - public float MetronomeVolume - { - get { return _metronomeVolume; } - set - { - value = SynthHelper.ClampF(value, SynthConstants.MinVolume, SynthConstants.MaxVolume); - _metronomeVolume = value; - _synth.PostMessage(new { cmd = AlphaSynthWebWorker.CmdSetMetronomeVolume, value = value }); - } - } - - /// - public double PlaybackSpeed - { - get { return _playbackSpeed; } - set - { - value = SynthHelper.ClampD(value, SynthConstants.MinPlaybackSpeed, SynthConstants.MaxPlaybackSpeed); - _playbackSpeed = value; - _synth.PostMessage(new { cmd = AlphaSynthWebWorker.CmdSetPlaybackSpeed, value = value }); - } - } - - /// - public int TickPosition - { - get { return _tickPosition; } - set - { - if (value < 0) - { - value = 0; - } - _tickPosition = value; - _synth.PostMessage(new { cmd = AlphaSynthWebWorker.CmdSetTickPosition, value = value }); - } - } - - /// - public double TimePosition - { - get { return _timePosition; } - set - { - if (value < 0) - { - value = 0; - } - _timePosition = value; - _synth.PostMessage(new { cmd = AlphaSynthWebWorker.CmdSetTimePosition, value = value }); - } - } - - /// - public bool IsLooping - { - get { return _isLooping; } - set - { - _isLooping = value; - _synth.PostMessage(new { cmd = AlphaSynthWebWorker.CmdSetIsLooping, value = value }); - } - } - - /// - public PlaybackRange PlaybackRange - { - get { return _playbackRange; } - set - { - if (value != null) - { - if (value.StartTick < 0) - { - value.StartTick = 0; - } - if (value.EndTick < 0) - { - value.EndTick = 0; - } - } - _playbackRange = value; - _synth.PostMessage(new { cmd = AlphaSynthWebWorker.CmdSetPlaybackRange, value = value }); - } - } - - public AlphaSynthWebWorkerApi(ISynthOutput player, string alphaSynthScriptFile, LogLevel logLevel) - { - _output = player; - _output.Ready += OnOutputReady; - _output.SamplesPlayed += OnOutputSamplesPlayed; - _output.SampleRequest += OnOutputSampleRequest; - _output.Finished += OnOutputFinished; - - _events = new FastDictionary>(); - - _output.Open(); - - try - { - _synth = new Worker(alphaSynthScriptFile); - } - catch - { - // fallback to blob worker - try - { - HaxeString script = "importScripts('" + alphaSynthScriptFile + "')"; - var blob = new Blob(Script.Write("[ script ]")); - _synth = new Worker(URL.CreateObjectURL(blob)); - } - catch (Exception e) - { - Logger.Error("AlphaSynth", "Failed to create WebWorker: " + e); - // TODO: fallback to synchronous mode - } - } - - _synth.AddEventListener("message", (Action)HandleWorkerMessage, false); - - _synth.PostMessage(new - { - cmd = AlphaSynthWebWorker.CmdInitialize, - sampleRate = _output.SampleRate, - logLevel = logLevel - }); - - MasterVolume = 1; - PlaybackSpeed = 1; - MetronomeVolume = 0; - } - - // - // API communicating with the web worker - - /// - public void Play() - { - _synth.PostMessage(new { cmd = AlphaSynthWebWorker.CmdPlay }); - } - - /// - public void Pause() - { - _synth.PostMessage(new { cmd = AlphaSynthWebWorker.CmdPause }); - } - - /// - public void PlayPause() - { - _synth.PostMessage(new { cmd = AlphaSynthWebWorker.CmdPlayPause }); - } - - /// - public void Stop() - { - _synth.PostMessage(new { cmd = AlphaSynthWebWorker.CmdStop }); - } - - /// - public void LoadSoundFont(byte[] data) - { - _synth.PostMessage(new { cmd = AlphaSynthWebWorker.CmdLoadSoundFontBytes, data = data }); - } - - public void LoadSoundFontFromUrl(string data) - { - var url = data.As(); - Logger.Info("AlphaSynth", "Start loading Soundfont from url " + url); - var request = new XMLHttpRequest(); - request.Open("GET", url, true); - request.ResponseType = XMLHttpRequestResponseType.ARRAYBUFFER; - request.OnLoad = (Action)(e => - { - var buffer = new Uint8Array(request.Response); - _synth.PostMessage(new { cmd = AlphaSynthWebWorker.CmdLoadSoundFontBytes, data = buffer.As() }); - }); - request.OnError = (Action)(e => - { - Logger.Error("AlphaSynth", "Loading failed: " + e.Member("message")); - TriggerEvent("soundFontLoadFailed"); - }); - request.OnProgress = (Action)(e => - { - Logger.Debug("AlphaSynth", "Soundfont downloading: " + e.Member("loaded") + "/" + e.Member("total") + " bytes"); - TriggerEvent("soundFontLoad", new object[] {new - { - loaded = e.Member("loaded"), - total = e.Member("total") - }}); - }); - request.Send(); - } - - public void LoadMidiFile(MidiFile midi) - { - _synth.PostMessage(new { cmd = AlphaSynthWebWorker.CmdLoadMidi, midi = JsonConverter.MidiFileToJsObject(midi) }); - } - - /// - public void SetChannelMute(int channel, bool mute) - { - _synth.PostMessage(new { cmd = AlphaSynthWebWorker.CmdSetChannelMute, channel = channel, mute = mute }); - } - - /// - public void ResetChannelStates() - { - _synth.PostMessage(new { cmd = AlphaSynthWebWorker.CmdResetChannelStates }); - } - - /// - public void SetChannelSolo(int channel, bool solo) - { - _synth.PostMessage(new { cmd = AlphaSynthWebWorker.CmdSetChannelSolo, channel = channel, solo = solo }); - } - - /// - public void SetChannelVolume(int channel, double volume) - { - volume = SynthHelper.ClampD(volume, SynthConstants.MinVolume, SynthConstants.MaxVolume); - _synth.PostMessage(new { cmd = AlphaSynthWebWorker.CmdSetChannelVolume, channel = channel, volume = volume }); - } - - /// - public void SetChannelProgram(int channel, byte program) - { - program = SynthHelper.ClampB(program, SynthConstants.MinProgram, SynthConstants.MaxProgram); - _synth.PostMessage(new { cmd = AlphaSynthWebWorker.CmdSetChannelProgram, channel = channel, program = program }); - } - - public virtual void HandleWorkerMessage(MessageEvent e) - { - var data = e.Data; - string cmd = data.cmd; - switch (cmd) - { - case AlphaSynthWebWorker.CmdReady: - _workerIsReady = true; - CheckReady(); - break; - case AlphaSynthWebWorker.CmdReadyForPlayback: - _workerIsReadyForPlayback = true; - CheckReadyForPlayback(); - break; - case AlphaSynthWebWorker.CmdPositionChanged: - _timePosition = data.currentTime; - _tickPosition = data.currentTick; - TriggerEvent("positionChanged", new[] - { - new PositionChangedEventArgs(data.currentTime, data.endTime, data.currentTick, data.endTick) - }); - break; - case AlphaSynthWebWorker.CmdPlayerStateChanged: - _state = data.state; - TriggerEvent("playerStateChanged", new[] - { - new PlayerStateChangedEventArgs(data.state), - }); - break; - case AlphaSynthWebWorker.CmdFinished: - TriggerEvent("finished"); - break; - case AlphaSynthWebWorker.CmdSoundFontLoaded: - TriggerEvent("soundFontLoaded"); - break; - case AlphaSynthWebWorker.CmdSoundFontLoadFailed: - TriggerEvent("soundFontLoadFailed"); - break; - case AlphaSynthWebWorker.CmdMidiLoaded: - CheckReadyForPlayback(); - TriggerEvent("midiFileLoaded", new object[] { data.error }); - break; - case AlphaSynthWebWorker.CmdMidiLoadFailed: - CheckReadyForPlayback(); - TriggerEvent("midiFileLoadFailed", new object[] { data.error }); - break; - case AlphaSynthWebWorker.CmdLog: - Logger.Log(data.level, "AlphaSynth", data.message); - break; - - // output communication ( output <- worker ) - case AlphaSynthWorkerSynthOutput.CmdOutputSequencerFinished: - _output.SequencerFinished(); - break; - case AlphaSynthWorkerSynthOutput.CmdOutputAddSamples: - _output.AddSamples(data.samples); - break; - case AlphaSynthWorkerSynthOutput.CmdOutputPlay: - _output.Play(); - break; - case AlphaSynthWorkerSynthOutput.CmdOutputPause: - _output.Pause(); - break; - case AlphaSynthWorkerSynthOutput.CmdOutputResetSamples: - _output.ResetSamples(); - break; - } - } - - private void CheckReady() - { - if (IsReady) - { - TriggerEvent("ready"); - } - } - - - private void CheckReadyForPlayback() - { - if (IsReadyForPlayback) - { - TriggerEvent("readyForPlayback"); - } - } - - /// - /// Registers for the specified event. - /// - /// The event to register for - /// The function to call on the event. - public void On(string events, Delegate action) - { - if (!_events.ContainsKey(events)) - { - _events[events] = new FastList(); - } - _events[events].Add(action.As()); - } - - private void TriggerEvent(string name, object[] args = null) - { - var events = _events[name]; - if (events != null) - { - for (int i = 0; i < events.Count; i++) - { - var action = events[i]; - Script.Write("untyped __js__(\"{0}.apply(null, args)\", action);"); - } - } - } - - // - // output communication ( output -> worker ) - - - public void OnOutputSampleRequest() - { - _synth.PostMessage(new { cmd = AlphaSynthWorkerSynthOutput.CmdOutputSampleRequest }); - } - - public void OnOutputFinished() - { - _synth.PostMessage(new { cmd = AlphaSynthWorkerSynthOutput.CmdOutputFinished }); - } - - public void OnOutputSamplesPlayed(int samples) - { - _synth.PostMessage(new { cmd = AlphaSynthWorkerSynthOutput.CmdOutputSamplesPlayed, samples = samples }); - } - - private void OnOutputReady() - { - _outputIsReady = true; - CheckReady(); - } - } -} diff --git a/Source/AlphaTab.JavaScript/Platform/JavaScript/AlphaSynthWorkerSynthOutput.cs b/Source/AlphaTab.JavaScript/Platform/JavaScript/AlphaSynthWorkerSynthOutput.cs deleted file mode 100644 index 222eb050a..000000000 --- a/Source/AlphaTab.JavaScript/Platform/JavaScript/AlphaSynthWorkerSynthOutput.cs +++ /dev/null @@ -1,95 +0,0 @@ -using System; -using AlphaTab.Audio.Synth; -using AlphaTab.Audio.Synth.Ds; -using AlphaTab.Haxe.Js; -using AlphaTab.Haxe.Js.Html; -using AlphaTab.Util; - -namespace AlphaTab.Platform.JavaScript -{ - class AlphaSynthWorkerSynthOutput : ISynthOutput - { - public const string CmdOutputPrefix = AlphaSynthWebWorker.CmdPrefix + "output."; - - // Worker -> Output - public const string CmdOutputSequencerFinished = CmdOutputPrefix + "sequencerFinished"; - public const string CmdOutputAddSamples = CmdOutputPrefix + "addSamples"; - public const string CmdOutputPlay = CmdOutputPrefix + "play"; - public const string CmdOutputPause = CmdOutputPrefix + "pause"; - public const string CmdOutputResetSamples = CmdOutputPrefix + "resetSamples"; - - // Output -> Worker - public const string CmdOutputSampleRequest = CmdOutputPrefix + "sampleRequest"; - public const string CmdOutputFinished = CmdOutputPrefix + "finished"; - public const string CmdOutputSamplesPlayed = CmdOutputPrefix + "samplesPlayed"; - - - // this value is initialized by the alphaSynth WebWorker wrapper - // that also includes the alphaSynth library into the worker. - public static int PreferredSampleRate { get; set; } - - private DedicatedWorkerGlobalScope _worker; - - public int SampleRate - { - get { return PreferredSampleRate; } - } - - public void Open() - { - Logger.Debug("AlphaSynth", "Initializing webworker worker"); - _worker = Lib.Global; - _worker.AddEventListener("message", (Action)HandleMessage); - Ready(); - } - - private void HandleMessage(MessageEvent e) - { - var data = e.Data; - var cmd = data.cmd; - switch (cmd) - { - case CmdOutputSampleRequest: - SampleRequest(); - break; - case CmdOutputFinished: - Finished(); - break; - case CmdOutputSamplesPlayed: - SamplesPlayed(data.samples); - break; - } - } - - - public event Action Ready; - public event Action SamplesPlayed; - public event Action SampleRequest; - public event Action Finished; - - public void SequencerFinished() - { - _worker.PostMessage(new { cmd = CmdOutputSequencerFinished }); - } - - public void AddSamples(SampleArray samples) - { - _worker.PostMessage(new { cmd = CmdOutputAddSamples, samples = samples }); - } - - public void Play() - { - _worker.PostMessage(new { cmd = CmdOutputPlay }); - } - - public void Pause() - { - _worker.PostMessage(new { cmd = CmdOutputPause }); - } - - public void ResetSamples() - { - _worker.PostMessage(new { cmd = CmdOutputResetSamples }); - } - } -} diff --git a/Source/AlphaTab.JavaScript/Platform/JavaScript/AlphaTabApi.cs b/Source/AlphaTab.JavaScript/Platform/JavaScript/AlphaTabApi.cs deleted file mode 100644 index 9dca4eca8..000000000 --- a/Source/AlphaTab.JavaScript/Platform/JavaScript/AlphaTabApi.cs +++ /dev/null @@ -1,1608 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.Audio; -using AlphaTab.Audio.Synth; -using AlphaTab.Audio.Synth.Synthesis; -using AlphaTab.Audio.Generator; -using AlphaTab.Audio.Synth.Midi; -using AlphaTab.Collections; -using AlphaTab.Haxe; -using AlphaTab.Haxe.Js; -using AlphaTab.Haxe.Js.Html; -using AlphaTab.Importer; -using AlphaTab.IO; -using AlphaTab.Model; -using AlphaTab.Rendering; -using AlphaTab.Rendering.Glyphs; -using AlphaTab.Rendering.Utils; -using AlphaTab.Util; -using Haxe; -using Haxe.Js.Html; -using Phase; - -namespace AlphaTab.Platform.JavaScript -{ - public class ResizeEventArgs - { - public int OldWidth { get; set; } - public int NewWidth { get; set; } - public Settings Settings { get; set; } - } - - public class AlphaTabApi - { - private readonly Element _canvasElement; - private int _visibilityCheckerInterval; - private int _visibilityCheckerIntervalId; - - private FastList _renderResults; - private int _totalResultCount; - - protected bool IsElementVisible - { - get { return Element.OffsetWidth.IsTruthy() || Element.OffsetHeight.IsTruthy() || Element.GetClientRects().Length.IsTruthy(); } - } - - public Settings Settings { get; private set; } - public IScoreRenderer Renderer { get; private set; } - public Score Score { get; set; } - public int[] TrackIndexes { get; set; } - public Element Element { get; set; } - public Track[] Tracks - { - get - { - var tracks = TrackIndexesToTracks(TrackIndexes); - - if (tracks.Count == 0 && Score.Tracks.Count > 0) - { - tracks.Add(Score.Tracks[0]); - } - - return tracks.ToArray(); - } - } - - public AlphaTabApi(Element element, dynamic options) - { - Element = element; - - Element.ClassList.Add("alphaTab"); - - // load settings - var dataAttributes = GetDataAttributes(); - var settings = Settings = AlphaTab.Settings.FromJson(options, dataAttributes); - var autoSize = settings.Width < 0; - - Logger.LogLevel = settings.LogLevel; - - #region build tracks array - - // get track data to parse - dynamic tracksData; - if (options != null && options.tracks) - { - tracksData = options.tracks; - } - else - { - if (dataAttributes.ContainsKey("tracks")) - { - tracksData = dataAttributes["tracks"]; - } - else - { - tracksData = 0; - } - } - - SetTracks(tracksData, false); - - #endregion - - string contents = ""; - if (element != null) - { - // get load contents - - if (dataAttributes.ContainsKey("tex") && element.InnerText.IsTruthy()) - { - contents = element.InnerHTML; - element.InnerHTML = ""; - contents = contents.Trim(); - } - - #region Create context elements (wrapper, canvas etc) - - _canvasElement = Browser.Document.CreateElement("div"); - - _canvasElement.ClassName = "alphaTabSurface"; - _canvasElement.Style.FontSize = "0"; - _canvasElement.Style.Overflow = "hidden"; - _canvasElement.Style.LineHeight = "0"; - element.AppendChild(_canvasElement); - - #endregion - - #region Setup scroll and resize handlers for lazy-loading - - if (settings.Engine == "default" || settings.Engine == "svg") - { - Browser.Window.AddEventListener("scroll", (Action)(() => - { - ShowSvgsInViewPort(); - }), true); - Browser.Window.AddEventListener("resize", (Action)(() => - { - ShowSvgsInViewPort(); - }), true); - } - - #endregion - - #region Auto Sizing - - if (autoSize) - { - settings.Width = element.OffsetWidth; - int timeoutId = 0; - Browser.Window.AddEventListener("resize", (Action)(() => - { - Browser.Window.ClearTimeout(timeoutId); - timeoutId = Browser.Window.SetTimeout((Action)(() => - { - if (element.OffsetWidth != settings.Width) - { - TriggerResize(); - } - }), 1); - })); - } - - #endregion - } - - #region Renderer Setup - - CreateStyleElement(settings); - - if (element != null && autoSize) - { - var initialResizeEventInfo = new ResizeEventArgs(); - initialResizeEventInfo.OldWidth = 0; - initialResizeEventInfo.NewWidth = element.OffsetWidth; - initialResizeEventInfo.Settings = settings; - TriggerEvent("resize", initialResizeEventInfo); - settings.Width = initialResizeEventInfo.NewWidth; - } - - var workersUnsupported = !Browser.Window.Member("Worker"); - if (settings.UseWebWorker && !workersUnsupported && settings.Engine != "html5") - { - Renderer = new AlphaTabWorkerScoreRenderer(this, settings); - } - else - { - Renderer = new ScoreRenderer(settings); - } - Renderer.RenderFinished += o => TriggerEvent("rendered"); - Renderer.PostRenderFinished += () => - { - Element.ClassList.Remove("loading"); - Element.ClassList.Remove("rendering"); - TriggerEvent("postRendered"); - }; - Renderer.PreRender += result => - { - _renderResults = new FastList(); - _totalResultCount = 0; - AppendRenderResult(result); - }; - Renderer.PartialRenderFinished += AppendRenderResult; - Renderer.RenderFinished += r => - { - AppendRenderResult(r); - AppendRenderResult(null); // marks last element - }; - Renderer.Error += Error; - - #endregion - - if (settings.EnablePlayer) - { - SetupPlayer(); - } - - #region Load Default Data - - Action initialRender = () => - { - // rendering was possibly delayed due to invisible element - // in this case we need the correct width for autosize - if (autoSize) - { - Settings.Width = Element.OffsetWidth; - Renderer.UpdateSettings(settings); - } - - if (!string.IsNullOrEmpty(contents)) - { - Tex(contents); - } - else if (options && options.file) - { - Load(options.file); - } - else if (dataAttributes.ContainsKey("file")) - { - Load(dataAttributes["file"]); - } - }; - - _visibilityCheckerInterval = 500; - if (options && options.visibilityCheckInterval) - { - _visibilityCheckerInterval = options.visibilityCheckInterval; - } - if (IsElementVisible) - { - // element is visible, so we start rendering - initialRender(); - } - else - { - // if the alphaTab element is not visible, we postpone the rendering - // we check in a regular interval whether it became available. - Logger.Warning("Rendering", "AlphaTab container is invisible, checking for element visibility in " + _visibilityCheckerInterval + "ms intervals"); - _visibilityCheckerIntervalId = Browser.Window.SetInterval((Action)(() => - { - if (IsElementVisible) - { - Logger.Info("Rendering", "AlphaTab container became visible, triggering initial rendering"); - initialRender(); - Browser.Window.ClearInterval(_visibilityCheckerIntervalId); - _visibilityCheckerIntervalId = 0; - } - }), _visibilityCheckerInterval); - } - - #endregion - } - - private FastDictionary GetDataAttributes() - { - var dataAttributes = new FastDictionary(); - - if (Element.Dataset.As()) - { - foreach (var key in Platform.JsonKeys(Element.Dataset)) - { - object value = Element.Dataset.Member(key); - try - { - string stringValue = (string)value; - value = Json.Parse(stringValue); - } - catch - { -#pragma warning disable 252,253 - if (value == "") -#pragma warning restore 252,253 - { - value = null; - } - } - dataAttributes[key] = value; - } - } - else - { - for (var i = 0; i < Element.Attributes.Length; i++) - { - var attr = Element.Attributes.Item(i); - string nodeName = attr.NodeName; - if (nodeName.StartsWith("data-")) - { - var keyParts = nodeName.Substring(5).Split('-'); - var key = keyParts[0]; - for (int j = 1; j < keyParts.Length; j++) - { - key += keyParts[j].Substring(0, 1).ToUpper() + keyParts[j].Substring(1); - } - - object value = attr.NodeValue; - try - { - value = Json.Parse(value.As()); - } - catch - { -#pragma warning disable 252, 253 - if (value == "") -#pragma warning restore 252, 253 - { - value = null; - } - } - dataAttributes[key] = value; - } - } - } - - return dataAttributes; - } - - - private void TriggerResize() - { - // if the element is visible, perfect, we do the update - if (IsElementVisible) - { - if (_visibilityCheckerIntervalId != 0) - { - Logger.Info("Rendering", "AlphaTab container became visible again, doing autosizing"); - Browser.Window.ClearInterval(_visibilityCheckerIntervalId); - _visibilityCheckerIntervalId = 0; - } - - var resizeEventInfo = new ResizeEventArgs(); - resizeEventInfo.OldWidth = Settings.Width; - resizeEventInfo.NewWidth = Element.OffsetWidth; - resizeEventInfo.Settings = Settings; - TriggerEvent("resize", resizeEventInfo); - Settings.Width = resizeEventInfo.NewWidth; - Renderer.UpdateSettings(Settings); - Renderer.Resize(Element.OffsetWidth); - } - // if there is no "invisibility timer" we set up one, if there is already a timer scheduled, it will trigger the proper rendering. - else if (_visibilityCheckerIntervalId == 0) - { - Logger.Warning("Rendering", "AlphaTab container was invisible while autosizing, checking for element visibility in " + _visibilityCheckerInterval + "ms intervals"); - _visibilityCheckerIntervalId = Browser.Window.SetInterval((Action)(TriggerResize), _visibilityCheckerInterval); - } - } - - private void ShowSvgsInViewPort() - { - var placeholders = _canvasElement.QuerySelectorAll("[data-lazy=true]"); - for (var i = 0; i < placeholders.Length; i++) - { - var placeholder = (Element)placeholders.Item(i); - if (IsElementInViewPort(placeholder)) - { - placeholder.OuterHTML = placeholder.Member("svg"); - } - } - } - - private static bool IsElementInViewPort(Element el) - { - var rect = el.GetBoundingClientRect(); - return - ( - rect.Top + rect.Height >= 0 && rect.Top <= Browser.Window.InnerHeight && - rect.Left + rect.Width >= 0 && rect.Left <= Browser.Window.InnerWidth - ); - } - - public void Print(string width) - { - // prepare a popup window for printing (a4 width, window height, centered) - - var preview = Browser.Window.Open("", "", "width=0,height=0"); - var a4 = preview.Document.CreateElement("div"); - if (!string.IsNullOrEmpty(width)) - { - a4.Style.Width = width; - } - else - { - if (Settings.Layout.Mode == "horizontal") - { - a4.Style.Width = "297mm"; - } - else - { - a4.Style.Width = "210mm"; - } - } - preview.Document.Write(""); - preview.Document.Body.AppendChild(a4); - - int dualScreenLeft = Platform.TypeOf(Browser.Window.Member("ScreenLeft")) != "undefined" - ? Browser.Window.Member("ScreenLeft") - : (int)Browser.Window.Screen.Left; - int dualScreenTop = Platform.TypeOf(Browser.Window.Member("ScreenTop")) != "undefined" - ? Browser.Window.Member("ScreenTop") - : (int)Browser.Window.Screen.Top; - int screenWidth = Platform.TypeOf(Browser.Window.InnerWidth) != "undefined" - ? Browser.Window.InnerWidth - : Platform.TypeOf(Browser.Document.DocumentElement.ClientWidth) != "undefined" - ? Browser.Document.DocumentElement.ClientWidth - : Browser.Window.Screen.Width; - int screenHeight = Platform.TypeOf(Browser.Window.InnerHeight) != "undefined" - ? Browser.Window.InnerHeight - : Platform.TypeOf(Browser.Document.DocumentElement.ClientHeight) != "undefined" - ? Browser.Document.DocumentElement.ClientHeight - : Browser.Window.Screen.Height; - - int w = a4.OffsetWidth + 50; - var h = (int)Browser.Window.InnerHeight; - int left = ((screenWidth / 2) - (w / 2)) + dualScreenLeft; - int top = ((screenHeight / 2) - (h / 2)) + dualScreenTop; - preview.ResizeTo(w, h); - preview.MoveTo(left, top); - - preview.Focus(); - - // render alphaTab - var settings = Settings.Defaults; - settings.ScriptFile = Settings.ScriptFile; - settings.FontDirectory = Settings.FontDirectory; - settings.Scale = 0.8f; - settings.StretchForce = 0.8f; - settings.DisableLazyLoading = true; - settings.UseWebWorker = false; - - var alphaTab = new AlphaTabApi(a4, settings); - alphaTab.Renderer.PostRenderFinished += () => - { - alphaTab._canvasElement.Style.Height = "100%"; - preview.Print(); - }; - alphaTab.SetTracks(Tracks); - } - - private void AppendRenderResult(RenderFinishedEventArgs result) - { - if (result != null) - { - _canvasElement.Style.Width = result.TotalWidth + "px"; - _canvasElement.Style.Height = result.TotalHeight + "px"; - } - - - if (result == null || result.RenderResult != null) - { - // the queue/dequeue like mechanism used here is to maintain the order within the setTimeout. - // setTimeout allows to decouple the rendering from the JS processing a bit which makes the overall display faster. - _renderResults.Add(result); - - Browser.Window.SetTimeout((Action)(() => - { - while (_renderResults.Count > 0) - { - var renderResult = _renderResults[0]; - _renderResults.RemoveAt(0); - - // null result indicates that the rendering finished - if (renderResult == null) - { - // so we remove elements that might be from a previous render session - while (_canvasElement.ChildElementCount > _totalResultCount) - { - _canvasElement.RemoveChild(_canvasElement.LastChild); - } - } - // NOTE: here we try to replace existing children - else - { - var body = renderResult.RenderResult; - if (Platform.TypeOf(body) == "string") - { - Element placeholder; - if (_totalResultCount < _canvasElement.ChildElementCount) - { - placeholder = (Element)_canvasElement.ChildNodes.Item(_totalResultCount); - } - else - { - placeholder = Browser.Document.CreateElement("div"); - _canvasElement.AppendChild(placeholder); - } - - placeholder.Style.Width = renderResult.Width + "px"; - placeholder.Style.Height = renderResult.Height + "px"; - placeholder.Style.Display = "inline-block"; - - if (IsElementInViewPort(placeholder) || Settings.DisableLazyLoading) - { - string bodyHtml = (string)body; - placeholder.OuterHTML = bodyHtml; - } - else - { - - placeholder.Member("svg", body); - placeholder.SetAttribute("data-lazy", "true"); - } - } - else - { - if (_totalResultCount < _canvasElement.ChildElementCount) - { - _canvasElement.ReplaceChild(renderResult.RenderResult.As(), _canvasElement.ChildNodes.Item(_totalResultCount)); - } - else - { - _canvasElement.AppendChild(renderResult.RenderResult.As()); - } - } - _totalResultCount++; - } - } - }), 1); - - } - } - - private void CreateStyleElement(Settings settings) - { - var elementDocument = Element.OwnerDocument; - var styleElement = (StyleElement)elementDocument.GetElementById("alphaTabStyle"); - if (styleElement == null) - { - string fontDirectory = settings.FontDirectory; - styleElement = (StyleElement)elementDocument.CreateElement("style"); - styleElement.Id = "alphaTabStyle"; - styleElement.Type = "text/css"; - var css = new StringBuilder(); - css.AppendLine("@font-face {"); - css.AppendLine(" font-family: 'alphaTab';"); - css.AppendLine(" src: url('" + fontDirectory + "Bravura.eot');"); - css.AppendLine(" src: url('" + fontDirectory + "Bravura.eot?#iefix') format('embedded-opentype')"); - css.AppendLine(" , url('" + fontDirectory + "Bravura.woff') format('woff')"); - css.AppendLine(" , url('" + fontDirectory + "Bravura.otf') format('opentype')"); - css.AppendLine(" , url('" + fontDirectory + "Bravura.svg#Bravura') format('svg');"); - css.AppendLine(" font-weight: normal;"); - css.AppendLine(" font-style: normal;"); - css.AppendLine("}"); - css.AppendLine(".alphaTabSurface * {"); - css.AppendLine(" cursor: default;"); - css.AppendLine("}"); - css.AppendLine(".at {"); - css.AppendLine(" font-family: 'alphaTab';"); - css.AppendLine(" speak: none;"); - css.AppendLine(" font-style: normal;"); - css.AppendLine(" font-weight: normal;"); - css.AppendLine(" font-variant: normal;"); - css.AppendLine(" text-transform: none;"); - css.AppendLine(" line-height: 1;"); - css.AppendLine(" line-height: 1;"); - css.AppendLine(" -webkit-font-smoothing: antialiased;"); - css.AppendLine(" -moz-osx-font-smoothing: grayscale;"); - css.AppendLine(" font-size: 34px;"); - css.AppendLine(" overflow: visible !important;"); - css.AppendLine("}"); - styleElement.InnerHTML = css.ToString(); - elementDocument.GetElementsByTagName("head").Item(0).AppendChild(styleElement); - Environment.CheckForFontAvailability(); - } - } - - - public virtual void Destroy() - { - Element.InnerHTML = ""; - Renderer.Destroy(); - } - - public void Load(object data) - { - Element.ClassList.Add("loading"); - try - { - if (Platform.InstanceOf(data)) - { - ScoreLoaded(ScoreLoader.LoadScoreFromBytes(Platform.ArrayBufferToByteArray((ArrayBuffer)data), Settings)); - } - else if (Platform.InstanceOf(data)) - { - ScoreLoaded(ScoreLoader.LoadScoreFromBytes((byte[])data, Settings)); - } - else if (Platform.TypeOf(data) == "string") - { - ScoreLoader.LoadScoreAsync((string)data, s => ScoreLoaded(s), e => - { - Error("import", e); - }, Settings); - } - } - catch (Exception e) - { - Error("import", e); - } - } - - - public void Tex(string contents) - { - Element.ClassList.Add("loading"); - try - { - var parser = new AlphaTexImporter(); - var data = ByteBuffer.FromBuffer(Platform.StringToByteArray(contents)); - parser.Init(data, Settings); - ScoreLoaded(parser.ReadScore()); - } - catch (Exception e) - { - Error("import", e); - } - } - - public void SetTracks(dynamic tracksData, bool render = true) - { - if (tracksData.length && Platform.TypeOf(tracksData[0].Index) == "number") - { - Score = tracksData[0].Score; - } - else if (Platform.TypeOf(tracksData.Index) == "number") - { - Score = tracksData.Score; - } - - TrackIndexes = ParseTracks(tracksData); - - if (render) - { - Render(); - } - } - - public FastList TrackIndexesToTracks(int[] trackIndexes) - { - var tracks = new FastList(); - - foreach (var track in trackIndexes) - { - if (track >= 0 && track < Score.Tracks.Count) - { - tracks.Add(Score.Tracks[track]); - } - } - - return tracks; - } - - public int[] ParseTracks(dynamic tracksData) - { - FastList tracks = new FastList(); - - // decode string - if (Platform.TypeOf(tracksData) == "string") - { - try - { - tracksData = Json.Parse(tracksData.As()); - } - catch - { - tracksData = new[] { 0 }; - } - } - - // decode array - if (Platform.TypeOf(tracksData) == "number") - { - tracks.Add((int)tracksData); - } - else if (tracksData.length) - { - for (var i = 0; i < tracksData.length; i++) - { - int value; - if (Platform.TypeOf(tracksData[i]) == "number") - { - value = (int)tracksData[i]; - } - else if (Platform.TypeOf(tracksData[i].Index) == "number") - { - Track track = tracksData[i]; - value = track.Index; - } - else - { - value = Platform.ParseInt(tracksData[i].ToString()); - } - - if (value >= 0) - { - tracks.Add(value); - } - } - } - else if (Platform.TypeOf(tracksData.Index) == "number") - { - tracks.Add(tracksData.Index.As()); - } - - return tracks.ToArray(); - } - - public void ScoreLoaded(Score score, bool render = true) - { - ModelUtils.ApplyPitchOffsets(Settings, score); - - Score = score; - LoadMidiForScore(); - - TriggerEvent("loaded", score); - if (render) - { - Render(); - } - } - - - public void Error(string type, Exception details) - { - Logger.Error(type, "An unexpected error occurred", details); - TriggerEvent("error", new - { - type = type, - details = details - }); - } - - public void TriggerEvent(string name, object details = null) - { - if (Element != null) - { - name = "alphaTab." + name; - dynamic e = Browser.Document.CreateEvent("CustomEvent"); - e.initCustomEvent(name, false, false, details); - Element.DispatchEvent(e); - - if (Platform.JsonExists(Browser.Window, "jQuery")) - { - dynamic jquery = Browser.Window.Member("jQuery"); - jquery(Element).trigger(name, details); - } - } - } - - public void Render() - { - if (Renderer == null) return; - Action renderAction = null; - renderAction = () => - { - // if font is not yet loaded, try again in 1 sec - if (!Environment.IsFontLoaded && Settings.Engine == "html5") - { - // TODO: trigger event in environment. - Browser.Window.SetTimeout((Action)(() => - { - renderAction(); - }), 500); - } - else - { - // when font is finally loaded, start rendering - Renderer.Render(Score, TrackIndexes); - } - }; - renderAction(); - } - - public void UpdateLayout(object json) - { - Settings.Layout = Settings.LayoutFromJson(json); - Renderer.UpdateSettings(Settings); - Renderer.Invalidate(); - } - - #region Player - - private AlphaSynthWebWorkerApi _player; - - public IAlphaSynth Player { get; set; } - private MidiTickLookup _tickCache; - private Element _cursorWrapper; - private Element _beatCursor; - private Element _barCursor; - private Element _selectionWrapper; - private int _previousTick; - private BoundsLookup _previousCursorCache; - private BoundsLookup _cursorCache; - private PlayerState _playerState; - private SelectionInfo _selectionStart; - private SelectionInfo _selectionEnd; - private bool _selecting; - private Beat _currentBeat; - private PlayerState _previousStateForCursor; - private int _lastScroll; - - /// - /// This method initilaizes the player. It detects browser compatibility and - /// initializes a alphaSynth version for the client. - /// - /// Compatibility: - /// If a browser supports WebWorkers, we will use WebWorkers for Synthesizing the samples and a Flash player for playback - /// If the browser does not support WebWorkers we'll use a pure Flash fallback which requires Flash 11.4 - /// - /// - IE6-9 - Unsupported - /// - IE10-11 - Flash is used for playback, Synthesizing is done in a WebWorker - /// - Firefox - Web Audio API is used for playback, Synthesizing is done in a WebWorker - /// - Chrome - Web Audio API is used for playback, Synthesizing is done in a WebWorker - /// - Safari - Web Audio API is used for playback, Synthesizing is done in a WebWorker - /// - Opera - Web Audio API is used for playback, Synthesizing is done in a WebWorker - /// - private void SetupPlayer() - { - var supportsWebAudio = Platform.SupportsWebAudio; - var supportsWebWorkers = Platform.SupportsWebWorkers; - var forceFlash = Platform.ForceFlash; - var alphaSynthScriptFile = Environment.ScriptFile; - - if (supportsWebAudio && !forceFlash) - { - Logger.Info("Player", "Will use webworkers for synthesizing and web audio api for playback"); - _player = new AlphaSynthWebWorkerApi(new AlphaSynthWebAudioOutput(), alphaSynthScriptFile, Settings.LogLevel); - } - else if (supportsWebWorkers) - { - Logger.Info("Player", "Will use webworkers for synthesizing and flash for playback"); - _player = new AlphaSynthWebWorkerApi(new AlphaSynthFlashOutput(alphaSynthScriptFile), alphaSynthScriptFile, Settings.LogLevel); - } - - Player = _player; - - if (_player == null) - { - Logger.Error("Player", "Player requires webworkers and web audio api or flash, browser unsupported"); - } - else - { - _player.On("ready", (Action)(() => - { - LoadSoundFont(Settings.SoundFontFile); - LoadMidiForScore(); - })); - _player.On("readyForPlayback", (Action)(() => - { - TriggerEvent("playerReady"); - })); - _player.On("soundFontLoad", (Action)(data => - { - TriggerEvent("soundFontLoad", data); - })); - _player.On("soundFontLoaded", (Action)(() => - { - TriggerEvent("soundFontLoaded"); - })); - _player.On("soundFontLoadFailed", (Action)(() => - { - TriggerEvent("soundFontLoadFailed"); - })); - - _player.On("midiLoad", (Action)(data => - { - TriggerEvent("midiLoad", data); - })); - _player.On("midiFileLoaded", (Action)(() => - { - TriggerEvent("midiFileLoaded"); - })); - _player.On("midiFileLoadFailed", (Action)(() => - { - TriggerEvent("midiFileLoadFailed"); - })); - _player.On("playerStateChanged", (Action)(data => - { - TriggerEvent("playerStateChanged", data); - })); - _player.On("positionChanged", (Action)(data => - { - TriggerEvent("positionChanged", data); - })); - _player.On("finished", (Action)(data => - { - TriggerEvent("finished", data); - })); - - if (Settings.EnableCursor) - { - SetupCursor(); - } - } - } - - - private void LoadMidiForScore() - { - if (Player == null || Score == null || !_player.IsReady) - { - return; - } - - Logger.Info("AlphaTab", "Generating Midi"); - var midiFile = new MidiFile(); - var handler = new AlphaSynthMidiFileHandler(midiFile); - var generator = new MidiFileGenerator(Score, Settings, handler); - generator.Generate(); - _tickCache = generator.TickLookup; - _player.LoadMidiFile(midiFile); - } - - public void DownloadMidi() - { - var midiFile = new MidiFile(); - var handler = new AlphaSynthMidiFileHandler(midiFile); - var generator = new MidiFileGenerator(Score, Settings, handler); - generator.Generate(); - - var binary = midiFile.ToBinary(); - Uint8Array uint8Array = Script.Write("binary.toUint8Array()"); - var fileName = string.IsNullOrEmpty(Score.Title) ? "File.mid" : Score.Title + ".mid"; - var dlLink = (AnchorElement)Browser.Document.CreateElement("a"); - dlLink.Download = fileName; - - var blob = new Blob(new[] { uint8Array }, new - { - type = "audio/midi" - }); - var url = URL.CreateObjectURL(blob); - dlLink.Href = url; - dlLink.Style.Display = "none"; - Browser.Document.Body.AppendChild(dlLink); - dlLink.Click(); - Browser.Document.Body.RemoveChild(dlLink); - } - - public void SetTrackVolume(object tracks, float volume) - { - if (Player == null) - { - return; - } - - var trackList = TrackIndexesToTracks(ParseTracks(tracks)); - foreach (var track in trackList) - { - Player.SetChannelVolume(track.PlaybackInfo.PrimaryChannel, volume); - Player.SetChannelVolume(track.PlaybackInfo.SecondaryChannel, volume); - } - } - - public void SetTrackSolo(object tracks, bool solo) - { - if (Player == null) - { - return; - } - - var trackList = TrackIndexesToTracks(ParseTracks(tracks)); - foreach (var track in trackList) - { - Player.SetChannelSolo(track.PlaybackInfo.PrimaryChannel, solo); - Player.SetChannelSolo(track.PlaybackInfo.SecondaryChannel, solo); - } - } - - - public void SetTrackMute(object tracks, bool mute) - { - if (Player == null) - { - return; - } - - var trackList = TrackIndexesToTracks(ParseTracks(tracks)); - foreach (var track in trackList) - { - Player.SetChannelMute(track.PlaybackInfo.PrimaryChannel, mute); - Player.SetChannelMute(track.PlaybackInfo.SecondaryChannel, mute); - } - } - - public void LoadSoundFont(object value) - { - if (Player == null) - { - return; - } - - if (Platform.TypeOf(value) == "string") - { - _player.LoadSoundFontFromUrl((string)value); - } - else - { - _player.LoadSoundFont((byte[])value); - } - } - - public void Play() - { - if (Player == null) - { - return; - } - _player.Play(); - } - public void Pause() - { - if (Player == null) - { - return; - } - Player.Pause(); - } - public void PlayPause() - { - if (Player == null) - { - return; - } - Player.PlayPause(); - } - - public void Stop() - { - if (Player == null) - { - return; - } - Player.Stop(); - CursorUpdateTick(0, true); - } - - private void SetupCursor() - { - // - // Create cursors - - var cursorWrapper = Browser.Document.CreateElement("div"); - cursorWrapper.ClassList.Add("cursors"); - - var selectionWrapper = Browser.Document.CreateElement("div"); - selectionWrapper.ClassList.Add("selectionWrapper"); - - var barCursor = Browser.Document.CreateElement("div"); - barCursor.ClassList.Add("barCursor"); - - var beatCursor = Browser.Document.CreateElement("div"); - beatCursor.ClassList.Add("beatCursor"); - - var surface = Element.QuerySelector(".alphaTabSurface"); - - // required css styles - Element.Style.Position = "relative"; - Element.Style.TextAlign = "left"; - - cursorWrapper.Style.Position = "absolute"; - cursorWrapper.Style.ZIndex = "1000"; - cursorWrapper.Style.Display = "inline"; - Script.Write("untyped __js__(\"{0}.pointerEvents = 'none'\", cursorWrapper.style);"); - - selectionWrapper.Style.Position = "absolute"; - barCursor.Style.Position = "absolute"; - beatCursor.Style.Position = "absolute"; - beatCursor.Style.Transition = "all 0s linear"; - - // store options and created elements for fast access - _cursorWrapper = cursorWrapper; - _barCursor = barCursor; - _beatCursor = beatCursor; - _selectionWrapper = selectionWrapper; - - // add cursors to UI - Element.InsertBefore(cursorWrapper, Element.FirstChild); - cursorWrapper.AppendChild(selectionWrapper); - cursorWrapper.AppendChild(barCursor); - cursorWrapper.AppendChild(beatCursor); - - // - // Hook into events - _previousTick = 0; - _playerState = PlayerState.Paused; - // we need to update our position caches if we render a tablature - Renderer.PostRenderFinished += () => - { - _cursorCache = Renderer.BoundsLookup; - CursorUpdateTick(_previousTick); - - var surfaceSite = surface.GetBoundingClientRect(); - cursorWrapper.Style.Width = surfaceSite.Width + "px"; - cursorWrapper.Style.Height = surfaceSite.Height + "px"; - }; - - _player.On("positionChanged", (Action)(data => - { - _previousTick = data.CurrentTick; - Browser.Window.SetTimeout((Action)(() => - { - CursorUpdateTick(data.CurrentTick); - }), 0); // enqueue cursor update for later to return ExternalInterface call in case of Flash - })); - - - _player.On("playerStateChanged", (Action)(data => - { - _playerState = data.State; - Browser.Window.SetTimeout((Action)(() => - { - CursorUpdateTick(_previousTick); - }), 0); // enqueue cursor update for later to return ExternalInterface call in case of Flash - })); - - if (Settings.EnableSeekByClick) - { - SetupClickHandling(); - } - } - - private void SetupClickHandling() - { - _canvasElement.AddEventListener("click", (Action)(e => - { - var parentOffset = GetOffset(_canvasElement); - - var relX = e.PageX - parentOffset.X; - var relY = e.PageY - parentOffset.Y; - var beat = _cursorCache.GetBeatAtPos(relX, relY); - if (beat == null) return; - var note = _cursorCache.GetNoteAtPos(beat, relX, relY); - TriggerEvent("beatClick", new - { - beat = beat, - note = note - }); - })); - _canvasElement.AddEventListener("mousedown", (Action)(e => - { - if (e.Button != 0) - { - return; - } - e.PreventDefault(); - - // https://github.com/nefe/You-Dont-Need-jQuery#2.3 - var parentOffset = GetOffset(_canvasElement); - - var relX = e.PageX - parentOffset.X; - var relY = e.PageY - parentOffset.Y; - var beat = _cursorCache.GetBeatAtPos(relX, relY); - if (beat != null) - { - _selectionStart = new SelectionInfo(beat); - _selectionEnd = null; - _selecting = true; - } - })); - - _canvasElement.AddEventListener("mousemove", (Action)(e => - { - if (!_selecting) return; - var parentOffset = GetOffset(_canvasElement); - var relX = e.PageX - parentOffset.X; - var relY = e.PageY - parentOffset.Y; - var beat = _cursorCache.GetBeatAtPos(relX, relY); - if (beat != null && (_selectionEnd == null || _selectionEnd.Beat != beat)) - { - _selectionEnd = new SelectionInfo(beat); - CursorSelectRange(_selectionStart, _selectionEnd); - } - })); - - _canvasElement.AddEventListener("mouseup", (Action)(e => - { - if (!_selecting) return; - e.PreventDefault(); - - // for the selection ensure start < end - if (_selectionEnd != null) - { - var startTick = _selectionStart.Beat.AbsoluteDisplayStart; - var endTick = _selectionStart.Beat.AbsoluteDisplayStart; - if (endTick < startTick) - { - var t = _selectionStart; - _selectionStart = _selectionEnd; - _selectionEnd = t; - } - } - - if (_selectionStart != null) - { - // get the start and stop ticks (which consider properly repeats) - var tickCache = _tickCache; - var realMasterBarStart = tickCache.GetMasterBarStart(_selectionStart.Beat.Voice.Bar.MasterBar); - - // move to selection start - CursorUpdateBeat(_selectionStart.Beat, null, 0, false); - Player.TickPosition = realMasterBarStart + _selectionStart.Beat.PlaybackStart; - - // set playback range - if (_selectionEnd != null && _selectionStart.Beat != _selectionEnd.Beat) - { - var realMasterBarEnd = tickCache.GetMasterBarStart(_selectionEnd.Beat.Voice.Bar.MasterBar); - Player.PlaybackRange = new PlaybackRange - { - StartTick = realMasterBarStart + _selectionStart.Beat.PlaybackStart, - EndTick = realMasterBarEnd + _selectionEnd.Beat.PlaybackStart + - _selectionEnd.Beat.PlaybackDuration - 50 - }; - } - else - { - _selectionStart = null; - Player.PlaybackRange = null; - CursorSelectRange(_selectionStart, _selectionEnd); - } - } - _selecting = false; - })); - - Renderer.PostRenderFinished += () => - { - if (_selectionStart != null) - { - CursorSelectRange(_selectionStart, _selectionEnd); - } - }; - } - - private Bounds GetOffset(Element element) - { - var bounds = element.GetBoundingClientRect(); - float top = bounds.Top + element.OwnerDocument.DefaultView.PageYOffset; - float left = bounds.Left + element.OwnerDocument.DefaultView.PageXOffset; - return new Bounds - { - X = left, - Y = top, - W = bounds.Width, - H = bounds.Height - }; - } - - /// - /// updates the cursors to highlight the beat at the specified tick position - /// - /// - /// - private void CursorUpdateTick(int tick, bool stop = false) - { - Browser.Window.RequestAnimationFrame(f => - { - var cache = _tickCache; - if (cache != null) - { - var tracks = Tracks; - if (tracks.Length > 0) - { - var beat = cache.FindBeat(tracks, tick); - if (beat != null) - { - CursorUpdateBeat(beat.CurrentBeat, beat.NextBeat, beat.Duration, stop); - } - } - } - }); - } - - /// - /// updates the cursors to highlight the specified beat - /// - private void CursorUpdateBeat(Beat beat, Beat nextBeat, double duration, bool stop) - { - if (beat == null) return; - - var cache = _cursorCache; - if (cache == null) - { - return; - } - - var previousBeat = _currentBeat; - var previousCache = _previousCursorCache; - var previousState = _previousStateForCursor; - _currentBeat = beat; - _previousCursorCache = cache; - _previousStateForCursor = _playerState; - - if (beat == previousBeat && cache == previousCache && previousState == _playerState) - { - return; - } - - - var barCursor = _barCursor; - var beatCursor = _beatCursor; - - var beatBoundings = cache.FindBeat(beat); - if (beatBoundings == null) - { - return; - } - - var barBoundings = beatBoundings.BarBounds.MasterBarBounds; - var barBounds = barBoundings.VisualBounds; - barCursor.Style.Top = barBounds.Y + "px"; - barCursor.Style.Left = barBounds.X + "px"; - barCursor.Style.Width = barBounds.W + "px"; - barCursor.Style.Height = barBounds.H + "px"; - - // move beat to start position immediately - beatCursor.Style.Transition = "none"; - beatCursor.Style.Top = barBounds.Y + "px"; - beatCursor.Style.Left = beatBoundings.VisualBounds.X + "px"; - beatCursor.Style.Width = Settings.BeatCursorWidth + "px"; - beatCursor.Style.Height = barBounds.H + "px"; - - // if playing, animate the cursor to the next beat - var elements = Element.GetElementsByClassName("atHighlight"); - while (elements.Length > 0) - { - elements.Item(0).ClassList.Remove("atHighlight"); - } - - if (_playerState == PlayerState.Playing || stop) - { - duration /= Player.PlaybackSpeed; - - if (!stop) - { - var className = BeatContainerGlyph.GetGroupId(beat); - var elementsToHighlight = Element.GetElementsByClassName(className); - for (int i = 0; i < elementsToHighlight.Length; i++) - { - elementsToHighlight.Item(i).ClassList.Add("atHighlight"); - } - - var nextBeatX = barBoundings.VisualBounds.X + barBoundings.VisualBounds.W; - // get position of next beat on same stavegroup - if (nextBeat != null) - { - // if we are moving within the same bar or to the next bar - // transition to the next beat, otherwise transition to the end of the bar. - if (nextBeat.Voice.Bar.Index == beat.Voice.Bar.Index || - nextBeat.Voice.Bar.Index == beat.Voice.Bar.Index + 1) - { - var nextBeatBoundings = cache.FindBeat(nextBeat); - if (nextBeatBoundings != null && - nextBeatBoundings.BarBounds.MasterBarBounds.StaveGroupBounds == barBoundings.StaveGroupBounds) - { - nextBeatX = nextBeatBoundings.VisualBounds.X; - } - } - } - - Browser.Window.RequestAnimationFrame(f => - { - //Logger.Info("Player", - // "Transition from " + beatBoundings.VisualBounds.X + " to " + nextBeatX + " in " + duration + - // "(" + Player.PlaybackRange + ")"); - beatCursor.Style.Transition = "all 0s linear"; - beatCursor.Style.TransitionDuration = duration + "ms"; - beatCursor.Style.Left = nextBeatX + "px"; - }); - } - - if (!_selecting) - { - // calculate position of whole music wheet within the scroll parent - var scrollElement = Browser.Document.QuerySelector(Settings.ScrollElement); - - var elementOffset = GetOffset(Element); - var nodeName = scrollElement.NodeName.ToLowerCase(); - if (nodeName != "html" && nodeName != "body") - { - var scrollElementOffset = GetOffset(scrollElement); - elementOffset.Y = elementOffset.Y + scrollElement.ScrollTop - scrollElementOffset.Y; - elementOffset.X = elementOffset.X + scrollElement.ScrollLeft - scrollElementOffset.X; - } - else - { - scrollElement = Browser.Document.DocumentElement; - } - - if (Settings.ScrollMode == "vertical") - { - var scrollTop = (int)(elementOffset.Y + barBoundings.RealBounds.Y + Settings.ScrollOffsetY); - if (scrollTop != _lastScroll) - { - _lastScroll = scrollTop; - ScrollToY(scrollElement, scrollTop, Settings.ScrollSpeed); - } - } - else if (Settings.ScrollMode == "horizontal-bar") - { - var x = (int)barBoundings.VisualBounds.X; - if (x != _lastScroll) - { - var scrollLeft = (int)(barBoundings.RealBounds.X + Settings.ScrollOffsetX); - _lastScroll = (int)barBoundings.VisualBounds.X; - ScrollToX(scrollElement, scrollLeft, Settings.ScrollSpeed); - } - } - else if (Settings.ScrollMode == "horizontal-offscreen") - { - var elementRight = scrollElement.ScrollLeft + GetOffset(scrollElement).W; - if ((barBoundings.VisualBounds.X + barBoundings.VisualBounds.W) >= elementRight || barBoundings.VisualBounds.X < scrollElement.ScrollLeft) - { - var scrollLeft = barBoundings.RealBounds.X + Settings.ScrollOffsetX; - _lastScroll = (int)barBoundings.VisualBounds.X; - ScrollToX(scrollElement, (int)scrollLeft, Settings.ScrollSpeed); - } - } - } - - // trigger an event for others to indicate which beat/bar is played - TriggerEvent("playedBeatChanged", beat); - } - } - - private void ScrollToY(DOMElement element, int scrollTargetY, double speed) - { - var startY = element.ScrollTop; - var diff = scrollTargetY - startY; - HaxeFloat start = 0; - - Action step = null; - step = x => - { - if (start == 0) start = x; - - double time = x - start; - var percent = Math.Min(time / speed, 1); - element.ScrollTop = (int)(startY + diff * percent); - - if (time < speed) - { - Browser.Window.RequestAnimationFrame(step); - } - }; - Browser.Window.RequestAnimationFrame(step); - } - - private void ScrollToX(DOMElement element, int scrollTargetX, int speed) - { - var startX = element.ScrollLeft; - var diff = scrollTargetX - startX; - HaxeFloat start = 0; - - Action step = null; - step = t => - { - if (start == 0) start = t; - - double time = t - start; - var percent = Math.Min(time / speed, 1); - element.ScrollLeft = (int)(startX + diff * percent); - - if (time < speed) - { - Browser.Window.RequestAnimationFrame(step); - } - }; - Browser.Window.RequestAnimationFrame(step); - } - - private void CursorSelectRange(SelectionInfo startBeat, SelectionInfo endBeat) - { - var cache = _cursorCache; - if (cache == null) return; - - var selectionWrapper = _selectionWrapper; - selectionWrapper.InnerHTML = ""; - - if (startBeat == null || endBeat == null || startBeat.Beat == endBeat.Beat) - { - return; - } - - if (startBeat.Bounds == null) - { - startBeat.Bounds = cache.FindBeat(startBeat.Beat); - } - - if (endBeat.Bounds == null) - { - endBeat.Bounds = cache.FindBeat(endBeat.Beat); - } - - var startTick = startBeat.Beat.AbsolutePlaybackStart; - var endTick = endBeat.Beat.AbsolutePlaybackStart; - if (endTick < startTick) - { - var t = startBeat; - startBeat = endBeat; - endBeat = t; - } - - var startX = startBeat.Bounds.RealBounds.X; - var endX = endBeat.Bounds.RealBounds.X + endBeat.Bounds.RealBounds.W; - if (endBeat.Beat.Index == endBeat.Beat.Voice.Beats.Count - 1) - { - endX = endBeat.Bounds.BarBounds.MasterBarBounds.RealBounds.X + endBeat.Bounds.BarBounds.MasterBarBounds.RealBounds.W; - } - - // if the selection goes across multiple staves, we need a special selection highlighting - if (startBeat.Bounds.BarBounds.MasterBarBounds.StaveGroupBounds != endBeat.Bounds.BarBounds.MasterBarBounds.StaveGroupBounds) - { - // from the startbeat to the end of the staff, - // then fill all staffs until the end-beat staff - // then from staff-start to the end beat (or to end of bar if it's the last beat) - - var staffStartX = startBeat.Bounds.BarBounds.MasterBarBounds.StaveGroupBounds.VisualBounds.X; - var staffEndX = startBeat.Bounds.BarBounds.MasterBarBounds.StaveGroupBounds.VisualBounds.X + startBeat.Bounds.BarBounds.MasterBarBounds.StaveGroupBounds.VisualBounds.W; - - var startSelection = Browser.Document.CreateElement("div"); - startSelection.Style.Position = "absolute"; - startSelection.Style.Top = startBeat.Bounds.BarBounds.MasterBarBounds.VisualBounds.Y + "px"; - startSelection.Style.Left = startX + "px"; - startSelection.Style.Width = (staffEndX - startX) + "px"; - startSelection.Style.Height = startBeat.Bounds.BarBounds.MasterBarBounds.VisualBounds.H + "px"; - selectionWrapper.AppendChild(startSelection); - - var staffStartIndex = startBeat.Bounds.BarBounds.MasterBarBounds.StaveGroupBounds.Index + 1; - var staffEndIndex = endBeat.Bounds.BarBounds.MasterBarBounds.StaveGroupBounds.Index; - for (var staffIndex = staffStartIndex; staffIndex < staffEndIndex; staffIndex++) - { - var staffBounds = cache.StaveGroups[staffIndex]; - - var middleSelection = Browser.Document.CreateElement("div"); - middleSelection.Style.Position = "absolute"; - middleSelection.Style.Top = staffBounds.VisualBounds.Y + "px"; - middleSelection.Style.Left = staffStartX + "px"; - middleSelection.Style.Width = (staffEndX - staffStartX) + "px"; - middleSelection.Style.Height = staffBounds.VisualBounds.H + "px"; - selectionWrapper.AppendChild(middleSelection); - } - - var endSelection = Browser.Document.CreateElement("div"); - endSelection.Style.Position = "absolute"; - endSelection.Style.Top = endBeat.Bounds.BarBounds.MasterBarBounds.VisualBounds.Y + "px"; - endSelection.Style.Left = staffStartX + "px"; - endSelection.Style.Width = (endX - staffStartX) + "px"; - endSelection.Style.Height = endBeat.Bounds.BarBounds.MasterBarBounds.VisualBounds.H + "px"; - selectionWrapper.AppendChild(endSelection); - } - else - { - // if the beats are on the same staff, we simply highlight from the startbeat to endbeat - var selection = Browser.Document.CreateElement("div"); - selection.Style.Position = "absolute"; - selection.Style.Top = startBeat.Bounds.BarBounds.MasterBarBounds.VisualBounds.Y + "px"; - selection.Style.Left = startX + "px"; - selection.Style.Width = (endX - startX) + "px"; - selection.Style.Height = startBeat.Bounds.BarBounds.MasterBarBounds.VisualBounds.H + "px"; - selectionWrapper.AppendChild(selection); - } - } - - #endregion - - } - - internal class SelectionInfo - { - public Beat Beat { get; set; } - public BeatBounds Bounds { get; set; } - - public SelectionInfo(Beat beat) - { - Beat = beat; - } - } -} diff --git a/Source/AlphaTab.JavaScript/Platform/JavaScript/AlphaTabWebWorker.cs b/Source/AlphaTab.JavaScript/Platform/JavaScript/AlphaTabWebWorker.cs deleted file mode 100644 index 76f39cdf8..000000000 --- a/Source/AlphaTab.JavaScript/Platform/JavaScript/AlphaTabWebWorker.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System; -using AlphaTab.Haxe; -using AlphaTab.Haxe.Js; -using AlphaTab.Haxe.Js.Html; -using AlphaTab.Model; -using AlphaTab.Rendering; -using AlphaTab.Util; - -namespace AlphaTab.Platform.JavaScript -{ - class AlphaTabWebWorker - { - private ScoreRenderer _renderer; - private readonly DedicatedWorkerGlobalScope _main; - - public AlphaTabWebWorker(DedicatedWorkerGlobalScope main) - { - _main = main; - _main.AddEventListener("message", (Action)HandleMessage, false); - } - - public static void Init() - { - new AlphaTabWebWorker(Lib.Global); - } - - private void HandleMessage(Event e) - { - var data = ((MessageEvent)e).Data; - var cmd = data ? data.cmd : ""; - switch (cmd) - { - case "alphaTab.initialize": - Settings settings = Settings.FromJson(data.settings, null); - Logger.LogLevel = settings.LogLevel; - _renderer = new ScoreRenderer(settings); - _renderer.PartialRenderFinished += result => _main.PostMessage(new { cmd = "alphaTab.partialRenderFinished", result = result }); - _renderer.RenderFinished += result => _main.PostMessage(new { cmd = "alphaTab.renderFinished", result = result }); - _renderer.PostRenderFinished += () => _main.PostMessage(new { cmd = "alphaTab.postRenderFinished", boundsLookup = _renderer.BoundsLookup.ToJson() }); - _renderer.PreRender += result => _main.PostMessage(new { cmd = "alphaTab.preRender", result = result }); - _renderer.Error += Error; - break; - case "alphaTab.invalidate": - _renderer.Invalidate(); - break; - case "alphaTab.resize": - _renderer.Resize(data.width); - break; - case "alphaTab.render": - var score = JsonConverter.JsObjectToScore(data.score, _renderer.Settings); - RenderMultiple(score, data.trackIndexes); - break; - case "alphaTab.updateSettings": - UpdateSettings(data.settings); - break; - } - } - - private void UpdateSettings(object settings) - { - _renderer.UpdateSettings(Settings.FromJson(settings, null)); - } - - private void RenderMultiple(Score score, int[] trackIndexes) - { - try - { - _renderer.Render(score, trackIndexes); - } - catch (Exception e) - { - Error("render", e); - } - } - - private void Error(string type, Exception e) - { - Logger.Error(type, "An unexpected error occurred in worker", e); - - dynamic error = Json.Parse(Json.Stringify(e)); - - dynamic e2 = e; - - if (e2.message) - { - error.message = e2.message; - } - if (e2.stack) - { - error.stack = e2.stack; - } - if (e2.constructor && e2.constructor.name) - { - error.type = e2.constructor.name; - } - _main.PostMessage(new { cmd = "alphaTab.error", error = new { type = type, detail = error } }); - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Platform/JavaScript/AlphaTabWorkerScoreRenderer.cs b/Source/AlphaTab.JavaScript/Platform/JavaScript/AlphaTabWorkerScoreRenderer.cs deleted file mode 100644 index 25ec8d515..000000000 --- a/Source/AlphaTab.JavaScript/Platform/JavaScript/AlphaTabWorkerScoreRenderer.cs +++ /dev/null @@ -1,148 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.Haxe.Js.Html; -using AlphaTab.Model; -using AlphaTab.Rendering; -using AlphaTab.Rendering.Utils; -using AlphaTab.Util; -using Haxe; - -namespace AlphaTab.Platform.JavaScript -{ - class AlphaTabWorkerScoreRenderer : IScoreRenderer - { - private readonly AlphaTabApi _api; - private readonly Worker _worker; - - public BoundsLookup BoundsLookup { get; private set; } - - public AlphaTabWorkerScoreRenderer(AlphaTabApi api, Settings settings) - { - _api = api; - try - { - _worker = new Worker(settings.ScriptFile); - } - catch - { - // fallback to blob worker - try - { - HaxeString script = "importScripts('" + settings.ScriptFile + "')"; - var blob = new Blob(new [] { script }); - _worker = new Worker(URL.CreateObjectURL(blob)); - } - catch (Exception e) - { - Logger.Error("Rendering", "Failed to create WebWorker: " + e); - // TODO: fallback to synchronous mode - } - } - - _worker.PostMessage(new { cmd = "alphaTab.initialize", settings = settings.ToJson() }); - _worker.AddEventListener("message", (Action)(HandleWorkerMessage)); - } - - public void Destroy() - { - _worker.Terminate(); - } - - public void UpdateSettings(Settings settings) - { - _worker.PostMessage(new { cmd = "alphaTab.updateSettings", settings = settings.ToJson() }); - } - - public void Invalidate() - { - _worker.PostMessage(new { cmd = "alphaTab.invalidate" }); - } - - public void Resize(int width) - { - _worker.PostMessage(new { cmd = "alphaTab.resize", width = width }); - } - - private void HandleWorkerMessage(Event e) - { - var data = ((MessageEvent)e).Data; - string cmd = data.cmd; - switch (cmd) - { - case "alphaTab.preRender": - OnPreRender(data.result); - break; - case "alphaTab.partialRenderFinished": - OnPartialRenderFinished(data.result); - break; - case "alphaTab.renderFinished": - OnRenderFinished(data.result); - break; - case "alphaTab.postRenderFinished": - BoundsLookup = BoundsLookup.FromJson(data.boundsLookup, _api.Score); - OnPostRenderFinished(); - break; - case "alphaTab.error": - OnError(data.type, data.detail); - break; - } - } - - public void Render(Score score, int[] trackIndexes) - { - var jsObject = JsonConverter.ScoreToJsObject(score); - _worker.PostMessage(new { cmd = "alphaTab.render", score = jsObject, trackIndexes = trackIndexes }); - } - - public event Action PreRender; - protected virtual void OnPreRender(RenderFinishedEventArgs obj) - { - var handler = PreRender; - if (handler != null) handler(obj); - } - - public event Action PartialRenderFinished; - protected virtual void OnPartialRenderFinished(RenderFinishedEventArgs obj) - { - var handler = PartialRenderFinished; - if (handler != null) handler(obj); - } - - public event Action RenderFinished; - protected virtual void OnRenderFinished(RenderFinishedEventArgs obj) - { - var handler = RenderFinished; - if (handler != null) handler(obj); - } - - public event Action Error; - protected virtual void OnError(string type, Exception details) - { - var handler = Error; - if (handler != null) handler(type, details); - } - - public event Action PostRenderFinished; - protected virtual void OnPostRenderFinished() - { - var handler = PostRenderFinished; - if (handler != null) handler(); - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Platform/JavaScript/Html5Canvas.cs b/Source/AlphaTab.JavaScript/Platform/JavaScript/Html5Canvas.cs deleted file mode 100644 index b4f6b6a27..000000000 --- a/Source/AlphaTab.JavaScript/Platform/JavaScript/Html5Canvas.cs +++ /dev/null @@ -1,347 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.Haxe.Js; -using AlphaTab.Haxe.Js.Html; -using AlphaTab.Platform.Model; -using AlphaTab.Rendering; -using AlphaTab.Rendering.Glyphs; -using Phase; -using TextAlign = AlphaTab.Platform.Model.TextAlign; - -namespace AlphaTab.Platform.JavaScript -{ - /// - /// A canvas implementation for HTML5 canvas - /// - class Html5Canvas : ICanvas - { - protected const float BlurCorrection = 0; - - private CanvasElement _measureCanvas; - private CanvasRenderingContext2D _measureContext; - - private CanvasElement _canvas; - private CanvasRenderingContext2D _context; - private Color _color; - private Font _font; - private Font _musicFont; - private float _lineWidth; - - public RenderingResources Resources { get; set; } - - public Html5Canvas() - { - _color = new Color(0, 0, 0); - var fontElement = Browser.Document.CreateElement("span"); - fontElement.ClassList.Add("at"); - Browser.Document.Body.AppendChild(fontElement); - var style = Browser.Window.GetComputedStyle(fontElement); - string family = style.FontFamily; - if (family.StartsWith("\"") || family.StartsWith("'")) - { - family = family.Substring(1, family.Length - 2); - } - _musicFont = new Font(family, Platform.ParseFloat(style.FontSize)); - - _measureCanvas = (CanvasElement)Browser.Document.CreateElement("canvas"); - _measureCanvas.Width = 10; - _measureCanvas.Height = 10; - _measureCanvas.Style.Width = 10 + "px"; - _measureCanvas.Style.Height = 10 + "px"; - _measureContext = (CanvasRenderingContext2D)_measureCanvas.GetContext("2d"); - _measureContext.TextBaseline = "top"; - } - - public virtual object OnPreRender() - { - // nothing to do - return null; - } - - public virtual object OnRenderFinished() - { - // nothing to do - return null; - } - public void BeginRender(float width, float height) - { - _canvas = (CanvasElement)Browser.Document.CreateElement("canvas"); - _canvas.Width = (int)width; - _canvas.Height = (int)height; - _canvas.Style.Width = width + "px"; - _canvas.Style.Height = height + "px"; - _context = (CanvasRenderingContext2D)_canvas.GetContext("2d"); - _context.TextBaseline = "top"; - _context.LineWidth = _lineWidth; - } - - public object EndRender() - { - var result = _canvas; - _canvas = null; - return result; - } - - public Color Color - { - get - { - return _color; - } - set - { - if (_color.RGBA == value.RGBA) return; - _color = value; - _context.StrokeStyle = value.RGBA; - _context.FillStyle = value.RGBA; - } - } - - public float LineWidth - { - get - { - return _lineWidth; - } - set - { - _lineWidth = value; - if (_context != null) - { - _context.LineWidth = value; - } - } - } - - public void FillRect(float x, float y, float w, float h) - { - if (w > 0) - { - _context.FillRect(((int)x - BlurCorrection), ((int)y - BlurCorrection), w, h); - } - } - - public void StrokeRect(float x, float y, float w, float h) - { - _context.StrokeRect((x - BlurCorrection), (y - BlurCorrection), w, h); - } - - public void BeginPath() - { - _context.BeginPath(); - } - - public void ClosePath() - { - _context.ClosePath(); - } - - public void MoveTo(float x, float y) - { - _context.MoveTo((x - BlurCorrection), (y - BlurCorrection)); - } - - public void LineTo(float x, float y) - { - _context.LineTo((x - BlurCorrection), (y - BlurCorrection)); - } - - public void QuadraticCurveTo(float cpx, float cpy, float x, float y) - { - _context.QuadraticCurveTo(cpx, cpy, x, y); - } - - public void BezierCurveTo(float cp1x, float cp1y, float cp2x, float cp2y, float x, float y) - { - _context.BezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y); - } - - public void FillCircle(float x, float y, float radius) - { - _context.BeginPath(); - _context.Arc(x, y, radius, 0, (Math.PI * 2), true); - Fill(); - } - - public void Fill() - { - _context.Fill(); - } - - public void Stroke() - { - _context.Stroke(); - } - - public Font Font - { - get { return _font; } - set - { - _font = value; - if (_context != null) - { - _context.Font = value.ToCssString(); - } - - _measureContext.Font = value.ToCssString(); - } - } - - public TextAlign TextAlign - { - get - { - switch (_context.TextAlign) - { - case "left": - return TextAlign.Left; - case "center": - return TextAlign.Center; - case "right": - return TextAlign.Right; - default: - return TextAlign.Left; - } - } - set - { - switch (value) - { - case TextAlign.Left: - _context.TextAlign = "left"; - break; - case TextAlign.Center: - _context.TextAlign = "center"; - break; - case TextAlign.Right: - _context.TextAlign = "right"; - break; - } - } - } - - public TextBaseline TextBaseline - { - get - { - switch (_context.TextBaseline) - { - case "top": - return TextBaseline.Top; - case "middle": - return TextBaseline.Middle; - case "bottom": - return TextBaseline.Bottom; - default: - return TextBaseline.Top; - } - } - set - { - switch (value) - { - case TextBaseline.Top: - _context.TextBaseline = "top"; - break; - case TextBaseline.Middle: - _context.TextBaseline = "middle"; - break; - case TextBaseline.Bottom: - _context.TextBaseline = "bottom"; - break; - } - } - } - - public void BeginGroup(string identifier) - { - } - - public void EndGroup() - { - } - - public void FillText(string text, float x, float y) - { - x = (int)x; - y = (int)y; - _context.FillText(text, x, y); - } - - public float MeasureText(string text) - { - return (float)_measureContext.MeasureText(text).Width; - } - - public void FillMusicFontSymbol(float x, float y, float scale, MusicFontSymbol symbol) - { - if (symbol == MusicFontSymbol.None) - { - return; - } - - x = (int)x; - y = (int)y; - var baseLine = _context.TextBaseline; - var font = _context.Font; - _context.Font = _musicFont.ToCssString(scale); - _context.TextBaseline = "middle"; - _context.FillText(Platform.StringFromCharCode((int)symbol), x, y); - _context.TextBaseline = baseLine; - _context.Font = font; - } - - public void FillMusicFontSymbols(float x, float y, float scale, MusicFontSymbol[] symbols) - { - x = (int)x; - y = (int)y; - var baseLine = _context.TextBaseline; - var font = _context.Font; - _context.Font = _musicFont.ToCssString(scale); - _context.TextBaseline = "middle"; - - var s = ""; - foreach (var symbol in symbols) - { - if (symbol != MusicFontSymbol.None) - { - s += Platform.StringFromCharCode((int)symbol); - } - } - - _context.FillText(s, x, y); - _context.TextBaseline = baseLine; - _context.Font = font; - } - - public void BeginRotate(float centerX, float centerY, float angle) - { - _context.Save(); - _context.Translate(centerX, centerY); - _context.Rotate(angle * Math.PI / 180.0f); - } - - public void EndRotate() - { - _context.Restore(); - } - - } -} \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Platform/JavaScript/JQueryAlphaTab.cs b/Source/AlphaTab.JavaScript/Platform/JavaScript/JQueryAlphaTab.cs deleted file mode 100644 index b609c0790..000000000 --- a/Source/AlphaTab.JavaScript/Platform/JavaScript/JQueryAlphaTab.cs +++ /dev/null @@ -1,345 +0,0 @@ -using System; -using AlphaTab.Audio.Synth; -using AlphaTab.Audio.Synth.Synthesis; -using AlphaTab.Collections; -using AlphaTab.Haxe.jQuery; -using AlphaTab.Haxe.Js; -using AlphaTab.Haxe.Js.Html; -using AlphaTab.Model; -using AlphaTab.Rendering; -using AlphaTab.Util; -using Phase; -using Phase.Attributes; - -namespace AlphaTab.Platform.JavaScript -{ - class JQueryAlphaTab - { - public object Exec(Element element, string method, string[] args) - { - if (Script.Write("untyped __js__(\"typeof(method) != 'string'\")")) - { - args = new[] { method }; - method = "init"; - } - - if (method[0] == '_' || method == "Exec") - { - return null; - } - - var jElement = new JQuery(element); - AlphaTabApi context = (AlphaTabApi)jElement.Data("alphaTab"); - if (method == "destroy" && !context.IsTruthy()) - { - return null; - } - if (method != "init" && !context.IsTruthy()) - { - throw new Error("alphaTab not initialized"); - } - - var apiMethod = Script.Write("untyped __js__(\"this[method]\")"); - if (apiMethod.IsTruthy()) - { - var realArgs = Script.Write("untyped __js__(\"[ jElement, context ].concat(args)\")"); - return Script.Write("untyped apiMethod.apply(this, realArgs)"); - } - else - { - Logger.Error("Api", "Method '" + method + "' does not exist on jQuery.alphaTab"); - return null; - } - } - - [Name("init")] - public void Init(JQuery element, AlphaTabApi context, dynamic options) - { - if (!context.IsTruthy()) - { - context = new AlphaTabApi(element[0], options); - element.Data("alphaTab", context); - foreach (var listener in _initListeners) - { - listener(element, context, options); - } - } - } - - [Name("destroy")] - public void Destroy(JQuery element, AlphaTabApi context) - { - element.RemoveData("alphaTab"); - context.Destroy(); - } - - [Name("tex")] - public void Tex(JQuery element, AlphaTabApi context, string tex) - { - context.Tex(tex); - } - - [Name("tracks")] - public Track[] Tracks(JQuery element, AlphaTabApi context, dynamic tracks) - { - if (tracks) - { - context.SetTracks(tracks, true); - } - return context.Tracks; - } - - - [Name("api")] - public AlphaTabApi Api(JQuery element, AlphaTabApi context) - { - return context; - } - - [Name("score")] - public Score Score(JQuery element, AlphaTabApi context, Score score) - { - if (score.IsTruthy()) - { - context.ScoreLoaded(score); - } - return context.Score; - } - - [Name("renderer")] - public IScoreRenderer Renderer(JQuery element, AlphaTabApi context) - { - return context.Renderer; - } - - [Name("layout")] - public LayoutSettings Layout(JQuery element, AlphaTabApi context, object layout) - { - if (layout.IsTruthy()) - { - context.UpdateLayout(layout); - } - return context.Settings.Layout; - } - - [Name("print")] - public void Print(JQuery element, AlphaTabApi context, string width) - { - context.Print(width); - } - - #region Player - [Name("player")] - public IAlphaSynth Player(JQuery element, AlphaTabApi context) - { - return context.Player; - } - - [Name("playerOptions")] - public Settings PlayerOptions(JQuery element, AlphaTabApi context, object options) - { - if (options.IsTruthy()) - { - Settings.FillPlayerOptions(context.Settings, options, false); - } - return context.Settings; - } - - [Name("cursorOptions")] - public Settings CursorOptions(JQuery element, AlphaTabApi context, object options) - { - return PlayerOptions(element, context, options); - } - - [Name("playerState")] - public PlayerState PlayerState(JQuery element, AlphaTabApi context) - { - if (context.Player == null) - { - return Audio.Synth.PlayerState.Paused; - } - return context.Player.State; - } - - [Name("masterVolume")] - public float MasterVolume(JQuery element, AlphaTabApi context, float masterVolume) - { - if (context.Player == null) - { - return 0; - } - - if (masterVolume.IsTruthy()) - { - context.Player.MasterVolume = masterVolume; - } - - return context.Player.MasterVolume; - } - - [Name("playbackSpeed")] - public double PlaybackSpeed(JQuery element, AlphaTabApi context, double playbackSpeed) - { - if (context.Player == null) - { - return 0; - } - - if (playbackSpeed.IsTruthy()) - { - context.Player.PlaybackSpeed = playbackSpeed; - } - - return context.Player.PlaybackSpeed; - } - - [Name("metronomeVolume")] - public float MetronomeVolume(JQuery element, AlphaTabApi context, float metronomeVolume) - { - if (context.Player == null) - { - return 0; - } - - if (metronomeVolume.IsTruthy()) - { - context.Player.MetronomeVolume = metronomeVolume; - } - - return context.Player.MetronomeVolume; - } - - [Name("tickPosition")] - public int TickPosition(JQuery element, AlphaTabApi context, int tickPosition) - { - if (context.Player == null) - { - return 0; - } - - if (tickPosition.IsTruthy()) - { - context.Player.TickPosition = tickPosition; - } - - return context.Player.TickPosition; - } - - [Name("playbackRange")] - public PlaybackRange PlaybackRange(JQuery element, AlphaTabApi context, PlaybackRange playbackRange) - { - if (context.Player == null) - { - return null; - } - - if (playbackRange.IsTruthy()) - { - context.Player.PlaybackRange = playbackRange; - } - - return context.Player.PlaybackRange; - } - - [Name("loop")] - public bool PlaybackRange(JQuery element, AlphaTabApi context, bool loop) - { - if (context.Player == null) - { - return false; - } - - if (loop.IsTruthy()) - { - context.Player.IsLooping = loop; - } - - return context.Player.IsLooping; - } - - [Name("autoScroll")] - public string AutoScroll(JQuery element, AlphaTabApi context, string autoScroll) - { - if (context.Player == null) - { - return null; - } - - if (autoScroll.IsTruthy()) - { - context.Settings.ScrollMode = autoScroll; - } - - return context.Settings.ScrollMode; - } - - [Name("play")] - public void Play(JQuery element, AlphaTabApi context) - { - context.Play(); - } - - [Name("pause")] - public void Pause(JQuery element, AlphaTabApi context) - { - context.Pause(); - } - - [Name("playPause")] - public void PlayPause(JQuery element, AlphaTabApi context) - { - context.PlayPause(); - } - - [Name("stop")] - public void Stop(JQuery element, AlphaTabApi context) - { - context.Stop(); - } - - [Name("loadSoundFont")] - public void LoadSoundFont(JQuery element, AlphaTabApi context, object value) - { - context.LoadSoundFont(value); - } - - [Name("muteTrack")] - public void MuteTrack(JQuery element, AlphaTabApi context, object tracks, bool mute) - { - context.SetTrackMute(tracks, mute); - } - - [Name("soloTrack")] - public void SoloTrack(JQuery element, AlphaTabApi context, object tracks, bool solo) - { - context.SetTrackSolo(tracks, solo); - } - - - [Name("trackVolume")] - public void TrackVolume(JQuery element, AlphaTabApi context, object tracks, float volume) - { - context.SetTrackVolume(tracks, volume); - } - - [Name("downloadMidi")] - public void DownloadMidi(JQuery element, AlphaTabApi context, object tracks, float volume) - { - context.DownloadMidi(); - } - - #endregion - - private readonly FastList> _initListeners = new FastList>(); - [Name("_oninit")] - public void OnInit(Action listener) - { - _initListeners.Add(listener); - } - - public static Action Restore = selector => - { - new JQuery(selector).Empty().RemoveData("alphaTab"); - }; - } -} diff --git a/Source/AlphaTab.JavaScript/Platform/Platform.cs b/Source/AlphaTab.JavaScript/Platform/Platform.cs deleted file mode 100644 index 7124e74fb..000000000 --- a/Source/AlphaTab.JavaScript/Platform/Platform.cs +++ /dev/null @@ -1,380 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using AlphaTab.Audio.Synth; -using AlphaTab.Audio.Synth.Midi; -using AlphaTab.Audio.Synth.Midi.Event; -using AlphaTab.Collections; -using AlphaTab.Haxe.Js; -using AlphaTab.IO; -using AlphaTab.Platform.JavaScript; -using AlphaTab.Util; -using Haxe; -using Haxe.Js.Html; -using Phase; -using Phase.Attributes; - -namespace AlphaTab.Platform -{ - [Name("js.html.TextDecoder")] - [External] - class TextDecoder - { - public extern TextDecoder(HaxeString label); - - [Name("decode")] - public extern HaxeString Decode(ArrayBuffer data); - [Name("decode")] - public extern HaxeString Decode(ArrayBufferView data); - } - - static partial class Platform - { - [Inline] - public static float ParseFloat(string s) - { - return Script.Write("untyped parseFloat(s)"); - } - - [Inline] - public static string GetCallerName() - { - return Script.Write("untyped __js__(\"arguments.callee.caller.caller.name\")"); - } - - public static void Log(LogLevel logLevel, string category, string msg, object details = null) - { - // ReSharper disable once RedundantAssignment - msg = "[AlphaTab][" + category + "] " + msg; - - Haxe.Js.Html.Console console = Lib.Global.console; - - switch (logLevel) - { - case LogLevel.None: - break; - case LogLevel.Debug: - msg = "[Debug]" + msg; - console.Debug(msg, details); - break; - case LogLevel.Info: - msg = "[Info]" + msg; - console.Info(msg, details); - break; - case LogLevel.Warning: - console.Warn(msg, details); - break; - case LogLevel.Error: - var stack = CallStack.ToString(CallStack.Get()); - console.Error(msg, stack, details); - break; - } - } - - [Inline] - public static dynamic NewObject() - { - return Script.Write("untyped __js__(\"{}\")"); - } - - [Inline] - public static bool JsonExists(object json, string property) - { - return Script.Write("untyped __js__(\"({0} && {1} in {0})\", json, property)"); - } - - [Inline] - public static string[] JsonKeys(object json) - { - return Script.Write("untyped __js__(\"Object.keys({0})\", json)"); - } - - [Inline] - public static float Log2(float f) - { - return Script.Write("untyped Math.log2(f)"); - } - - public static int ParseInt(string s) - { - var val = Script.Write("untyped parseInt(s)"); - return Script.Write("Math.isNaN(untyped val)") ? int.MinValue : (int)val; - } - - [Inline] - public static int[] CloneArray(int[] array) - { - return Script.Write("untyped __js__(\"new Int32Array({0})\", array)"); - } - - [Inline] - public static void BlockCopy(byte[] src, int srcOffset, byte[] dst, int dstOffset, int count) - { - ArrayCopy(src, srcOffset, dst, dstOffset, count); - } - - [Inline] - public static string StringFromCharCode(int c) - { - return Script.Write("String.fromCharCode(c.ToHaxeInt())"); - } - - public static sbyte ReadSignedByte(this IReadable readable) - { - var n = readable.ReadByte(); - if (n >= 128) return (sbyte)(n - 256); - return (sbyte)n; - } - - public static string ToString(byte[] data, string encoding) - { - if (SupportsTextDecoder) - { - var detectedEncoding = DetectEncoding(data); - if (detectedEncoding != null) - { - encoding = detectedEncoding; - } - if (encoding == null) - { - encoding = "utf-8"; - } - var decoder = new TextDecoder(encoding); - return decoder.Decode(data.As()); - } - else - { - // manual UTF8 decoding for older browsers - var s = new StringBuilder(); - int i = 0; - while (i < data.Length) - { - var c = data[i++]; - if (c < 0x80) - { - if (c == 0) break; - s.AppendChar(c); - } - else if (c < 0xE0) - { - s.AppendChar(((c & 0x3F) << 6) | (data[i++] & 0x7F)); - } - else if (c < 0xF0) - { - s.AppendChar(((c & 0x1F) << 12) | ((data[i++] & 0x7F) << 6) | (data[i++] & 0x7F)); - } - else - { - var u = ((c & 0x0F) << 18) | ((data[i++] & 0x7F) << 12) | - ((data[i++] & 0x7F) << 6) | (data[i++] & 0x7F); - s.AppendChar((u >> 18) + 0xD7C0); - s.AppendChar((u & 0x3FF) | 0xDC00); - } - } - return s.ToString(); - } - } - - public static byte[] StringToByteArray(string contents) - { - var byteArray = new byte[contents.Length]; - for (int i = 0; i < contents.Length; i++) - { - byteArray[i] = (byte)contents[i]; - } - return byteArray; - } - - private static string S4() - { - return Script.Write("untyped Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1)"); - } - - public static string NewGuid() - { - return S4() + S4() + '-' + S4() + '-' + S4() + '-' + - S4() + '-' + S4() + S4() + S4(); - } - - [Inline] - public static T Member(this object s, string name) - { - return Script.Write("untyped s[name]"); - } - - [Inline] - public static T Member(this object s, string name, T value) - { - return Script.Write("untyped s[name] = value"); - } - - [Inline] - public static string[] Match(this string s, string regex) - { - return Script.Write("untyped s.match(regex)"); - } - [Inline] - public static bool IsTruthy(this object o) - { - return Script.Write("untyped !!o"); - } - - [Inline] - public static bool IsNaN(float v) - { - return Script.Write("Math.isNaN(v.ToHaxeFloat())"); - } - - [Inline] - public static string TypeOf(object o) - { - return Script.Write("untyped __typeof__(o)"); - } - - [External] - [Template("untyped __instanceof__({o}, {T})")] - public static extern bool InstanceOf(object o); - - [Inline] - public static byte[] ArrayBufferToByteArray(ArrayBuffer data) - { - return Script.Write("untyped __js__(\"new Uint8Array({0})\", data)"); - } - - public static double ToDouble(byte[] bytes) - { - var array = new Float64Array(Script.Write("untyped __js__(\"{0}.buffer\", bytes)")); - return array[0]; - } - - public static float ToFloat(byte[] bytes) - { - var array = new Float32Array(Script.Write("untyped __js__(\"{0}.buffer\", bytes)")); - return array[0]; - } - - public static void ClearIntArray(int[] array) - { - for (int i = 0; i < array.Length; i++) - { - array[i] = 0; - } - } - - public static void ClearShortArray(short[] array) - { - for (int i = 0; i < array.Length; i++) - { - array[i] = 0; - } - } - - public static int Random(int max) - { - HaxeInt m = max; - return Script.Write("Std.int(Math.random() * m)"); - } - - [Inline] - public static double RandomDouble() - { - return Script.Write("Math.random()"); - } - - public static bool SupportsWebAudio - { - [Inline] - get - { - return Script.Write("untyped __js__(\"!!window.ScriptProcessorNode\")"); - } - } - - public static bool SupportsWebWorkers - { - [Inline] - get - { - return Script.Write("untyped __js__(\"!!window.Worker\")"); - } - } - - public static bool ForceFlash - { - [Inline] - get - { - return Script.Write("untyped __js__(\"!!window.ForceFlash\")"); - } - } - - - public static bool SupportsTextDecoder - { - [Inline] - get - { - return !!Lib.Global.TextDecoder; - } - } - - [Inline] - public static void ArrayCopy(int[] src, int srcOffset, int[] dst, int dstOffset, int count) - { - Script.Write("untyped __js__(\"{2}.set({0}.subarray({1},{1}+{4}), {3})\", src, srcOffset, dst, dstOffset, count);"); - } - - [Inline] - public static void ArrayCopy(short[] src, int srcOffset, short[] dst, int dstOffset, int count) - { - Script.Write("untyped __js__(\"{2}.set({0}.subarray({1},{1}+{4}), {3})\", src, srcOffset, dst, dstOffset, count);"); - } - - [Inline] - public static void ArrayCopy(byte[] src, int srcOffset, byte[] dst, int dstOffset, int count) - { - Script.Write("untyped __js__(\"{2}.set({0}.subarray({1},{1}+{4}), {3})\", src, srcOffset, dst, dstOffset, count);"); - } - - public static void ArrayCopy(T[] src, int srcOffset, T[] dst, int dstOffset, int count) - { - for (int i = 0; i < count; i++) - { - dst[dstOffset + i] = src[srcOffset + i]; - } - } - - [Inline] - public static void Reverse(byte[] array) - { - Script.Write("untyped __js__(\"{0}.reverse()\", array);"); - } - - [Inline] - public static string GetTypeName(T obj) - { - return Script.Write("Type.getClassName(Type.getClass(obj))"); - } - - public static long GetCurrentMilliseconds() - { - return Script.Write("untyped __js__(\"Date.now()\")"); - } - } -} diff --git a/Source/AlphaTab.JavaScript/Properties/AssemblyInfo.cs b/Source/AlphaTab.JavaScript/Properties/AssemblyInfo.cs deleted file mode 100644 index 6f13766b0..000000000 --- a/Source/AlphaTab.JavaScript/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("AlphaTab.Test.Js")] \ No newline at end of file diff --git a/Source/AlphaTab.JavaScript/Rendering/Utils/BoundsLookup.cs b/Source/AlphaTab.JavaScript/Rendering/Utils/BoundsLookup.cs deleted file mode 100644 index d7787dc94..000000000 --- a/Source/AlphaTab.JavaScript/Rendering/Utils/BoundsLookup.cs +++ /dev/null @@ -1,169 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Collections; -using AlphaTab.Model; -using AlphaTab.Platform; - -namespace AlphaTab.Rendering.Utils -{ - partial class BoundsLookup - { - public object ToJson() - { - var json = Platform.Platform.NewObject(); - - var staveGroups = new FastList(); - json.StaveGroups = staveGroups; - - foreach (var group in StaveGroups) - { - StaveGroupBounds g = Platform.Platform.NewObject(); - g.VisualBounds = BoundsToJson(group.VisualBounds); - g.RealBounds = BoundsToJson(group.RealBounds); - g.Bars = new FastList(); - - foreach (var masterBar in group.Bars) - { - MasterBarBounds mb = Platform.Platform.NewObject(); - mb.LineAlignedBounds = BoundsToJson(masterBar.LineAlignedBounds); - mb.VisualBounds = BoundsToJson(masterBar.VisualBounds); - mb.RealBounds = BoundsToJson(masterBar.RealBounds); - mb.Index = masterBar.Index; - mb.Bars = new FastList(); - - foreach (var bar in masterBar.Bars) - { - BarBounds b = Platform.Platform.NewObject(); - b.VisualBounds = BoundsToJson(bar.VisualBounds); - b.RealBounds = BoundsToJson(bar.RealBounds); - - b.Beats = new FastList(); - - foreach (var beat in bar.Beats) - { - var bb = Platform.Platform.NewObject(); - - bb.VisualBounds = BoundsToJson(beat.VisualBounds); - bb.RealBounds = BoundsToJson(beat.RealBounds); - bb.BeatIndex = beat.Beat.Index; - bb.VoiceIndex = beat.Beat.Voice.Index; - bb.BarIndex = beat.Beat.Voice.Bar.Index; - bb.StaffIndex = beat.Beat.Voice.Bar.Staff.Index; - bb.TrackIndex = beat.Beat.Voice.Bar.Staff.Track.Index; - - if (beat.Notes != null) - { - FastList notes = bb.Notes = new FastList(); - - foreach (var note in beat.Notes) - { - var n = Platform.Platform.NewObject(); - n.Index = note.Note.Index; - n.NoteHeadBounds = BoundsToJson(note.NoteHeadBounds); - notes.Add(n); - } - } - - b.Beats.Add(bb); - } - - mb.Bars.Add(b); - } - - g.Bars.Add(mb); - } - - staveGroups.Add(g); - } - - return json; - } - - public static BoundsLookup FromJson(object json, Score score) - { - var lookup = new BoundsLookup(); - - var staveGroups = json.Member>("StaveGroups"); - foreach (var staveGroup in staveGroups) - { - var sg = new StaveGroupBounds(); - sg.VisualBounds = staveGroup.VisualBounds; - sg.RealBounds = staveGroup.RealBounds; - lookup.AddStaveGroup(sg); - - foreach (var masterBar in staveGroup.Bars) - { - var mb = new MasterBarBounds(); - mb.Index = masterBar.Index; - mb.IsFirstOfLine = masterBar.IsFirstOfLine; - mb.LineAlignedBounds = masterBar.LineAlignedBounds; - mb.VisualBounds = masterBar.VisualBounds; - mb.RealBounds = masterBar.RealBounds; - sg.AddBar(mb); - - foreach (var bar in masterBar.Bars) - { - var b = new BarBounds(); - b.VisualBounds = bar.VisualBounds; - b.RealBounds = bar.RealBounds; - mb.AddBar(b); - - foreach (var beat in bar.Beats) - { - var bb = new BeatBounds(); - bb.VisualBounds = beat.VisualBounds; - bb.RealBounds = beat.RealBounds; - bb.Beat = score - .Tracks[beat.Member("TrackIndex")] - .Staves[beat.Member("StaffIndex")] - .Bars[beat.Member("BarIndex")] - .Voices[beat.Member("VoiceIndex")] - .Beats[beat.Member("BeatIndex")]; - - if (beat.Notes != null) - { - bb.Notes = new FastList(); - foreach (var note in beat.Notes) - { - var n = new NoteBounds(); - n.Note = bb.Beat.Notes[note.Member("Index")]; - n.NoteHeadBounds = note.NoteHeadBounds; - bb.AddNote(n); - } - } - - b.AddBeat(bb); - } - } - } - } - - return lookup; - } - - private Bounds BoundsToJson(Bounds bounds) - { - var json = Platform.Platform.NewObject(); - json.X = bounds.X; - json.Y = bounds.Y; - json.W = bounds.W; - json.H = bounds.H; - return json; - } - } -} diff --git a/Source/AlphaTab.JavaScript/Settings.cs b/Source/AlphaTab.JavaScript/Settings.cs deleted file mode 100644 index 5fae29a41..000000000 --- a/Source/AlphaTab.JavaScript/Settings.cs +++ /dev/null @@ -1,858 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Collections; -using AlphaTab.Platform; -using AlphaTab.Util; -using Phase; - -namespace AlphaTab -{ - /// - /// This public class contains instance specific settings for alphaTab - /// - public partial class Settings - { - public string ScriptFile - { - get; set; - } - - public string FontDirectory - { - get; set; - } - - public bool DisableLazyLoading - { - get; set; - } - - public bool UseWebWorker - { - get; set; - } - - public bool EnablePlayer - { - get; set; - } - - public string SoundFontFile - { - get; set; - } - - public bool EnableCursor - { - get; set; - } - - public int ScrollOffsetX - { - get; set; - } - - public int ScrollOffsetY - { - get; set; - } - - public bool EnableSeekByClick - { - get; set; - } - - public string ScrollMode - { - get; set; - } - - public int ScrollSpeed - { - get; set; - } - - public string ScrollElement - { - get; set; - } - - - public int BeatCursorWidth - { - get; set; - } - - private static void SetDefaults(Settings settings) - { - settings.UseWebWorker = true; - settings.ScrollMode = "vertical"; - settings.ScrollSpeed = 300; - settings.ScrollElement = "html,body"; - settings.BeatCursorWidth = 3; - } - - public static void FillPlayerOptions(Settings settings, dynamic json, bool setDefaults, FastDictionary dataAttributes = null) - { - if (Platform.Platform.JsonExists(json, "cursor")) - { - settings.EnableCursor = json.cursor; - } - else if (dataAttributes != null && dataAttributes.ContainsKey("cursor")) - { - settings.EnableCursor = (bool)dataAttributes["cursor"]; - } - else if(setDefaults) - { - settings.EnableCursor = true; - } - - if (settings.EnableCursor) - { - if (Platform.Platform.JsonExists(json, "playerOffset")) - { - FillCursorOffset(settings, json.playerOffset); - } - else if (dataAttributes != null && dataAttributes.ContainsKey("playerOffset")) - { - FillCursorOffset(settings, dataAttributes["playerOffset"]); - } - } - - if (Platform.Platform.JsonExists(json, "handleClick")) - { - settings.EnableSeekByClick = json.handleClick; - } - else if(setDefaults) - { - settings.EnableSeekByClick = true; - } - - if (Platform.Platform.JsonExists(json, "autoScroll")) - { - settings.ScrollMode = json.autoScroll; - } - else if(setDefaults) - { - settings.ScrollMode = "vertical"; - } - - if (Platform.Platform.JsonExists(json, "scrollSpeed")) - { - settings.ScrollSpeed = json.scrollSpeed; - } - else if(setDefaults) - { - settings.ScrollSpeed = 300; - } - - if (Platform.Platform.JsonExists(json, "scrollSpeed")) - { - settings.ScrollElement = json.scrollSpeed; - } - else if(setDefaults) - { - settings.ScrollElement = "html,body"; - } - - if (Platform.Platform.JsonExists(json, "beatCursorWidth")) - { - settings.BeatCursorWidth = json.beatCursorWidth; - } - else if(setDefaults) - { - settings.BeatCursorWidth = 3; - } - } - - public dynamic ToJson() - { - dynamic json = Platform.Platform.NewObject(); - - json.useWorker = UseWebWorker; - json.scale = Scale; - json.slurHeight = SlurHeightFactor; - json.width = Width; - json.engine = Engine; - json.stretchForce = StretchForce; - json.forcePianoFingering = ForcePianoFingering; - json.transpositionPitches = TranspositionPitches; - json.displayTranspositionPitches = DisplayTranspositionPitches; - json.logging = LogLevel; - json.smallGraceTabNotes = SmallGraceTabNotes; - json.extendBendArrowsOnTiedNotes = ExtendBendArrowsOnTiedNotes; - json.showParenthesisForTiedBends = ShowParenthesisForTiedBends; - json.showTabNoteOnTiedBend = ShowTabNoteOnTiedBend; - json.displayMode = DisplayMode; - json.fingeringMode = FingeringMode; - json.showZeroOnDiveWhammy = ShowZeroOnDiveWhammy; - json.extendLineEffectsToBeatEnd = ExtendLineEffectsToBeatEnd; - json.songBookBendDuration = SongBookBendDuration; - json.songBookDipDuration = SongBookDipDuration; - - json.scriptFile = ScriptFile; - json.fontDirectory = FontDirectory; - json.lazy = DisableLazyLoading; - - json.includeNoteBounds = IncludeNoteBounds; - - json.vibrato = Platform.Platform.NewObject(); - json.noteSlightAmplitude = Vibrato.NoteSlightAmplitude; - json.noteWideAmplitude = Vibrato.NoteWideAmplitude; - json.noteSlightLength = Vibrato.NoteSlightLength; - json.noteWideLength = Vibrato.NoteWideLength; - json.beatSlightAmplitude = Vibrato.BeatSlightAmplitude; - json.beatWideAmplitude = Vibrato.BeatWideAmplitude; - json.beatSlightLength = Vibrato.BeatSlightLength; - json.beatWideLength = Vibrato.BeatWideLength; - - json.layout = Platform.Platform.NewObject(); - json.layout.mode = Layout.Mode; - json.layout.additionalSettings = Platform.Platform.NewObject(); - foreach (string setting in Layout.AdditionalSettings) - { - json.layout.additionalSettings[setting] = Layout.AdditionalSettings[setting]; - } - - json.importer = Platform.Platform.NewObject(); - foreach (string setting in ImporterSettings) - { - json.importer[setting] = ImporterSettings[setting]; - } - - json.staves = Platform.Platform.NewObject(); - json.staves.id = Staves.Id; - json.staves.additionalSettings = Platform.Platform.NewObject(); - - foreach (var additionalSetting in Staves.AdditionalSettings) - { - json.staves.additionalSettings[additionalSetting] = Staves.AdditionalSettings[additionalSetting]; - } - - return json; - } - - public static Settings FromJson(dynamic json, FastDictionary dataAttributes) - { - if (json is Settings) - { - return (Settings)json; - } - - var settings = Defaults; - settings.ScriptFile = Environment.ScriptFile; - - FillFromJson(settings, json, dataAttributes); - - return settings; - } - - public static void FillFromJson(Settings settings, dynamic json, FastDictionary dataAttributes) - { - var global = Script.Write("js.Lib.global"); - - // System Settings - - if (global.document && global.ALPHATAB_ROOT) - { - settings.ScriptFile = global.ALPHATAB_ROOT; - settings.ScriptFile = EnsureFullUrl(settings.ScriptFile); - settings.ScriptFile = AppendScriptName(settings.ScriptFile); - } - else - { - settings.ScriptFile = Environment.ScriptFile; - } - - if (global.document && global.ALPHATAB_FONT) - { - settings.FontDirectory = global.ALPHATAB_FONT; - settings.FontDirectory = EnsureFullUrl(settings.FontDirectory); - } - else - { - settings.FontDirectory = settings.ScriptFile; - if (!string.IsNullOrEmpty(settings.FontDirectory)) - { - var lastSlash = settings.FontDirectory.LastIndexOf('/'); - if (lastSlash >= 0) - { - settings.FontDirectory = settings.FontDirectory.Substring(0, lastSlash) + "/Font/"; - } - } - } - - if (Platform.Platform.JsonExists(json, "logging")) - { - settings.LogLevel = DecodeLogLevel(json.log); - } - else if (dataAttributes != null && dataAttributes.ContainsKey("logging")) - { - settings.LogLevel = DecodeLogLevel(dataAttributes["logging"]); - } - - if (Platform.Platform.JsonExists(json, "useWorker")) - { - settings.UseWebWorker = json.useWorker; - } - else if (dataAttributes != null && dataAttributes.ContainsKey("useWorker")) - { - settings.UseWebWorker = dataAttributes["useWorker"].IsTruthy(); - } - - // Display settings - - if (Platform.Platform.JsonExists(json, "displayMode")) - { - settings.DisplayMode = DecodeDisplayMode(json.displayMode); - } - else if (dataAttributes != null && dataAttributes.ContainsKey("displayMode")) - { - settings.DisplayMode = DecodeDisplayMode(dataAttributes["displayMode"]); - } - - // Override some defaults on songbook mode - if (settings.DisplayMode == DisplayMode.SongBook) - { - settings.ApplySongBookDefaults(); - } - - if (Platform.Platform.JsonExists(json, "scale")) - { - settings.Scale = json.scale; - } - else if (dataAttributes != null && dataAttributes.ContainsKey("scale")) - { - settings.Scale = dataAttributes["scale"].As(); - } - if (Platform.Platform.JsonExists(json, "slurHeight")) - { - settings.SlurHeightFactor = json.slurHeight; - } - else if (dataAttributes != null && dataAttributes.ContainsKey("slurHeight")) - { - settings.SlurHeightFactor = dataAttributes["slurHeight"].As(); - } - if (Platform.Platform.JsonExists(json, "width")) - { - settings.Width = json.width; - } - else if (dataAttributes != null && dataAttributes.ContainsKey("width")) - { - settings.Width = dataAttributes["width"].As(); - } - if (Platform.Platform.JsonExists(json, "engine")) - { - settings.Engine = json.engine; - } - else if (dataAttributes != null && dataAttributes.ContainsKey("engine")) - { - settings.Engine = dataAttributes["engine"].As(); - } - if (Platform.Platform.JsonExists(json, "stretchForce")) - { - settings.StretchForce = json.stretchForce; - } - else if (dataAttributes != null && dataAttributes.ContainsKey("stretchForce")) - { - settings.StretchForce = dataAttributes["stretchForce"].As(); - } - if (Platform.Platform.JsonExists(json, "forcePianoFingering")) - { - settings.ForcePianoFingering = json.forcePianoFingering; - } - else if (dataAttributes != null && dataAttributes.ContainsKey("forcePianoFingering")) - { - settings.ForcePianoFingering = dataAttributes["forcePianoFingering"].As(); - } - if (Platform.Platform.JsonExists(json, "lazy")) - { - settings.DisableLazyLoading = !json.lazy; - } - else if (dataAttributes != null && dataAttributes.ContainsKey("lazy")) - { - settings.DisableLazyLoading = !dataAttributes["lazy"].IsTruthy(); - } - if (Platform.Platform.JsonExists(json, "transpositionPitches")) - { - settings.TranspositionPitches = json.transpositionPitches; - } - else if (dataAttributes != null && dataAttributes.ContainsKey("transpositionPitches")) - { - var pitchOffsets = dataAttributes["transpositionPitches"]; - if (pitchOffsets != null && pitchOffsets.Member("length")) - { - settings.TranspositionPitches = (int[])pitchOffsets; - } - } - - if (Platform.Platform.JsonExists(json, "displayTranspositionPitches")) - { - settings.DisplayTranspositionPitches = json.displayTranspositionPitches; - } - else if (dataAttributes != null && dataAttributes.ContainsKey("displayTranspositionPitches")) - { - var pitchOffsets = dataAttributes["displayTranspositionPitches"]; - if (pitchOffsets != null && pitchOffsets.Member("length")) - { - settings.DisplayTranspositionPitches = (int[])pitchOffsets; - } - } - - if (Platform.Platform.JsonExists(json, "scriptFile")) - { - settings.ScriptFile = EnsureFullUrl(json.scriptFile); - settings.ScriptFile = AppendScriptName(settings.ScriptFile); - } - - if (Platform.Platform.JsonExists(json, "fontDirectory")) - { - settings.FontDirectory = EnsureFullUrl(json.fontDirectory); - } - - if (Platform.Platform.JsonExists(json, "smallGraceTabNotes")) - { - settings.SmallGraceTabNotes = json.smallGraceTabNotes; - } - else if (dataAttributes != null && dataAttributes.ContainsKey("smallGraceTabNotes")) - { - settings.SmallGraceTabNotes = (bool)dataAttributes["smallGraceTabNotes"]; - } - - if (Platform.Platform.JsonExists(json, "fingeringMode")) - { - settings.FingeringMode = DecodeFingeringMode(json.fingeringMode); - } - else if (dataAttributes != null && dataAttributes.ContainsKey("fingeringMode")) - { - settings.FingeringMode = DecodeFingeringMode(dataAttributes["fingeringMode"]); - } - - if (Platform.Platform.JsonExists(json, "extendBendArrowsOnTiedNotes")) - { - settings.ExtendBendArrowsOnTiedNotes = json.extendBendArrowsOnTiedNotes; - } - else if (dataAttributes != null && dataAttributes.ContainsKey("extendBendArrowsOnTiedNotes")) - { - settings.ExtendBendArrowsOnTiedNotes = (bool)dataAttributes["extendBendArrowsOnTiedNotes"]; - } - - if (Platform.Platform.JsonExists(json, "showParenthesisForTiedBends")) - { - settings.ShowParenthesisForTiedBends = json.showParenthesisForTiedBends; - } - else if (dataAttributes != null && dataAttributes.ContainsKey("showParenthesisForTiedBends")) - { - settings.ShowParenthesisForTiedBends = (bool)dataAttributes["showParenthesisForTiedBends"]; - } - - if (Platform.Platform.JsonExists(json, "showTabNoteOnTiedBend")) - { - settings.ShowTabNoteOnTiedBend = json.showTabNoteOnTiedBend; - } - else if (dataAttributes != null && dataAttributes.ContainsKey("showTabNoteOnTiedBend")) - { - settings.ShowTabNoteOnTiedBend = (bool)dataAttributes["showTabNoteOnTiedBend"]; - } - - if (Platform.Platform.JsonExists(json, "showZeroOnDiveWhammy")) - { - settings.ShowZeroOnDiveWhammy = json.showZeroOnDiveWhammy; - } - else if (dataAttributes != null && dataAttributes.ContainsKey("showZeroOnDiveWhammy")) - { - settings.ShowZeroOnDiveWhammy = (bool)dataAttributes["showZeroOnDiveWhammy"]; - } - - if (Platform.Platform.JsonExists(json, "extendLineEffectsToBeatEnd")) - { - settings.ExtendLineEffectsToBeatEnd = json.extendLineEffectsToBeatEnd; - } - else if (dataAttributes != null && dataAttributes.ContainsKey("extendLineEffectsToBeatEnd")) - { - settings.ExtendLineEffectsToBeatEnd = (bool)dataAttributes["extendLineEffectsToBeatEnd"]; - } - - if (Platform.Platform.JsonExists(json, "songBookBendDuration")) - { - settings.SongBookBendDuration = json.songBookBendDuration; - } - else if (dataAttributes != null && dataAttributes.ContainsKey("songBookBendDuration")) - { - settings.SongBookBendDuration = (int)dataAttributes["songBookBendDuration"]; - } - - - if (Platform.Platform.JsonExists(json, "songBookDipDuration")) - { - settings.SongBookDipDuration = json.songBookDipDuration; - } - else if (dataAttributes != null && dataAttributes.ContainsKey("songBookDipDuration")) - { - settings.SongBookDipDuration = (int)dataAttributes["songBookDipDuration"]; - } - - if (Platform.Platform.JsonExists(json, "layout")) - { - settings.Layout = LayoutFromJson(json.layout); - } - else if (dataAttributes != null && dataAttributes.ContainsKey("layout")) - { - settings.Layout = LayoutFromJson(dataAttributes["layout"]); - } - - - if (Platform.Platform.JsonExists(json, "includeNoteBounds")) - { - settings.IncludeNoteBounds = json.includeNoteBounds; - } - else if (dataAttributes != null && dataAttributes.ContainsKey("includeNoteBounds")) - { - settings.IncludeNoteBounds = (bool)dataAttributes["includeNoteBounds"]; - } - - if (Platform.Platform.JsonExists(json, "vibrato")) - { - var vibrato = json.vibrato; - if (vibrato.noteSlightAmplitude) - { - settings.Vibrato.NoteSlightAmplitude = vibrato.noteSlightAmplitude; - } - if (vibrato.noteWideAmplitude) - { - settings.Vibrato.NoteWideAmplitude = vibrato.noteWideAmplitude; - } - if (vibrato.noteSlightLength) - { - settings.Vibrato.NoteSlightLength = vibrato.noteSlightLength; - } - if (vibrato.noteWideLength) - { - settings.Vibrato.NoteWideLength = vibrato.noteWideLength; - } - if (vibrato.beatSlightAmplitude) - { - settings.Vibrato.BeatSlightAmplitude = vibrato.beatSlightAmplitude; - } - if (vibrato.beatWideAmplitude) - { - settings.Vibrato.BeatWideAmplitude = vibrato.beatWideAmplitude; - } - if (vibrato.beatSlightLength) - { - settings.Vibrato.BeatSlightLength = vibrato.beatSlightLength; - } - if (vibrato.beatWideLength) - { - settings.Vibrato.BeatWideLength = vibrato.beatWideLength; - } - } - else if (dataAttributes != null) - { - if (dataAttributes.ContainsKey("vibratoNoteSlightLength")) - { - settings.Vibrato.NoteSlightLength = (int) dataAttributes["vibratoNoteSlightLength"]; - } - if (dataAttributes.ContainsKey("vibratoNoteSlightAmplitude")) - { - settings.Vibrato.NoteSlightAmplitude = (int) dataAttributes["vibratoNoteSlightAmplitude"]; - } - if (dataAttributes.ContainsKey("vibratoNoteWideLength")) - { - settings.Vibrato.NoteWideLength = (int) dataAttributes["vibratoNoteWideLength"]; - } - if (dataAttributes.ContainsKey("vibratoNoteWideAmplitude")) - { - settings.Vibrato.NoteWideAmplitude = (int) dataAttributes["vibratoNoteWideAmplitude"]; - } - if (dataAttributes.ContainsKey("vibratoBeatSlightLength")) - { - settings.Vibrato.BeatSlightLength = (int) dataAttributes["vibratoBeatSlightLength"]; - } - if (dataAttributes.ContainsKey("vibratoBeatSlightAmplitude")) - { - settings.Vibrato.BeatSlightAmplitude = (int) dataAttributes["vibratoBeatSlightAmplitude"]; - } - if (dataAttributes.ContainsKey("vibratoBeatWideLength")) - { - settings.Vibrato.BeatWideLength = (int) dataAttributes["vibratoBeatWideLength"]; - } - if (dataAttributes.ContainsKey("vibratoBeatWideAmplitude")) - { - settings.Vibrato.BeatWideAmplitude = (int) dataAttributes["vibratoBeatWideAmplitude"]; - } - } - - if (dataAttributes != null) - { - foreach (var key in dataAttributes) - { - if (key.StartsWith("layout")) - { - var property = key.Substring(6); - settings.Layout.AdditionalSettings[property.ToLower()] = dataAttributes[key]; - } - } - } - - if (Platform.Platform.JsonExists(json, "staves")) - { - settings.Staves = StavesFromJson(json.staves); - } - else if (dataAttributes != null && dataAttributes.ContainsKey("staves")) - { - settings.Staves = StavesFromJson(dataAttributes["staves"]); - } - - if (dataAttributes != null) - { - foreach (var key in dataAttributes) - { - if (key.StartsWith("staves")) - { - var property = key.Substring(6); - settings.Staves.AdditionalSettings[property.ToLower()] = dataAttributes[key]; - } - } - } - - if (Platform.Platform.JsonExists(json, "player")) - { - settings.EnablePlayer = true; - settings.SoundFontFile = json.player; - } - else if (dataAttributes != null && dataAttributes.ContainsKey("player")) - { - settings.EnablePlayer = true; - settings.SoundFontFile = (string)dataAttributes["player"]; - } - - if (settings.EnablePlayer) - { - FillPlayerOptions(settings, json, true, dataAttributes); - } - - if (Platform.Platform.JsonExists(json, "importer")) - { - string[] keys2 = Platform.Platform.JsonKeys(json.importer); - foreach (var key2 in keys2) - { - settings.ImporterSettings[key2.ToLower()] = json.importer[key2]; - } - } - else if (dataAttributes != null) - { - foreach (var key in dataAttributes) - { - if (key.StartsWith("importer")) - { - var property = key.Substring(8); - settings.ImporterSettings[property.ToLower()] = dataAttributes[key]; - } - } - } - } - - - private static DisplayMode DecodeDisplayMode(object mode) - { - if (Platform.Platform.TypeOf(mode) == "number") - { - return (DisplayMode)mode; - } - - if (Platform.Platform.TypeOf(mode) == "string") - { - var s = (string)mode; - switch (s.ToLower()) - { - case "songbook": - return DisplayMode.SongBook; - case "guitarpro": - return DisplayMode.GuitarPro; - } - } - - return DisplayMode.GuitarPro; - } - - private static FingeringMode DecodeFingeringMode(object mode) - { - if (Platform.Platform.TypeOf(mode) == "number") - { - return (FingeringMode)mode; - } - - if (Platform.Platform.TypeOf(mode) == "string") - { - var s = (string)mode; - switch (s.ToLower()) - { - case "score": - return FingeringMode.Score; - case "effectband": - return FingeringMode.SingleNoteEffectBand; - } - } - - return FingeringMode.Score; - } - - private static LogLevel DecodeLogLevel(object log) - { - if (Platform.Platform.TypeOf(log) == "number") - { - return (LogLevel) log; - } - - if (Platform.Platform.TypeOf(log) == "string") - { - var s = (string) log; - switch (s.ToLower()) - { - case "none": - return LogLevel.None; - case "debug": - return LogLevel.Debug; - case "info": - return LogLevel.Info; - case "warning": - return LogLevel.Warning; - case "error": - return LogLevel.Error; - } - } - - return LogLevel.Info; - } - - private static void FillCursorOffset(Settings settings, object playerOffset) - { - if (Platform.Platform.TypeOf(playerOffset) == "number") - { - settings.ScrollOffsetX = (int)playerOffset; - settings.ScrollOffsetY = (int)playerOffset; - } - else if (Platform.Platform.JsonExists(playerOffset, "length")) - { - var offsets = (int[])playerOffset; - settings.ScrollOffsetX = offsets[0]; - settings.ScrollOffsetY = offsets[1]; - } - } - - private static StaveSettings StavesFromJson(dynamic json) - { - StaveSettings staveSettings; - if (Script.Write("untyped __typeof__(json) == \"string\"")) - { - staveSettings = new StaveSettings(json); - } - else if (json.id) - { - staveSettings = new StaveSettings(json.id); - if (json.additionalSettings) - { - string[] keys2 = Platform.Platform.JsonKeys(json.additionalSettings); - foreach (var key2 in keys2) - { - staveSettings.AdditionalSettings[key2.ToLower()] = json.additionalSettings[key2]; - } - } - } - else - { - return new StaveSettings("score-tab"); - } - return staveSettings; - } - - public static LayoutSettings LayoutFromJson(dynamic json) - { - var layout = new LayoutSettings(); - if (Script.Write("untyped __typeof__(json) == \"string\"")) - { - layout.Mode = json; - } - else - { - if (json.mode) layout.Mode = json.mode; - if (json.additionalSettings) - { - string[] keys = Platform.Platform.JsonKeys(json.additionalSettings); - foreach (var key in keys) - { - layout.AdditionalSettings[key.ToLower()] = json.additionalSettings[key]; - } - } - } - return layout; - } - - private static string AppendScriptName(string url) - { - // append script name - if (!string.IsNullOrEmpty(url) && !url.EndsWith(".js")) - { - if (!url.EndsWith("/")) - { - url += "/"; - } - url += "AlphaTab.js"; - } - return url; - } - - private static string EnsureFullUrl(string relativeUrl) - { - var global = Script.Write("js.Lib.global"); - if (!relativeUrl.StartsWith("http") && !relativeUrl.StartsWith("https") && !relativeUrl.StartsWith("file")) - { - var root = new StringBuilder(); - root.Append(global.location.protocol); - root.Append("//"); - if (global.location.hostname) - { - root.Append(global.location.hostname); - } - if (global.location.port) - { - root.Append(":"); - root.Append(global.location.port); - } - - // as it is not clearly defined how slashes are treated in the location object - // better be safe than sorry here - string directory = global.location.pathname.split("/").slice(0, -1).join("/"); - if (directory.Length > 0) - { - if (!directory.StartsWith("/")) root.Append("/"); - root.Append(directory); - } - - - if (!relativeUrl.StartsWith("/")) root.Append("/"); - root.Append(relativeUrl); - - return root.ToString(); - } - - return relativeUrl; - } - } -} diff --git a/Source/AlphaTab.JavaScript/Utils/UnionData.cs b/Source/AlphaTab.JavaScript/Utils/UnionData.cs deleted file mode 100644 index c588dc1db..000000000 --- a/Source/AlphaTab.JavaScript/Utils/UnionData.cs +++ /dev/null @@ -1,42 +0,0 @@ -using Haxe.Js.Html; -using Phase; -using Phase.Attributes; - -namespace AlphaTab.Utils -{ - [Abstract("js.html.DataView")] - class UnionData - { - [Inline] - public UnionData() => Script.AbstractThis = new DataView(new ArrayBuffer(8)); - - //double values - public double Double1 - { - [Inline] - get { return Script.This().GetFloat64(0, true); } - } - //float values - public double Float1 - { - [Inline] - get { return Script.This().GetFloat32(0, true); } - } - public double Float2 - { - [Inline] - get { return Script.This().GetFloat32(4, true); } - } - //int values - public double Int1 - { - [Inline] - get { return Script.This().GetInt32(0, true); } - } - public double Int2 - { - [Inline] - get { return Script.This().GetInt32(4, true); } - } - } -} diff --git a/Source/AlphaTab.JavaScript/phase.json b/Source/AlphaTab.JavaScript/phase.json deleted file mode 100644 index e998efa0f..000000000 --- a/Source/AlphaTab.JavaScript/phase.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "output": "..\\..\\Build\\Haxe\\src-gen", - //"processorCount": 1, - "postbuild": [ - { - "name": "Building JavaScript", - "executable": "haxe", - "arguments": "-cp ..\\..\\Phase\\Mscorlib -cp ..\\..\\Build\\Haxe\\src -cp ..\\..\\Build\\Haxe\\src-gen -js ..\\..\\Build\\JavaScript\\AlphaTab.js -main alphaTab.Main -D js-unflatten -dce full --macro addGlobalMetadata('alphaTab','@:expose') --macro keep('alphaTab',null,true) -D source-header= -D doc-gen" - } - ,{ - "name": "Minify JavaScript", - "executable": "cmd", - "arguments": "/C uglifyjs ..\\..\\Build\\JavaScript\\AlphaTab.js -o ..\\..\\Build\\JavaScript\\AlphaTab.min.js -c" - }, - { - "name": "Add Source Headers", - "executable": "node", - "arguments": "tools\\addSourceHeader.js Tools\\header.js Build\\JavaScript\\AlphaTab.js Build\\JavaScript\\AlphaTab.min.js", - "workingDirectory": "..\\..\\" - } - ] -} \ No newline at end of file diff --git a/Source/AlphaTab.Test.CSharp/AlphaTab.Test.CSharp.csproj b/Source/AlphaTab.Test.CSharp/AlphaTab.Test.CSharp.csproj deleted file mode 100644 index d067122b8..000000000 --- a/Source/AlphaTab.Test.CSharp/AlphaTab.Test.CSharp.csproj +++ /dev/null @@ -1,30 +0,0 @@ - - - AlphaTab.Test.CSharp - AlphaTab.Test.CSharp - net471 - AlphaTab.Test.CSharp - $(NoWarn);0626;0824 - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Source/AlphaTab.Test.CSharp/TestPlatform.cs b/Source/AlphaTab.Test.CSharp/TestPlatform.cs deleted file mode 100644 index 30f9c1aca..000000000 --- a/Source/AlphaTab.Test.CSharp/TestPlatform.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; -using System.IO; -using System.Text; -using System.Text.RegularExpressions; -using AlphaTab.IO; - -namespace AlphaTab.Test -{ - class TestPlatform - { - public static IReadable CreateStringReader(string tex) - { - return ByteBuffer.FromBuffer(Encoding.UTF8.GetBytes(tex)); - } - - public static byte[] LoadFile(string fileName) - { - return File.ReadAllBytes(fileName); - } - - public static string LoadFileAsString(string fileName) - { - return File.ReadAllText(fileName); - } - - public static bool IsMatch(string value, string regex) - { - return Regex.IsMatch(value, regex); - } - - public static string ChangeExtension(string file, string extension) - { - var lastDot = file.LastIndexOf("."); - if (lastDot == -1) - { - return file + extension; - } - else - { - return file.Substring(0, lastDot) + extension; - } - } - } -} diff --git a/Source/AlphaTab.Test.CSharp/phase.json b/Source/AlphaTab.Test.CSharp/phase.json deleted file mode 100644 index e1d6b4640..000000000 --- a/Source/AlphaTab.Test.CSharp/phase.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "output": "Haxe\\src-gen", - "processorCount": 1, - "postbuild": [ - { - "name": "Building JavaScript (debug)", - "executable": "haxe", - "arguments": "-cp ..\\..\\Phase\\Mscorlib -cp Haxe\\src -cp Haxe\\src-gen -js Haxe\\bin\\AlphaTab.Test.js -main alphaTab.Main -debug -lib munit" - } - ] -} \ No newline at end of file diff --git a/Source/AlphaTab.Test.Js/AlphaTab.Test.Js.csproj b/Source/AlphaTab.Test.Js/AlphaTab.Test.Js.csproj deleted file mode 100644 index ec0adf81a..000000000 --- a/Source/AlphaTab.Test.Js/AlphaTab.Test.Js.csproj +++ /dev/null @@ -1,40 +0,0 @@ - - - AlphaTab.Test.Js - AlphaTab.Test.Js - net471 - $(NoWarn);0626;0824 - {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - - - - - - - - - - - - - - - - - - - - - - - - - ..\..\Phase\Compiler\Phase.Core.dll - - - - - - - - \ No newline at end of file diff --git a/Source/AlphaTab.Test.Js/Haxe/src/alphaTab/test/AddResources.hx b/Source/AlphaTab.Test.Js/Haxe/src/alphaTab/test/AddResources.hx deleted file mode 100644 index abba8cc9b..000000000 --- a/Source/AlphaTab.Test.Js/Haxe/src/alphaTab/test/AddResources.hx +++ /dev/null @@ -1,44 +0,0 @@ -package alphaTab.test; - -class AddResources -{ - private static var Extensions = [".gp3", ".gp4", ".gp5", ".gpx", ".gp", ".xml", ".sf2"]; - public static macro function run() - { - var sourceDir = haxe.io.Path.join([Sys.getCwd(), "..", "AlphaTab.Test", "TestFiles"]); - trace('Adding resources from '+ sourceDir); - - var resourcePrefix = "TestFiles"; - - importRecursive(sourceDir, resourcePrefix); - - return macro null; - } - - public static function importRecursive(sourceDir:String, sourcePrefix:String) - { - var files = sys.FileSystem.readDirectory(sourceDir); - trace('Adding resources from '+ sourceDir); - for(file in files) - { - var fullName = haxe.io.Path.join([sourceDir, file]); - var resourceName = sourcePrefix + "/" + file; - if(sys.FileSystem.isDirectory(fullName)) - { - importRecursive(fullName, resourceName); - } - else - { - var i = file.lastIndexOf("."); - if(i >= 0) - { - var ext = file.substring(i); - if(Extensions.indexOf(ext) != -1) - { - haxe.macro.Context.addResource(resourceName, haxe.io.Bytes.alloc(0)); - } - } - } - } - } -} diff --git a/Source/AlphaTab.Test.Js/Haxe/src/alphaTab/test/Jasmine.hx b/Source/AlphaTab.Test.Js/Haxe/src/alphaTab/test/Jasmine.hx deleted file mode 100644 index 8c531b8d0..000000000 --- a/Source/AlphaTab.Test.Js/Haxe/src/alphaTab/test/Jasmine.hx +++ /dev/null @@ -1,96 +0,0 @@ -package alphaTab.test; - -#if macro - -import haxe.macro.*; -import haxe.macro.Type; -import haxe.macro.Expr; - -class Jasmine -{ - macro static function generateJasmineSuites() - { - var allTypes = new Array(); - Context.onGenerate(function(types:Array) { - for(type in types) - { - switch(type) - { - case TInst(t,params): - var classType = t.get(); - if(classType.meta.has(":testClass")) - { - allTypes.push(classType); - } - default: - } - } - }); - - Context.onAfterGenerate(function() { - trace('Collecting Test Suites'); - - var dir = haxe.io.Path.directory(Compiler.getOutput()); - var sourceFile = haxe.io.Path.withoutDirectory(Compiler.getOutput()); - var specFile = haxe.io.Path.join([dir, "alphaTab.tests.specs.js"]); - if(sys.FileSystem.exists(specFile)) - { - sys.FileSystem.deleteFile(specFile); - } - var output = sys.io.File.write(specFile, false); - - output.writeString("/// \r\n"); - - for(t in allTypes) - { - var typeName = t.pack.join(".") + "." + t.name; - - var func = (t.meta.has(":testIgnore")) ? "xdescribe" : "describe"; - - output.writeString(func + "(\"" + typeName + "\", function() {\r\n"); - output.writeString(" var __instance = new " + typeName + "();\r\n"); - - var fields = t.fields.get(); - for(field in fields) - { - switch(field.kind) - { - case FMethod(k): - if(k == MethNormal && field.meta.has(":testMethod")) - { - output.writeString(" it(\""+field.name+"\", function() {\r\n"); - - var ignoreInfo = field.meta.extract(":testIgnore"); - if(ignoreInfo.length > 0) - { - var reason:String = "Test ignored"; - - if (ignoreInfo[0].params != null && ignoreInfo[0].params.length > 0) - { - reason = haxe.macro.ExprTools.getValue(ignoreInfo[0].params[0]); - } - output.writeString(" pending(\"" + reason + "\");\r\n"); - } - output.writeString(" __instance."+field.name+"();\r\n"); - - output.writeString(" });\r\n"); - } - default: - } - } - - output.writeString("});\r\n"); - } - - output.writeString("// End of test registration\r\n"); - - output.flush(); - output.close(); - - trace('Collecting Test Suites done'); - - }); - return null; - } -#end -} \ No newline at end of file diff --git a/Source/AlphaTab.Test.Js/Haxe/src/alphaTab/test/Main.hx b/Source/AlphaTab.Test.Js/Haxe/src/alphaTab/test/Main.hx deleted file mode 100644 index 573467060..000000000 --- a/Source/AlphaTab.Test.Js/Haxe/src/alphaTab/test/Main.hx +++ /dev/null @@ -1,75 +0,0 @@ -package alphaTab.test; - -import alphaTab.test.audio.MidiFileGeneratorTest; -import alphaTab.test.audio.MidiPlaybackControllerTest; -import alphaTab.test.audio.AlphaSynthTests; - -import alphaTab.test.importer.AlphaTexImporterTest; -import alphaTab.test.importer.Gp3ImporterTest; -import alphaTab.test.importer.Gp4ImporterTest; -import alphaTab.test.importer.Gp5ImporterTest; -import alphaTab.test.importer.GpxImporterTest; -import alphaTab.test.importer.Gp7ImporterTest; -import alphaTab.test.importer.MusicXmlImporterSamplesTests; -import alphaTab.test.importer.MusicXmlImporterTestSuiteTests; - -import alphaTab.test.model.LyricsTest; -import alphaTab.test.model.TuningParserTest; - -import alphaTab.test.xml.XmlParseTest; - -class Main -{ - static function main() - { - var allresources = haxe.Resource.listNames(); - var loaded = 0; - - trace('Loading resources ('+loaded+'/' + allresources.length + ')'); - - var resourceContent : Array<{ name : String, data : String, str : String }> = Reflect.getProperty(haxe.Resource, "content"); - - for(res in allresources) - { - var xhr = new js.html.XMLHttpRequest(); - xhr.open("GET", res, true); - untyped xhr.resource = res; - xhr.responseType = js.html.XMLHttpRequestResponseType.ARRAYBUFFER; - xhr.onreadystatechange = function(e) - { - var currentRes = untyped __js__("this.resource"); - - if(xhr.readyState == js.html.XMLHttpRequest.DONE) - { - loaded++; - trace('Loading resources ('+loaded+'/' + allresources.length + ')'); - var e:js.html.CustomEvent = cast js.Browser.document.createEvent("CustomEvent"); - e.initCustomEvent("alphaTab.test.status", false, false, { - message: 'Loading resources ('+loaded+'/' + allresources.length + ')' - }); - js.Browser.document.dispatchEvent(e); - - var response:js.html.ArrayBuffer = xhr.response; - var resourceData = haxe.crypto.Base64.encode(haxe.io.Bytes.ofData(response)); - - for(r in resourceContent) - { - if(r.name == currentRes) - { - r.data = resourceData; - } - } - - if(loaded == allresources.length) - { - trace('Launching tests'); - e = cast js.Browser.document.createEvent("CustomEvent"); - e.initCustomEvent("alphaTab.test.run", false, false, null); - js.Browser.document.dispatchEvent(e); - } - } - }; - xhr.send(); - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab.Test.Js/TestFramework/Assert.cs b/Source/AlphaTab.Test.Js/TestFramework/Assert.cs deleted file mode 100644 index 05a7c4b65..000000000 --- a/Source/AlphaTab.Test.Js/TestFramework/Assert.cs +++ /dev/null @@ -1,148 +0,0 @@ -using System; -using Phase; -using Phase.Attributes; - -namespace Microsoft.VisualStudio.TestTools.UnitTesting -{ - [Name("alphaTab.test.Assert")] - static class Assert - { - public static void AreEqual(T1 expected, T2 actual) - { - var expectedNull = expected == null; - var actualNull = actual == null; - - bool equal = true; - if (expectedNull != actualNull) - { - equal = false; - } - else if (!expectedNull) - { - equal = expected.Equals(actual); - } - - if (!equal) - { - Fail("Value [" + (actualNull ? "null" : actual.ToString()) + "] was not equal to expected value [" + (expectedNull ? "null" : expected.ToString()) + "]"); - } - else - { - Script.Write("untyped __js__(\"expect().nothing()\");"); - } - } - - public static void AreEqual(T1 expected, T2 actual, string message) - { - var expectedNull = expected == null; - var actualNull = actual == null; - - bool equal = true; - if (expectedNull != actualNull) - { - equal = false; - } - else if (!expectedNull) - { - equal = expected.Equals(actual); - } - - if (!equal) - { - Fail(message); - } - else - { - Script.Write("untyped __js__(\"expect().nothing()\");"); - } - } - - public static void AreEqual(T1 expected, T2 actual, string message, params object[] arguments) - { - var expectedNull = expected == null; - var actualNull = actual == null; - - bool equal = true; - if (expectedNull != actualNull) - { - equal = false; - } - else if (!expectedNull) - { - equal = expected.Equals(actual); - } - - if (!equal) - { - Fail(string.Format(message, arguments)); - } - else - { - Script.Write("untyped __js__(\"expect().nothing()\");"); - } - } - - [Inline] - public static void Fail() - { - Script.Write("untyped __js__(\"fail()\");"); - } - - [Inline] - public static void Fail(string reason) - { - Script.Write("untyped __js__(\"fail({0})\", reason);"); - } - - [Inline] - public static void Fail(string reason, params object[] arguments) - { - var msg = string.Format(reason, arguments); - Script.Write("untyped __js__(\"fail({0})\", msg);"); - } - - [Inline] - public static void Inconclusive() - { - Script.Write("untyped __js__(\"pending()\");"); - } - - [Inline] - public static void Inconclusive(string reason) - { - Script.Write("untyped __js__(\"pending({0})\", reason);"); - } - - [Inline] - public static void Inconclusive(string reason, params object[] arguments) - { - var msg = string.Format(reason, arguments); - Script.Write("untyped __js__(\"pending({0})\", msg);"); - } - - [Inline] - public static void IsNotNull(T actual) where T : class - { - Script.Write("untyped __js__(\"expect({0}).toBeTruthy()\", actual);"); - } - - [Inline] - public static void IsNull(T actual) where T : class - { - Script.Write("untyped __js__(\"expect({0}).toBeFalsy()\", actual);"); - } - - [Inline] - public static void IsTrue(bool actual) - { - Script.Write("untyped __js__(\"expect({0}).toBe(true)\", actual);"); - - } - - [Inline] - public static void IsFalse(bool actual) - { - Script.Write("untyped __js__(\"expect({0}).toBe(false)\", actual);"); - } - } -} diff --git a/Source/AlphaTab.Test.Js/TestFramework/IgnoreAttribute.cs b/Source/AlphaTab.Test.Js/TestFramework/IgnoreAttribute.cs deleted file mode 100644 index 85dac7555..000000000 --- a/Source/AlphaTab.Test.Js/TestFramework/IgnoreAttribute.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using Phase.Attributes; - -namespace Microsoft.VisualStudio.TestTools.UnitTesting -{ - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = false, AllowMultiple = true)] - [Meta("@:testIgnore")] - [External] - sealed class IgnoreAttribute : Attribute - { - public extern IgnoreAttribute(); - public extern IgnoreAttribute(string reason); - } -} \ No newline at end of file diff --git a/Source/AlphaTab.Test.Js/TestFramework/TestClassAttribute.cs b/Source/AlphaTab.Test.Js/TestFramework/TestClassAttribute.cs deleted file mode 100644 index 763d651d4..000000000 --- a/Source/AlphaTab.Test.Js/TestFramework/TestClassAttribute.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using Phase.Attributes; - -namespace Microsoft.VisualStudio.TestTools.UnitTesting -{ - [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)] - [External] - [Meta("@:testClass")] - sealed class TestClassAttribute : Attribute - { - } -} \ No newline at end of file diff --git a/Source/AlphaTab.Test.Js/TestFramework/TestMethodAttribute.cs b/Source/AlphaTab.Test.Js/TestFramework/TestMethodAttribute.cs deleted file mode 100644 index a96d53cc0..000000000 --- a/Source/AlphaTab.Test.Js/TestFramework/TestMethodAttribute.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using Phase.Attributes; - -namespace Microsoft.VisualStudio.TestTools.UnitTesting -{ - [AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = true)] - [External] - [Meta("@:testMethod")] - sealed class TestMethodAttribute : Attribute - { - } -} diff --git a/Source/AlphaTab.Test.Js/TestPlatform.cs b/Source/AlphaTab.Test.Js/TestPlatform.cs deleted file mode 100644 index de7056597..000000000 --- a/Source/AlphaTab.Test.Js/TestPlatform.cs +++ /dev/null @@ -1,51 +0,0 @@ -using AlphaTab.IO; -using Haxe.Js.Html; -using Phase; - -namespace AlphaTab.Test -{ - class TestPlatform - { - public static IReadable CreateStringReader(string tex) - { - var buf = new ArrayBuffer(tex.Length * 2); - var view = new Uint16Array(buf); - for (int i = 0; i < tex.Length; i++) - { - view[i] = tex[i]; - } - return ByteBuffer.FromBuffer(view.As()); - } - - public static byte[] LoadFile(string path) - { - path = path.Replace("\\", "/"); - return new Uint8Array(Script.Write("haxe.Resource.getBytes(path).getData()")).As(); - } - - public static string LoadFileAsString(string path) - { - path = path.Replace("\\", "/"); - return Script.Write("haxe.Resource.getString(path)"); - } - - public static bool IsMatch(string value, string regex) - { - return Script.Write("new EReg(regex, \"\").match(value)"); - } - - public static string ChangeExtension(string file, string extension) - { - var lastDot = file.LastIndexOf("."); - if (lastDot == -1) - { - return file + extension; - } - else - { - return file.Substring(0, lastDot) + extension; - } - } - - } -} diff --git a/Source/AlphaTab.Test.Js/phase.json b/Source/AlphaTab.Test.Js/phase.json deleted file mode 100644 index 67cf00173..000000000 --- a/Source/AlphaTab.Test.Js/phase.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "output": "Haxe\\src-gen", - //"processorCount": 1, - "postbuild": [ - { - "name": "Building JavaScript (debug)", - "executable": "haxe", - "arguments": "-cp ..\\..\\Phase\\Mscorlib -cp ..\\..\\Build\\Haxe\\src -cp ..\\..\\Build\\Haxe\\src-gen -cp Haxe\\src -cp Haxe\\src-gen -js test\\alphaTab.tests.js -main alphaTab.test.Main -debug -D js-unflatten --macro addGlobalMetadata('alphaTab','@:expose') --macro keep('alphaTab',null,true) --macro alphaTab.test.AddResources.run() --macro alphaTab.test.Jasmine.generateJasmineSuites()" - }, - { - "name": "Copying testfiles to runner directory", - "executable": "cmd.exe", - "arguments": "/C xcopy ..\\AlphaTab.Test\\TestFiles test\\TestFiles\\ /Y /S /D" - } - ] -} \ No newline at end of file diff --git a/Source/AlphaTab.Test.Js/test/StartTests.bat b/Source/AlphaTab.Test.Js/test/StartTests.bat deleted file mode 100644 index 00a62080f..000000000 --- a/Source/AlphaTab.Test.Js/test/StartTests.bat +++ /dev/null @@ -1,5 +0,0 @@ -@echo off -echo Starting HTTP Server -start http-server -p 8001 -echo Opening Feature Sample -start http://localhost:8001/ \ No newline at end of file diff --git a/Source/AlphaTab.Test.Js/test/alphaTab.tests.specs.js b/Source/AlphaTab.Test.Js/test/alphaTab.tests.specs.js deleted file mode 100644 index 2783a1a8d..000000000 --- a/Source/AlphaTab.Test.Js/test/alphaTab.tests.specs.js +++ /dev/null @@ -1,977 +0,0 @@ -/// -describe("alphaTab.test.audio.AlphaSynthTests", function() { - var __instance = new alphaTab.test.audio.AlphaSynthTests(); - it("TestLoadSf2PatchBank", function() { - __instance.TestLoadSf2PatchBank(); - }); - it("TestPcmGeneration", function() { - __instance.TestPcmGeneration(); - }); -}); -describe("alphaTab.test.audio.MidiFileGeneratorTest", function() { - var __instance = new alphaTab.test.audio.MidiFileGeneratorTest(); - it("TestCorrectMidiOrder", function() { - __instance.TestCorrectMidiOrder(); - }); - it("TestBend", function() { - __instance.TestBend(); - }); - it("TestGraceBeatGeneration", function() { - __instance.TestGraceBeatGeneration(); - }); - it("TestBendMultiPoint", function() { - __instance.TestBendMultiPoint(); - }); -}); -describe("alphaTab.test.audio.MidiPlaybackControllerTest", function() { - var __instance = new alphaTab.test.audio.MidiPlaybackControllerTest(); - it("TestRepeatClose", function() { - __instance.TestRepeatClose(); - }); - it("TestRepeatCloseMulti", function() { - __instance.TestRepeatCloseMulti(); - }); - it("TestRepeatCloseWithoutStartAtBeginning", function() { - __instance.TestRepeatCloseWithoutStartAtBeginning(); - }); - it("TestRepeatCloseAlternateEndings", function() { - __instance.TestRepeatCloseAlternateEndings(); - }); -}); -describe("alphaTab.test.importer.AlphaTexImporterTest", function() { - var __instance = new alphaTab.test.importer.AlphaTexImporterTest(); - it("EnsureMetadataParsing_Issue73", function() { - __instance.EnsureMetadataParsing_Issue73(); - }); - it("TestTuning", function() { - __instance.TestTuning(); - }); - it("DeadNotes1_Issue79", function() { - __instance.DeadNotes1_Issue79(); - }); - it("DeadNotes2_Issue79", function() { - __instance.DeadNotes2_Issue79(); - }); - it("Trill_Issue79", function() { - __instance.Trill_Issue79(); - }); - it("Tremolo_Issue79", function() { - __instance.Tremolo_Issue79(); - }); - it("TremoloPicking_Issue79", function() { - __instance.TremoloPicking_Issue79(); - }); - it("Hamonics_Issue79", function() { - __instance.Hamonics_Issue79(); - }); - it("HamonicsRenderingText_Issue79", function() { - __instance.HamonicsRenderingText_Issue79(); - }); - it("Grace_Issue79", function() { - __instance.Grace_Issue79(); - }); - it("BendRendering_Issue79", function() { - __instance.BendRendering_Issue79(); - }); - it("TestLeftHandFingerSingleNote", function() { - __instance.TestLeftHandFingerSingleNote(); - }); - it("TestRightHandFingerSingleNote", function() { - __instance.TestRightHandFingerSingleNote(); - }); - it("TestLeftHandFingerChord", function() { - __instance.TestLeftHandFingerChord(); - }); - it("TestRightHandFingerChord", function() { - __instance.TestRightHandFingerChord(); - }); - it("TestUnstringed", function() { - __instance.TestUnstringed(); - }); -}); -describe("alphaTab.test.importer.Gp3ImporterTest", function() { - var __instance = new alphaTab.test.importer.Gp3ImporterTest(); - it("TestScoreInfo", function() { - __instance.TestScoreInfo(); - }); - it("TestNotes", function() { - __instance.TestNotes(); - }); - it("TestTimeSignatures", function() { - __instance.TestTimeSignatures(); - }); - it("TestDead", function() { - __instance.TestDead(); - }); - it("TestAccentuation", function() { - __instance.TestAccentuation(); - }); - it("TestGuitarPro3Harmonics", function() { - __instance.TestGuitarPro3Harmonics(); - }); - it("TestHammer", function() { - __instance.TestHammer(); - }); - it("TestBend", function() { - __instance.TestBend(); - }); - it("TestSlides", function() { - __instance.TestSlides(); - }); - it("TestGuitarPro3Vibrato", function() { - __instance.TestGuitarPro3Vibrato(); - }); - it("TestOtherEffects", function() { - __instance.TestOtherEffects(); - }); - it("TestStroke", function() { - __instance.TestStroke(); - }); - it("TestTuplets", function() { - __instance.TestTuplets(); - }); - it("TestRanges", function() { - __instance.TestRanges(); - }); - it("TestEffects", function() { - __instance.TestEffects(); - }); - it("TestStrings", function() { - __instance.TestStrings(); - }); -}); -describe("alphaTab.test.importer.Gp4ImporterTest", function() { - var __instance = new alphaTab.test.importer.Gp4ImporterTest(); - it("TestScoreInfo", function() { - __instance.TestScoreInfo(); - }); - it("TestNotes", function() { - __instance.TestNotes(); - }); - it("TestTimeSignatures", function() { - __instance.TestTimeSignatures(); - }); - it("TestDead", function() { - __instance.TestDead(); - }); - it("TestGrace", function() { - __instance.TestGrace(); - }); - it("TestAccentuation", function() { - __instance.TestAccentuation(); - }); - it("TestHarmonics", function() { - __instance.TestHarmonics(); - }); - it("TestHammer", function() { - __instance.TestHammer(); - }); - it("TestBend", function() { - __instance.TestBend(); - }); - it("TestTremolo", function() { - __instance.TestTremolo(); - }); - it("TestSlides", function() { - __instance.TestSlides(); - }); - it("TestVibrato", function() { - __instance.TestVibrato(); - }); - it("TestTrills", function() { - __instance.TestTrills(); - }); - it("TestOtherEffects", function() { - __instance.TestOtherEffects(); - }); - it("TestFingering", function() { - __instance.TestFingering(); - }); - it("TestStroke", function() { - __instance.TestStroke(); - }); - it("TestTuplets", function() { - __instance.TestTuplets(); - }); - it("TestRanges", function() { - __instance.TestRanges(); - }); - it("TestEffects", function() { - __instance.TestEffects(); - }); - it("TestStrings", function() { - __instance.TestStrings(); - }); - it("TestColors", function() { - __instance.TestColors(); - }); -}); -describe("alphaTab.test.importer.Gp5ImporterTest", function() { - var __instance = new alphaTab.test.importer.Gp5ImporterTest(); - it("TestScoreInfo", function() { - __instance.TestScoreInfo(); - }); - it("TestNotes", function() { - __instance.TestNotes(); - }); - it("TestTimeSignatures", function() { - __instance.TestTimeSignatures(); - }); - it("TestDead", function() { - __instance.TestDead(); - }); - it("TestGrace", function() { - __instance.TestGrace(); - }); - it("TestAccentuation", function() { - __instance.TestAccentuation(); - }); - it("TestHarmonics", function() { - __instance.TestHarmonics(); - }); - it("TestHammer", function() { - __instance.TestHammer(); - }); - it("TestBend", function() { - __instance.TestBend(); - }); - it("TestTremolo", function() { - __instance.TestTremolo(); - }); - it("TestSlides", function() { - __instance.TestSlides(); - }); - it("TestVibrato", function() { - __instance.TestVibrato(); - }); - it("TestTrills", function() { - __instance.TestTrills(); - }); - it("TestOtherEffects", function() { - __instance.TestOtherEffects(); - }); - it("TestFingering", function() { - __instance.TestFingering(); - }); - it("TestStroke", function() { - __instance.TestStroke(); - }); - it("TestTuplets", function() { - __instance.TestTuplets(); - }); - it("TestRanges", function() { - __instance.TestRanges(); - }); - it("TestEffects", function() { - __instance.TestEffects(); - }); - it("TestSerenade", function() { - __instance.TestSerenade(); - }); - it("TestStrings", function() { - __instance.TestStrings(); - }); - it("TestKeySignatures", function() { - __instance.TestKeySignatures(); - }); - it("TestChords", function() { - __instance.TestChords(); - }); - it("TestColors", function() { - __instance.TestColors(); - }); - it("TestCanon", function() { - __instance.TestCanon(); - }); -}); -describe("alphaTab.test.importer.Gp7ImporterTest", function() { - var __instance = new alphaTab.test.importer.Gp7ImporterTest(); - it("TestScoreInfo", function() { - __instance.TestScoreInfo(); - }); - it("TestNotes", function() { - __instance.TestNotes(); - }); - it("TestTimeSignatures", function() { - __instance.TestTimeSignatures(); - }); - it("TestDead", function() { - __instance.TestDead(); - }); - it("TestGrace", function() { - __instance.TestGrace(); - }); - it("TestAccentuation", function() { - __instance.TestAccentuation(); - }); - it("TestHarmonics", function() { - __instance.TestHarmonics(); - }); - it("TestHammer", function() { - __instance.TestHammer(); - }); - it("TestNumber", function() { - __instance.TestNumber(); - }); - it("TestBend", function() { - __instance.TestBend(); - }); - it("TestBendAdvanced", function() { - __instance.TestBendAdvanced(); - }); - it("TestWhammyAdvanced", function() { - __instance.TestWhammyAdvanced(); - }); - it("TestTremolo", function() { - __instance.TestTremolo(); - }); - it("TestSlides", function() { - __instance.TestSlides(); - }); - it("TestVibrato", function() { - __instance.TestVibrato(); - }); - it("TestTrills", function() { - __instance.TestTrills(); - }); - it("TestOtherEffects", function() { - __instance.TestOtherEffects(); - }); - it("TestFingering", function() { - __instance.TestFingering(); - }); - it("TestStroke", function() { - __instance.TestStroke(); - }); - it("TestTuplets", function() { - __instance.TestTuplets(); - }); - it("TestRanges", function() { - __instance.TestRanges(); - }); - it("TestEffects", function() { - __instance.TestEffects(); - }); - it("TestSerenade", function() { - __instance.TestSerenade(); - }); - it("TestStrings", function() { - __instance.TestStrings(); - }); - it("TestKeySignatures", function() { - __instance.TestKeySignatures(); - }); - it("TestChords", function() { - __instance.TestChords(); - }); - it("TestColors", function() { - __instance.TestColors(); - }); - it("TestTremoloVibrato", function() { - __instance.TestTremoloVibrato(); - }); - it("TestOttavia", function() { - __instance.TestOttavia(); - }); - it("TestSimileMark", function() { - __instance.TestSimileMark(); - }); - it("TestFermata", function() { - __instance.TestFermata(); - }); - it("TestPickSlide", function() { - __instance.TestPickSlide(); - }); -}); -describe("alphaTab.test.importer.GpxImporterTest", function() { - var __instance = new alphaTab.test.importer.GpxImporterTest(); - it("TestFileSystemCompressed", function() { - __instance.TestFileSystemCompressed(); - }); - it("TestScoreInfo", function() { - __instance.TestScoreInfo(); - }); - it("TestNotes", function() { - __instance.TestNotes(); - }); - it("TestTimeSignatures", function() { - __instance.TestTimeSignatures(); - }); - it("TestDead", function() { - __instance.TestDead(); - }); - it("TestGrace", function() { - __instance.TestGrace(); - }); - it("TestAccentuation", function() { - __instance.TestAccentuation(); - }); - it("TestHarmonics", function() { - __instance.TestHarmonics(); - }); - it("TestHammer", function() { - __instance.TestHammer(); - }); - it("TestBend", function() { - __instance.TestBend(); - }); - it("TestTremolo", function() { - __instance.TestTremolo(); - }); - it("TestSlides", function() { - __instance.TestSlides(); - }); - it("TestVibrato", function() { - __instance.TestVibrato(); - }); - it("TestTrills", function() { - __instance.TestTrills(); - }); - it("TestOtherEffects", function() { - __instance.TestOtherEffects(); - }); - it("TestFingering", function() { - __instance.TestFingering(); - }); - it("TestStroke", function() { - __instance.TestStroke(); - }); - it("TestTuplets", function() { - __instance.TestTuplets(); - }); - it("TestRanges", function() { - __instance.TestRanges(); - }); - it("TestEffects", function() { - __instance.TestEffects(); - }); - it("TestSerenade", function() { - __instance.TestSerenade(); - }); - it("TestStrings", function() { - __instance.TestStrings(); - }); - it("TestKeySignatures", function() { - __instance.TestKeySignatures(); - }); - it("TestChords", function() { - __instance.TestChords(); - }); - it("TestColors", function() { - __instance.TestColors(); - }); -}); -xdescribe("alphaTab.test.importer.MusicXmlImporterSamplesTests", function() { - var __instance = new alphaTab.test.importer.MusicXmlImporterSamplesTests(); - it("Test_ActorPreludeSample", function() { - __instance.Test_ActorPreludeSample(); - }); - it("Test_BeetAnGeSample", function() { - __instance.Test_BeetAnGeSample(); - }); - it("Test_Binchois", function() { - __instance.Test_Binchois(); - }); - it("Test_BrahWiMeSample", function() { - __instance.Test_BrahWiMeSample(); - }); - it("Test_BrookeWestSample", function() { - __instance.Test_BrookeWestSample(); - }); - it("Test_Chant", function() { - __instance.Test_Chant(); - }); - it("Test_DebuMandSample", function() { - __instance.Test_DebuMandSample(); - }); - it("Test_Dichterliebe01", function() { - __instance.Test_Dichterliebe01(); - }); - it("Test_Echigo", function() { - __instance.Test_Echigo(); - }); - it("Test_FaurReveSample", function() { - __instance.Test_FaurReveSample(); - }); - it("Test_MahlFaGe4Sample", function() { - __instance.Test_MahlFaGe4Sample(); - }); - it("Test_MozaChloSample", function() { - __instance.Test_MozaChloSample(); - }); - it("Test_MozartPianoSonata", function() { - __instance.Test_MozartPianoSonata(); - }); - it("Test_MozartTrio", function() { - __instance.Test_MozartTrio(); - }); - it("Test_MozaVeilSample", function() { - __instance.Test_MozaVeilSample(); - }); - it("Test_Saltarello", function() { - __instance.Test_Saltarello(); - }); - it("Test_SchbAvMaSample", function() { - __instance.Test_SchbAvMaSample(); - }); - it("Test_Telemann", function() { - __instance.Test_Telemann(); - }); -}); -xdescribe("alphaTab.test.importer.MusicXmlImporterTestSuiteTests", function() { - var __instance = new alphaTab.test.importer.MusicXmlImporterTestSuiteTests(); - it("Test_01a_Pitches_Pitches", function() { - __instance.Test_01a_Pitches_Pitches(); - }); - it("Test_01b_Pitches_Intervals", function() { - __instance.Test_01b_Pitches_Intervals(); - }); - it("Test_01c_Pitches_NoVoiceElement", function() { - __instance.Test_01c_Pitches_NoVoiceElement(); - }); - it("Test_01d_Pitches_Microtones", function() { - __instance.Test_01d_Pitches_Microtones(); - }); - it("Test_01e_Pitches_ParenthesizedAccidentals", function() { - __instance.Test_01e_Pitches_ParenthesizedAccidentals(); - }); - it("Test_01f_Pitches_ParenthesizedMicrotoneAccidentals", function() { - __instance.Test_01f_Pitches_ParenthesizedMicrotoneAccidentals(); - }); - it("Test_02a_Rests_Durations", function() { - __instance.Test_02a_Rests_Durations(); - }); - it("Test_02b_Rests_PitchedRests", function() { - __instance.Test_02b_Rests_PitchedRests(); - }); - it("Test_02c_Rests_MultiMeasureRests", function() { - __instance.Test_02c_Rests_MultiMeasureRests(); - }); - it("Test_02d_Rests_Multimeasure_TimeSignatures", function() { - __instance.Test_02d_Rests_Multimeasure_TimeSignatures(); - }); - it("Test_02e_Rests_NoType", function() { - __instance.Test_02e_Rests_NoType(); - }); - it("Test_03a_Rhythm_Durations", function() { - __instance.Test_03a_Rhythm_Durations(); - }); - it("Test_03b_Rhythm_Backup", function() { - __instance.Test_03b_Rhythm_Backup(); - }); - it("Test_03c_Rhythm_DivisionChange", function() { - __instance.Test_03c_Rhythm_DivisionChange(); - }); - it("Test_03d_Rhythm_DottedDurations_Factors", function() { - __instance.Test_03d_Rhythm_DottedDurations_Factors(); - }); - it("Test_11a_TimeSignatures", function() { - __instance.Test_11a_TimeSignatures(); - }); - it("Test_11b_TimeSignatures_NoTime", function() { - __instance.Test_11b_TimeSignatures_NoTime(); - }); - it("Test_11c_TimeSignatures_CompoundSimple", function() { - __instance.Test_11c_TimeSignatures_CompoundSimple(); - }); - it("Test_11d_TimeSignatures_CompoundMultiple", function() { - __instance.Test_11d_TimeSignatures_CompoundMultiple(); - }); - it("Test_11e_TimeSignatures_CompoundMixed", function() { - __instance.Test_11e_TimeSignatures_CompoundMixed(); - }); - it("Test_11f_TimeSignatures_SymbolMeaning", function() { - __instance.Test_11f_TimeSignatures_SymbolMeaning(); - }); - it("Test_11g_TimeSignatures_SingleNumber", function() { - __instance.Test_11g_TimeSignatures_SingleNumber(); - }); - it("Test_11h_TimeSignatures_SenzaMisura", function() { - __instance.Test_11h_TimeSignatures_SenzaMisura(); - }); - it("Test_12a_Clefs", function() { - __instance.Test_12a_Clefs(); - }); - it("Test_12b_Clefs_NoKeyOrClef", function() { - __instance.Test_12b_Clefs_NoKeyOrClef(); - }); - it("Test_13a_KeySignatures", function() { - __instance.Test_13a_KeySignatures(); - }); - it("Test_13b_KeySignatures_ChurchModes", function() { - __instance.Test_13b_KeySignatures_ChurchModes(); - }); - it("Test_13c_KeySignatures_NonTraditional", function() { - __instance.Test_13c_KeySignatures_NonTraditional(); - }); - it("Test_13d_KeySignatures_Microtones", function() { - __instance.Test_13d_KeySignatures_Microtones(); - }); - it("Test_14a_StaffDetails_LineChanges", function() { - __instance.Test_14a_StaffDetails_LineChanges(); - }); - it("Test_21a_Chord_Basic", function() { - __instance.Test_21a_Chord_Basic(); - }); - it("Test_21b_Chords_TwoNotes", function() { - __instance.Test_21b_Chords_TwoNotes(); - }); - it("Test_21c_Chords_ThreeNotesDuration", function() { - __instance.Test_21c_Chords_ThreeNotesDuration(); - }); - it("Test_21d_Chords_SchubertStabatMater", function() { - __instance.Test_21d_Chords_SchubertStabatMater(); - }); - it("Test_21e_Chords_PickupMeasures", function() { - __instance.Test_21e_Chords_PickupMeasures(); - }); - it("Test_21f_Chord_ElementInBetween", function() { - __instance.Test_21f_Chord_ElementInBetween(); - }); - it("Test_22a_Noteheads", function() { - __instance.Test_22a_Noteheads(); - }); - it("Test_22b_Staff_Notestyles", function() { - __instance.Test_22b_Staff_Notestyles(); - }); - it("Test_22c_Noteheads_Chords", function() { - __instance.Test_22c_Noteheads_Chords(); - }); - it("Test_22d_Parenthesized_Noteheads", function() { - __instance.Test_22d_Parenthesized_Noteheads(); - }); - it("Test_23a_Tuplets", function() { - __instance.Test_23a_Tuplets(); - }); - it("Test_23b_Tuplets_Styles", function() { - __instance.Test_23b_Tuplets_Styles(); - }); - it("Test_23c_Tuplet_Display_NonStandard", function() { - __instance.Test_23c_Tuplet_Display_NonStandard(); - }); - it("Test_23d_Tuplets_Nested", function() { - __instance.Test_23d_Tuplets_Nested(); - }); - it("Test_23e_Tuplets_Tremolo", function() { - __instance.Test_23e_Tuplets_Tremolo(); - }); - it("Test_23f_Tuplets_DurationButNoBracket", function() { - __instance.Test_23f_Tuplets_DurationButNoBracket(); - }); - it("Test_24a_GraceNotes", function() { - __instance.Test_24a_GraceNotes(); - }); - it("Test_24b_ChordAsGraceNote", function() { - __instance.Test_24b_ChordAsGraceNote(); - }); - it("Test_24c_GraceNote_MeasureEnd", function() { - __instance.Test_24c_GraceNote_MeasureEnd(); - }); - it("Test_24d_AfterGrace", function() { - __instance.Test_24d_AfterGrace(); - }); - it("Test_24e_GraceNote_StaffChange", function() { - __instance.Test_24e_GraceNote_StaffChange(); - }); - it("Test_24f_GraceNote_Slur", function() { - __instance.Test_24f_GraceNote_Slur(); - }); - it("Test_31a_Directions", function() { - __instance.Test_31a_Directions(); - }); - it("Test_31c_MetronomeMarks", function() { - __instance.Test_31c_MetronomeMarks(); - }); - it("Test_32a_Notations", function() { - __instance.Test_32a_Notations(); - }); - it("Test_32b_Articulations_Texts", function() { - __instance.Test_32b_Articulations_Texts(); - }); - it("Test_32c_MultipleNotationChildren", function() { - __instance.Test_32c_MultipleNotationChildren(); - }); - it("Test_32d_Arpeggio", function() { - __instance.Test_32d_Arpeggio(); - }); - it("Test_33a_Spanners", function() { - __instance.Test_33a_Spanners(); - }); - it("Test_33b_Spanners_Tie", function() { - __instance.Test_33b_Spanners_Tie(); - }); - it("Test_33c_Spanners_Slurs", function() { - __instance.Test_33c_Spanners_Slurs(); - }); - it("Test_33d_Spanners_OctaveShifts", function() { - __instance.Test_33d_Spanners_OctaveShifts(); - }); - it("Test_33e_Spanners_OctaveShifts_InvalidSize", function() { - __instance.Test_33e_Spanners_OctaveShifts_InvalidSize(); - }); - it("Test_33f_Trill_EndingOnGraceNote", function() { - __instance.Test_33f_Trill_EndingOnGraceNote(); - }); - it("Test_33g_Slur_ChordedNotes", function() { - __instance.Test_33g_Slur_ChordedNotes(); - }); - it("Test_33h_Spanners_Glissando", function() { - __instance.Test_33h_Spanners_Glissando(); - }); - it("Test_33i_Ties_NotEnded", function() { - __instance.Test_33i_Ties_NotEnded(); - }); - it("Test_41a_MultiParts_Partorder", function() { - __instance.Test_41a_MultiParts_Partorder(); - }); - it("Test_41b_MultiParts_MoreThan10", function() { - __instance.Test_41b_MultiParts_MoreThan10(); - }); - it("Test_41c_StaffGroups", function() { - __instance.Test_41c_StaffGroups(); - }); - it("Test_41d_StaffGroups_Nested", function() { - __instance.Test_41d_StaffGroups_Nested(); - }); - it("Test_41e_StaffGroups_InstrumentNames_Linebroken", function() { - __instance.Test_41e_StaffGroups_InstrumentNames_Linebroken(); - }); - it("Test_41f_StaffGroups_Overlapping", function() { - __instance.Test_41f_StaffGroups_Overlapping(); - }); - it("Test_41g_PartNoId", function() { - __instance.Test_41g_PartNoId(); - }); - it("Test_41h_TooManyParts", function() { - __instance.Test_41h_TooManyParts(); - }); - it("Test_41i_PartNameDisplay_Override", function() { - __instance.Test_41i_PartNameDisplay_Override(); - }); - it("Test_42a_MultiVoice_TwoVoicesOnStaff_Lyrics", function() { - __instance.Test_42a_MultiVoice_TwoVoicesOnStaff_Lyrics(); - }); - it("Test_42b_MultiVoice_MidMeasureClefChange", function() { - __instance.Test_42b_MultiVoice_MidMeasureClefChange(); - }); - it("Test_43a_PianoStaff", function() { - __instance.Test_43a_PianoStaff(); - }); - it("Test_43b_MultiStaff_DifferentKeys", function() { - __instance.Test_43b_MultiStaff_DifferentKeys(); - }); - it("Test_43c_MultiStaff_DifferentKeysAfterBackup", function() { - __instance.Test_43c_MultiStaff_DifferentKeysAfterBackup(); - }); - it("Test_43d_MultiStaff_StaffChange", function() { - __instance.Test_43d_MultiStaff_StaffChange(); - }); - it("Test_43e_Multistaff_ClefDynamics", function() { - __instance.Test_43e_Multistaff_ClefDynamics(); - }); - it("Test_45a_SimpleRepeat", function() { - __instance.Test_45a_SimpleRepeat(); - }); - it("Test_45b_RepeatWithAlternatives", function() { - __instance.Test_45b_RepeatWithAlternatives(); - }); - it("Test_45c_RepeatMultipleTimes", function() { - __instance.Test_45c_RepeatMultipleTimes(); - }); - it("Test_45d_Repeats_Nested_Alternatives", function() { - __instance.Test_45d_Repeats_Nested_Alternatives(); - }); - it("Test_45e_Repeats_Nested_Alternatives", function() { - __instance.Test_45e_Repeats_Nested_Alternatives(); - }); - it("Test_45f_Repeats_InvalidEndings", function() { - __instance.Test_45f_Repeats_InvalidEndings(); - }); - it("Test_45g_Repeats_NotEnded", function() { - __instance.Test_45g_Repeats_NotEnded(); - }); - it("Test_46a_Barlines", function() { - __instance.Test_46a_Barlines(); - }); - it("Test_46b_MidmeasureBarline", function() { - __instance.Test_46b_MidmeasureBarline(); - }); - it("Test_46c_Midmeasure_Clef", function() { - __instance.Test_46c_Midmeasure_Clef(); - }); - it("Test_46d_PickupMeasure_ImplicitMeasures", function() { - __instance.Test_46d_PickupMeasure_ImplicitMeasures(); - }); - it("Test_46e_PickupMeasure_SecondVoiceStartsLater", function() { - __instance.Test_46e_PickupMeasure_SecondVoiceStartsLater(); - }); - it("Test_46f_IncompleteMeasures", function() { - __instance.Test_46f_IncompleteMeasures(); - }); - it("Test_46g_PickupMeasure_Chordnames_FiguredBass", function() { - __instance.Test_46g_PickupMeasure_Chordnames_FiguredBass(); - }); - it("Test_51b_Header_Quotes", function() { - __instance.Test_51b_Header_Quotes(); - }); - it("Test_51c_MultipleRights", function() { - __instance.Test_51c_MultipleRights(); - }); - it("Test_51d_EmptyTitle", function() { - __instance.Test_51d_EmptyTitle(); - }); - it("Test_52a_PageLayout", function() { - __instance.Test_52a_PageLayout(); - }); - it("Test_52b_Breaks", function() { - __instance.Test_52b_Breaks(); - }); - it("Test_61a_Lyrics", function() { - __instance.Test_61a_Lyrics(); - }); - it("Test_61b_MultipleLyrics", function() { - __instance.Test_61b_MultipleLyrics(); - }); - it("Test_61c_Lyrics_Pianostaff", function() { - __instance.Test_61c_Lyrics_Pianostaff(); - }); - it("Test_61d_Lyrics_Melisma", function() { - __instance.Test_61d_Lyrics_Melisma(); - }); - it("Test_61e_Lyrics_Chords", function() { - __instance.Test_61e_Lyrics_Chords(); - }); - it("Test_61f_Lyrics_GracedNotes", function() { - __instance.Test_61f_Lyrics_GracedNotes(); - }); - it("Test_61g_Lyrics_NameNumber", function() { - __instance.Test_61g_Lyrics_NameNumber(); - }); - it("Test_61h_Lyrics_BeamsMelismata", function() { - __instance.Test_61h_Lyrics_BeamsMelismata(); - }); - it("Test_61i_Lyrics_Chords", function() { - __instance.Test_61i_Lyrics_Chords(); - }); - it("Test_61j_Lyrics_Elisions", function() { - __instance.Test_61j_Lyrics_Elisions(); - }); - it("Test_61k_Lyrics_SpannersExtenders", function() { - __instance.Test_61k_Lyrics_SpannersExtenders(); - }); - it("Test_71a_Chordnames", function() { - __instance.Test_71a_Chordnames(); - }); - it("Test_71c_ChordsFrets", function() { - __instance.Test_71c_ChordsFrets(); - }); - it("Test_71d_ChordsFrets_Multistaff", function() { - __instance.Test_71d_ChordsFrets_Multistaff(); - }); - it("Test_71e_TabStaves", function() { - __instance.Test_71e_TabStaves(); - }); - it("Test_71f_AllChordTypes", function() { - __instance.Test_71f_AllChordTypes(); - }); - it("Test_71g_MultipleChordnames", function() { - __instance.Test_71g_MultipleChordnames(); - }); - it("Test_72a_TransposingInstruments", function() { - __instance.Test_72a_TransposingInstruments(); - }); - it("Test_72b_TransposingInstruments_Full", function() { - __instance.Test_72b_TransposingInstruments_Full(); - }); - it("Test_72c_TransposingInstruments_Change", function() { - __instance.Test_72c_TransposingInstruments_Change(); - }); - it("Test_73a_Percussion", function() { - __instance.Test_73a_Percussion(); - }); - it("Test_74a_FiguredBass", function() { - __instance.Test_74a_FiguredBass(); - }); - it("Test_75a_AccordionRegistrations", function() { - __instance.Test_75a_AccordionRegistrations(); - }); - it("Test_99a_Sibelius5_IgnoreBeaming", function() { - __instance.Test_99a_Sibelius5_IgnoreBeaming(); - }); - it("Test_99b_Lyrics_BeamsMelismata_IgnoreBeams", function() { - __instance.Test_99b_Lyrics_BeamsMelismata_IgnoreBeams(); - }); -}); -describe("alphaTab.test.model.LyricsTest", function() { - var __instance = new alphaTab.test.model.LyricsTest(); - it("TestApplySingleLineFirstBar", function() { - __instance.TestApplySingleLineFirstBar(); - }); - it("TestApplySingleLineBarOffset", function() { - __instance.TestApplySingleLineBarOffset(); - }); - it("TestApplyMultiLineFirstBar", function() { - __instance.TestApplyMultiLineFirstBar(); - }); - it("TestApplyMultiLineBarOffset", function() { - __instance.TestApplyMultiLineBarOffset(); - }); - it("TestSpaces", function() { - __instance.TestSpaces(); - }); - it("TestNewLines", function() { - __instance.TestNewLines(); - }); - it("TestDash", function() { - __instance.TestDash(); - }); - it("TestPlus", function() { - __instance.TestPlus(); - }); - it("TestComments", function() { - __instance.TestComments(); - }); -}); -describe("alphaTab.test.model.TuningParserTest", function() { - var __instance = new alphaTab.test.model.TuningParserTest(); - it("TestStandard", function() { - __instance.TestStandard(); - }); -}); -describe("alphaTab.test.xml.XmlParseTest", function() { - var __instance = new alphaTab.test.xml.XmlParseTest(); - it("ParseSimple", function() { - __instance.ParseSimple(); - }); - it("ParseShorthand", function() { - __instance.ParseShorthand(); - }); - it("ParseSingleAttribute", function() { - __instance.ParseSingleAttribute(); - }); - it("ParseMultipleAttributes", function() { - __instance.ParseMultipleAttributes(); - }); - it("ParseSimpleText", function() { - __instance.ParseSimpleText(); - }); - it("ParseChild", function() { - __instance.ParseChild(); - }); - it("ParseMultiChild", function() { - __instance.ParseMultiChild(); - }); - it("ParseComments", function() { - __instance.ParseComments(); - }); - it("ParseDoctype", function() { - __instance.ParseDoctype(); - }); - it("ParseXmlHeadTest", function() { - __instance.ParseXmlHeadTest(); - }); - it("ParseFull", function() { - __instance.ParseFull(); - }); -}); -// End of test registration diff --git a/Source/AlphaTab.Test.Js/test/index.html b/Source/AlphaTab.Test.Js/test/index.html deleted file mode 100644 index 7a2c64a7e..000000000 --- a/Source/AlphaTab.Test.Js/test/index.html +++ /dev/null @@ -1,222 +0,0 @@ - - - - - AlphaTab Test Runner - - - - - - - - - - - - - - - -
    -
    -
    -
    - - - diff --git a/Source/AlphaTab.Test/AlphaTab.Test.projitems b/Source/AlphaTab.Test/AlphaTab.Test.projitems deleted file mode 100644 index b639bace6..000000000 --- a/Source/AlphaTab.Test/AlphaTab.Test.projitems +++ /dev/null @@ -1,1448 +0,0 @@ - - - - $(MSBuildAllProjects);$(MSBuildThisFileFullPath) - true - ce43d0c5-8a17-4f8f-9623-3a7a52ad02f4 - - - AlphaTab.Test - - - - - - - - - - - - - - - - - - - - - - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - \ No newline at end of file diff --git a/Source/AlphaTab.Test/AlphaTab.Test.shproj b/Source/AlphaTab.Test/AlphaTab.Test.shproj deleted file mode 100644 index 065d892f7..000000000 --- a/Source/AlphaTab.Test/AlphaTab.Test.shproj +++ /dev/null @@ -1,13 +0,0 @@ - - - - ce43d0c5-8a17-4f8f-9623-3a7a52ad02f4 - 14.0 - - - - - - - - diff --git a/Source/AlphaTab.Test/Audio/AlphaSynthTests.cs b/Source/AlphaTab.Test/Audio/AlphaSynthTests.cs deleted file mode 100644 index 99bac099d..000000000 --- a/Source/AlphaTab.Test/Audio/AlphaSynthTests.cs +++ /dev/null @@ -1,163 +0,0 @@ -using System; -using System.IO; -using AlphaTab.Audio.Generator; -using AlphaTab.Audio.Synth; -using AlphaTab.Audio.Synth.Bank; -using AlphaTab.Audio.Synth.Ds; -using AlphaTab.Audio.Synth.Midi; -using AlphaTab.Audio.Synth.Util; -using AlphaTab.Collections; -using AlphaTab.Importer; -using AlphaTab.IO; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace AlphaTab.Test.Audio -{ - [TestClass] - public class AlphaSynthTests - { - [TestMethod] - public void TestLoadSf2PatchBank() - { - var data = TestPlatform.LoadFile("TestFiles/Audio/default.sf2"); - var patchBank = new PatchBank(); - var input = ByteBuffer.FromBuffer(data); - patchBank.LoadSf2(input); - - Assert.AreEqual("GS sound set (16 bit)", patchBank.Name); - Assert.AreEqual("960920 ver. 1.00.16", patchBank.Comments); - Assert.AreEqual("0,1,2,3,4,5,6,7,8,9,16,24,32,128", string.Join(",", patchBank.LoadedBanks)); - - var gmBank = patchBank.GetBank(0); - var expectedPatches = new string[] - { - "Piano 1", "Piano 2", "Piano 3", "Honky-tonk", "E.Piano 1", "E.Piano 2", "Harpsichord", "Clav.", - "Celesta", "Glockenspiel", "Music Box", "Vibraphone", "Marimba", "Xylophone", "Tubular-bell", "Santur", "Organ 1", - "Organ 2", "Organ 3", "Church Org.1", "Reed Organ", "Accordion Fr", "Harmonica", "Bandoneon", - "Nylon-str.Gt", "Steel-str.Gt", "Jazz Gt.", "Clean Gt.", "Muted Gt.", "Overdrive Gt", "DistortionGt", "Gt.Harmonics", - "Acoustic Bs.", "Fingered Bs.", "Picked Bs.", "Fretless Bs.", "Slap Bass 1", "Slap Bass 2", "Synth Bass 1", - "Synth Bass 2", "Violin", "Viola", "Cello", "Contrabass", "Tremolo Str", "PizzicatoStr", "Harp", "Timpani", "Strings", - "Slow Strings", "Syn.Strings1", "Syn.Strings2", "Choir Aahs", "Voice Oohs", "SynVox", "OrchestraHit", "Trumpet", "Trombone", "Tuba", - "MutedTrumpet", "French Horns", "Brass 1", "Synth Brass1", "Synth Brass2", "Soprano Sax", "Alto Sax", "Tenor Sax", "Baritone Sax", - "Oboe", "English Horn", "Bassoon", "Clarinet", "Piccolo", "Flute", "Recorder", "Pan Flute", "Bottle Blow", "Shakuhachi", "Whistle", - "Ocarina", "Square Wave", "Saw Wave", "Syn.Calliope", "Chiffer Lead", "Charang", "Solo Vox", "5th Saw Wave", - "Bass & Lead", "Fantasia", "Warm Pad", "Polysynth", "Space Voice", "Bowed Glass", "Metal Pad", "Halo Pad", "Sweep Pad", - "Ice Rain", "Soundtrack", "Crystal", "Atmosphere", "Brightness", "Goblin", "Echo Drops", "Star Theme", "Sitar", - "Banjo", "Shamisen", "Koto", "Kalimba", "Bagpipe", "Fiddle", "Shanai", "Tinkle Bell", "Agogo", "Steel Drums", "Woodblock", - "Taiko", "Melo. Tom 1", "Synth Drum", "Reverse Cym.", "Gt.FretNoise", "Breath Noise", "Seashore", "Bird", "Telephone 1", - "Helicopter", "Applause", "Gun Shot" - }; - var actualPatches = new FastList(); - foreach (var patch in gmBank) - { - if (patch != null) - { - actualPatches.Add(patch.Name); - } - } - Assert.AreEqual(string.Join(",", expectedPatches), string.Join(",", actualPatches)); - } - - [TestMethod] - public void TestPcmGeneration() - { - var tex = "\\tempo 102 \\tuning E4 B3 G3 D3 A2 E2 \\instrument 25 . r.8 (0.4 0.3 ).8 " + - "(-.3 -.4 ).2 {d } | (0.4 0.3 ).8 r.8 (3.3 3.4 ).8 r.8 (5.4 5.3 ).4 r.8 (0.4 0.3 ).8 |" + - " r.8 (3.4 3.3 ).8 r.8 (6.3 6.4 ).8 (5.4 5.3 ).4 {d }r.8 |" + - " (0.4 0.3).8 r.8(3.4 3.3).8 r.8(5.4 5.3).4 r.8(3.4 3.3).8 | " + - "r.8(0.4 0.3).8(-.3 - .4).2 { d } | "; - var importer = new AlphaTexImporter(); - importer.Init(TestPlatform.CreateStringReader(tex)); - var score = importer.ReadScore(); - - var midi = new MidiFile(); - var gen = new MidiFileGenerator(score, null, new AlphaSynthMidiFileHandler(midi)); - gen.Generate(); - - var testOutput = new TestOutput(); - var synth = new AlphaSynth(testOutput); - synth.LoadSoundFont(TestPlatform.LoadFile("TestFiles/Audio/default.sf2")); - synth.LoadMidi(midi); - - synth.Play(); - - var finished = false; - synth.Finished += b => finished = true; - - while (!finished) - { - testOutput.Continue(); - } - - //Console.WriteLine(testOutput.Samples.Count); - //using (var writer = new BinaryWriter(new FileStream("test.pcm", FileMode.Create, FileAccess.Write))) - //{ - // for (int i = 0; i < testOutput.Samples.Count; i++) - // { - // writer.Write(testOutput.Samples[i]); - // } - //} - } - } - - class TestOutput : ISynthOutput - { - private bool _finished; - - public int SampleRate - { - get { return 44100; } - } - - public FastList Samples { get; set; } - - public void Open() - { - Samples = new FastList(); - Ready(); - } - - public void SequencerFinished() - { - _finished = true; - } - - public void Play() - { - } - - public void Continue() - { - if (_finished) - { - Finished(); - } - else - { - SampleRequest(); - } - } - - public void Pause() - { - } - - public void AddSamples(SampleArray f) - { - for (int i = 0; i < f.Length; i++) - { - Samples.Add(f[i]); - } - SamplesPlayed(f.Length); - } - - public void ResetSamples() - { - } - - public event Action Ready; - public event Action SamplesPlayed; - public event Action SampleRequest; - public event Action Finished; - } -} diff --git a/Source/AlphaTab.Test/Audio/FlatMidiEventGenerator.cs b/Source/AlphaTab.Test/Audio/FlatMidiEventGenerator.cs deleted file mode 100644 index 3d18a6a21..000000000 --- a/Source/AlphaTab.Test/Audio/FlatMidiEventGenerator.cs +++ /dev/null @@ -1,417 +0,0 @@ -using System.Collections.Generic; -using AlphaTab.Audio.Generator; -using AlphaTab.Collections; -using AlphaTab.Model; - -namespace AlphaTab.Test.Audio -{ - public class FlatMidiEventGenerator : IMidiFileHandler - { - public abstract class MidiEvent - { - public int Tick { get; set; } - - public override string ToString() - { - return "Tick[" + Tick + "]"; - } - - protected bool Equals(MidiEvent other) - { - return Tick == other.Tick; - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; - return Equals((MidiEvent) obj); - } - - public override int GetHashCode() - { - return Tick; - } - } - - public abstract class TrackMidiEvent : MidiEvent - { - public int Track { get; set; } - - public override string ToString() - { - return base.ToString() + " Track[" + Track + "]"; - } - - protected bool Equals(TrackMidiEvent other) - { - return base.Equals(other) && Track == other.Track; - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; - return Equals((TrackMidiEvent) obj); - } - - public override int GetHashCode() - { - unchecked - { - return (base.GetHashCode()*397) ^ Track; - } - } - } - - public abstract class ChannelMidiEvent : TrackMidiEvent - { - public int Channel { get; set; } - - public override string ToString() - { - return base.ToString() + " Channel[" + Channel + "]"; - } - - protected bool Equals(ChannelMidiEvent other) - { - return base.Equals(other) && Channel == other.Channel; - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; - return Equals((ChannelMidiEvent) obj); - } - - public override int GetHashCode() - { - unchecked - { - return (base.GetHashCode()*397) ^ Channel; - } - } - } - - public class TimeSignatureEvent : MidiEvent - { - public int Numerator { get; set; } - public int Denominator { get; set; } - - public override string ToString() - { - return "TimeSignature: " + base.ToString() + " Numerator[" + Numerator + "] Denominator[" + Denominator + "]"; - } - - protected bool Equals(TimeSignatureEvent other) - { - return base.Equals(other) && Numerator == other.Numerator && Denominator == other.Denominator; - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; - return Equals((TimeSignatureEvent) obj); - } - - public override int GetHashCode() - { - unchecked - { - int hashCode = base.GetHashCode(); - hashCode = (hashCode*397) ^ Numerator; - hashCode = (hashCode*397) ^ Denominator; - return hashCode; - } - } - } - - public class RestEvent : ChannelMidiEvent - { - public override string ToString() - { - return "Rest: " + base.ToString(); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; - return Equals((RestEvent) obj); - } - } - - public class NoteEvent : ChannelMidiEvent - { - public int Length { get; set; } - public byte Key { get; set; } - public DynamicValue DynamicValue { get; set; } - - public override string ToString() - { - return "Note: " + base.ToString() + " Length[" + Length + "] Key[" + Key + "] Dynamic[" + DynamicValue + "]"; - } - - protected bool Equals(NoteEvent other) - { - return base.Equals(other) && Length == other.Length && Key == other.Key && DynamicValue == other.DynamicValue; - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; - return Equals((NoteEvent) obj); - } - - public override int GetHashCode() - { - unchecked - { - int hashCode = base.GetHashCode(); - hashCode = (hashCode*397) ^ Length; - hashCode = (hashCode*397) ^ Key.GetHashCode(); - hashCode = (hashCode*397) ^ (int) DynamicValue; - return hashCode; - } - } - } - - public class ControlChangeEvent : ChannelMidiEvent - { - public byte Controller { get; set; } - public byte Value { get; set; } - - public override string ToString() - { - return "ControlChange: " + base.ToString() + " Controller[" + Controller + "] Value[" + Value + "]"; - } - - protected bool Equals(ControlChangeEvent other) - { - return base.Equals(other) && Controller == other.Controller && Value == other.Value; - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; - return Equals((ControlChangeEvent) obj); - } - - public override int GetHashCode() - { - unchecked - { - int hashCode = base.GetHashCode(); - hashCode = (hashCode*397) ^ Controller.GetHashCode(); - hashCode = (hashCode*397) ^ Value.GetHashCode(); - return hashCode; - } - } - } - - public class ProgramChangeEvent : ChannelMidiEvent - { - public byte Program { get; set; } - - public override string ToString() - { - return "ProgramChange: " + base.ToString() + " Program[" + Program + "]"; - } - - protected bool Equals(ProgramChangeEvent other) - { - return base.Equals(other) && Program == other.Program; - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; - return Equals((ProgramChangeEvent) obj); - } - - public override int GetHashCode() - { - unchecked - { - return (base.GetHashCode()*397) ^ Program.GetHashCode(); - } - } - } - - public class TempoEvent : MidiEvent - { - public int Tempo { get; set; } - - public override string ToString() - { - return "Tempo: " + base.ToString() + " Tempo[" + Tempo + "]"; - } - - protected bool Equals(TempoEvent other) - { - return base.Equals(other) && Tempo == other.Tempo; - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; - return Equals((TempoEvent) obj); - } - - public override int GetHashCode() - { - unchecked - { - return (base.GetHashCode()*397) ^ Tempo; - } - } - } - - - public class BendEvent : ChannelMidiEvent - { - public int Value { get; set; } - - public override string ToString() - { - return "Bend: " + base.ToString() + " Value[" + Value + "]"; - } - - protected bool Equals(BendEvent other) - { - return base.Equals(other) && Value == other.Value; - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; - return Equals((BendEvent) obj); - } - - public override int GetHashCode() - { - unchecked - { - return (base.GetHashCode()*397) ^ Value.GetHashCode(); - } - } - } - - public class TrackEndEvent : TrackMidiEvent - { - public override string ToString() - { - return "End of Track " + base.ToString(); - } - } - - public FastList MidiEvents { get; private set; } - - public FlatMidiEventGenerator() - { - MidiEvents = new FastList(); - } - - public void AddTimeSignature(int tick, int timeSignatureNumerator, int timeSignatureDenominator) - { - MidiEvents.Add(new TimeSignatureEvent - { - Tick = tick, - Numerator = timeSignatureNumerator, - Denominator = timeSignatureDenominator - }); - } - - public void AddRest(int track, int tick, int channel) - { - MidiEvents.Add(new RestEvent - { - Tick = tick, - Track = track, - Channel = channel - }); - } - - public void AddNote(int track, int start, int length, byte key, DynamicValue dynamicValue, byte channel) - { - MidiEvents.Add(new NoteEvent - { - Channel = channel, - DynamicValue = dynamicValue, - Key = key, - Length = length, - Tick = start, - Track = track - }); - } - - public void AddControlChange(int track, int tick, byte channel, byte controller, byte value) - { - MidiEvents.Add(new ControlChangeEvent - { - Track = track, - Tick = tick, - Channel = channel, - Controller = controller, - Value = value - }); - } - - public void AddProgramChange(int track, int tick, byte channel, byte program) - { - MidiEvents.Add(new ProgramChangeEvent - { - Channel = channel, - Program = program, - Tick = tick, - Track = track - }); - } - - public void AddTempo(int tick, int tempo) - { - MidiEvents.Add(new TempoEvent - { - Tick = tick, - Tempo = tempo - }); - } - - public void AddBend(int track, int tick, byte channel, int value) - { - MidiEvents.Add(new BendEvent() - { - Tick = tick, - Track = track, - Channel = channel, - Value = value - }); - } - - public void FinishTrack(int track, int tick) - { - MidiEvents.Add(new TrackEndEvent - { - Track = track, - Tick = tick - }); - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab.Test/Audio/MidiFileGeneratorTest.cs b/Source/AlphaTab.Test/Audio/MidiFileGeneratorTest.cs deleted file mode 100644 index 1b08d88fc..000000000 --- a/Source/AlphaTab.Test/Audio/MidiFileGeneratorTest.cs +++ /dev/null @@ -1,358 +0,0 @@ -using System; -using System.IO; -using System.Text; -using AlphaTab.Audio; -using AlphaTab.Audio.Generator; -using AlphaTab.Audio.Synth.Midi; -using AlphaTab.Audio.Synth.Midi.Event; -using AlphaTab.Collections; -using AlphaTab.Importer; -using AlphaTab.IO; -using AlphaTab.Model; -using AlphaTab.Util; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace AlphaTab.Test.Audio -{ - [TestClass] - public class MidiFileGeneratorTest - { - private Score ParseTex(string tex) - { - var importer = new AlphaTexImporter(); - importer.Init(TestPlatform.CreateStringReader(tex)); - return importer.ReadScore(); - } - - [TestMethod] - public void TestCorrectMidiOrder() - { - var midiFile = new MidiFile(); - midiFile.AddEvent(new MidiEvent(0, 0, 0, 0)); - midiFile.AddEvent(new MidiEvent(0, 0, 1, 0)); - midiFile.AddEvent(new MidiEvent(100, 0, 2, 0)); - midiFile.AddEvent(new MidiEvent(50, 0, 3, 0)); - midiFile.AddEvent(new MidiEvent(50, 0, 4, 0)); - - Assert.AreEqual(0, midiFile.Events[0].Data1); - Assert.AreEqual(1, midiFile.Events[1].Data1); - Assert.AreEqual(3, midiFile.Events[2].Data1); - Assert.AreEqual(4, midiFile.Events[3].Data1); - Assert.AreEqual(2, midiFile.Events[4].Data1); - } - - [TestMethod] - public void TestBend() - { - var tex = ":4 15.6{b(0 4)} 15.6"; - var score = ParseTex(tex); - - Assert.AreEqual(1, score.Tracks.Count); - Assert.AreEqual(1, score.Tracks[0].Staves[0].Bars.Count); - Assert.AreEqual(1, score.Tracks[0].Staves[0].Bars[0].Voices.Count); - Assert.AreEqual(2, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats.Count); - Assert.AreEqual(1, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes.Count); - Assert.AreEqual(1, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes.Count); - - var handler = new FlatMidiEventGenerator(); - var generator = new MidiFileGenerator(score, null, handler); - generator.Generate(); - - var info = score.Tracks[0].PlaybackInfo; - var note = score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0]; - - var expectedEvents = new FlatMidiEventGenerator.MidiEvent[] - { - // channel init - new FlatMidiEventGenerator.ControlChangeEvent { Tick = 0, Track = 0, Channel=info.PrimaryChannel, Controller = (byte) ControllerType.VolumeCoarse, Value = 120}, - new FlatMidiEventGenerator.ControlChangeEvent { Tick = 0, Track = 0, Channel=info.PrimaryChannel, Controller = (byte) ControllerType.PanCoarse, Value = 64}, - new FlatMidiEventGenerator.ControlChangeEvent { Tick = 0, Track = 0, Channel=info.PrimaryChannel, Controller = (byte) ControllerType.ExpressionControllerCoarse, Value = 127}, - new FlatMidiEventGenerator.ControlChangeEvent { Tick = 0, Track = 0, Channel=info.PrimaryChannel, Controller = (byte) ControllerType.RegisteredParameterFine, Value = 0}, - new FlatMidiEventGenerator.ControlChangeEvent { Tick = 0, Track = 0, Channel=info.PrimaryChannel, Controller = (byte) ControllerType.RegisteredParameterCourse, Value = 0}, - new FlatMidiEventGenerator.ControlChangeEvent { Tick = 0, Track = 0, Channel=info.PrimaryChannel, Controller = (byte) ControllerType.DataEntryFine, Value = 0}, - new FlatMidiEventGenerator.ControlChangeEvent { Tick = 0, Track = 0, Channel=info.PrimaryChannel, Controller = (byte) ControllerType.DataEntryCoarse, Value = 12}, - new FlatMidiEventGenerator.ProgramChangeEvent { Tick = 0, Track = 0, Channel=info.PrimaryChannel, Program = (byte) info.Program }, - - new FlatMidiEventGenerator.ControlChangeEvent { Tick = 0, Track = 0, Channel=info.SecondaryChannel, Controller = (byte) ControllerType.VolumeCoarse, Value = 120}, - new FlatMidiEventGenerator.ControlChangeEvent { Tick = 0, Track = 0, Channel=info.SecondaryChannel, Controller = (byte) ControllerType.PanCoarse, Value = 64}, - new FlatMidiEventGenerator.ControlChangeEvent { Tick = 0, Track = 0, Channel=info.SecondaryChannel, Controller = (byte) ControllerType.ExpressionControllerCoarse, Value = 127}, - new FlatMidiEventGenerator.ControlChangeEvent { Tick = 0, Track = 0, Channel=info.SecondaryChannel, Controller = (byte) ControllerType.RegisteredParameterFine, Value = 0}, - new FlatMidiEventGenerator.ControlChangeEvent { Tick = 0, Track = 0, Channel=info.SecondaryChannel, Controller = (byte) ControllerType.RegisteredParameterCourse, Value = 0}, - new FlatMidiEventGenerator.ControlChangeEvent { Tick = 0, Track = 0, Channel=info.SecondaryChannel, Controller = (byte) ControllerType.DataEntryFine, Value = 0}, - new FlatMidiEventGenerator.ControlChangeEvent { Tick = 0, Track = 0, Channel=info.SecondaryChannel, Controller = (byte) ControllerType.DataEntryCoarse, Value = 12}, - new FlatMidiEventGenerator.ProgramChangeEvent { Tick = 0, Track = 0, Channel=info.SecondaryChannel, Program = (byte) info.Program }, - - new FlatMidiEventGenerator.TimeSignatureEvent { Tick = 0, Numerator = 4, Denominator = 4 }, - new FlatMidiEventGenerator.TempoEvent { Tick = 0, Tempo = 120 }, - - // bend effect - new FlatMidiEventGenerator.BendEvent { Tick = 0, Track = 0, Channel = info.SecondaryChannel, Value = 64 }, // no bend - new FlatMidiEventGenerator.BendEvent { Tick = 0, Track = 0, Channel = info.SecondaryChannel, Value = 64 }, - new FlatMidiEventGenerator.BendEvent { Tick = 87, Track = 0, Channel = info.SecondaryChannel, Value = 65 }, - new FlatMidiEventGenerator.BendEvent { Tick = 174, Track = 0, Channel = info.SecondaryChannel, Value = 66 }, - new FlatMidiEventGenerator.BendEvent { Tick = 261, Track = 0, Channel = info.SecondaryChannel, Value = 67 }, - new FlatMidiEventGenerator.BendEvent { Tick = 349, Track = 0, Channel = info.SecondaryChannel, Value = 68 }, - new FlatMidiEventGenerator.BendEvent { Tick = 436, Track = 0, Channel = info.SecondaryChannel, Value = 69 }, - new FlatMidiEventGenerator.BendEvent { Tick = 523, Track = 0, Channel = info.SecondaryChannel, Value = 70 }, - new FlatMidiEventGenerator.BendEvent { Tick = 610, Track = 0, Channel = info.SecondaryChannel, Value = 71 }, - new FlatMidiEventGenerator.BendEvent { Tick = 698, Track = 0, Channel = info.SecondaryChannel, Value = 72 }, - new FlatMidiEventGenerator.BendEvent { Tick = 785, Track = 0, Channel = info.SecondaryChannel, Value = 73 }, - new FlatMidiEventGenerator.BendEvent { Tick = 872, Track = 0, Channel = info.SecondaryChannel, Value = 74 }, - new FlatMidiEventGenerator.BendEvent { Tick = 959, Track = 0, Channel = info.SecondaryChannel, Value = 75 }, - - // note itself - new FlatMidiEventGenerator.NoteEvent { Tick = 0, Track = 0, Channel = info.SecondaryChannel, DynamicValue = note.Dynamic, Key = (byte) note.RealValue, Length = note.Beat.Duration.ToTicks() }, - - // reset bend - new FlatMidiEventGenerator.BendEvent { Tick = 960, Track = 0, Channel = info.PrimaryChannel, Value = 64 }, - new FlatMidiEventGenerator.NoteEvent { Tick = 960, Track = 0, Channel = info.PrimaryChannel, DynamicValue = note.Dynamic, Key = (byte) note.RealValue, Length = note.Beat.Duration.ToTicks() }, - - // end of track - new FlatMidiEventGenerator.TrackEndEvent { Tick = 3840, Track = 0 } // 3840 = end of bar - }; - - for (int i = 0; i < handler.MidiEvents.Count; i++) - { - Logger.Info("Test", $"i[{i}] {handler.MidiEvents[i]}"); - if (i < expectedEvents.Length) - { - Assert.AreEqual(expectedEvents[i], handler.MidiEvents[i]); - } - } - - Assert.AreEqual(expectedEvents.Length, handler.MidiEvents.Count); - } - - [TestMethod] - public void TestGraceBeatGeneration() - { - var reader = new Gp7Importer(); - var buffer = TestPlatform.LoadFile("TestFiles/Audio/GraceBeats.gp"); - - var settings = Settings.SongBook; - reader.Init(ByteBuffer.FromBuffer(buffer), settings); - var score = reader.ReadScore(); - - var handler = new FlatMidiEventGenerator(); - var generator = new MidiFileGenerator(score, settings, handler); - generator.Generate(); - - // on beat - var tick = 0; - var ticks = new FastList(); - Assert.AreEqual(tick, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].AbsolutePlaybackStart); - Assert.AreEqual(3840, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].PlaybackDuration); - ticks.Add(tick); - tick += score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].PlaybackDuration; - - Assert.AreEqual(tick, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].AbsolutePlaybackStart); - Assert.AreEqual(120, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].PlaybackDuration); - ticks.Add(tick); - tick += score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].PlaybackDuration; - - Assert.AreEqual(tick, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[1].AbsolutePlaybackStart); - Assert.AreEqual(3720, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[1].PlaybackDuration); - ticks.Add(tick); - tick += score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[1].PlaybackDuration; - - // before beat - Assert.AreEqual(tick, score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[0].AbsolutePlaybackStart); - Assert.AreEqual(3720, score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[0].PlaybackDuration); - ticks.Add(tick); - tick += score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[0].PlaybackDuration; - - Assert.AreEqual(tick, score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[0].AbsolutePlaybackStart); - Assert.AreEqual(120, score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[0].PlaybackDuration); - ticks.Add(tick); - tick += score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[0].PlaybackDuration; - - Assert.AreEqual(tick, score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[1].AbsolutePlaybackStart); - Assert.AreEqual(3840, score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[1].PlaybackDuration); - ticks.Add(tick); - tick += score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[1].PlaybackDuration; - - // bend - Assert.AreEqual(GraceType.BendGrace, score.Tracks[0].Staves[0].Bars[4].Voices[0].Beats[0].GraceType); - Assert.AreEqual(tick, score.Tracks[0].Staves[0].Bars[4].Voices[0].Beats[0].AbsolutePlaybackStart); - Assert.AreEqual(1920, score.Tracks[0].Staves[0].Bars[4].Voices[0].Beats[0].PlaybackDuration); - ticks.Add(tick); - tick += score.Tracks[0].Staves[0].Bars[4].Voices[0].Beats[0].PlaybackDuration; - - Assert.AreEqual(tick, score.Tracks[0].Staves[0].Bars[4].Voices[0].Beats[1].AbsolutePlaybackStart); - Assert.AreEqual(1920, score.Tracks[0].Staves[0].Bars[4].Voices[0].Beats[1].PlaybackDuration); - ticks.Add(tick); - tick += score.Tracks[0].Staves[0].Bars[4].Voices[0].Beats[1].PlaybackDuration; - - var info = score.Tracks[0].PlaybackInfo; - var expectedEvents = new FlatMidiEventGenerator.MidiEvent[] - { - // channel init - new FlatMidiEventGenerator.ControlChangeEvent { Tick = 0, Track = 0, Channel=info.PrimaryChannel, Controller = (byte) ControllerType.VolumeCoarse, Value = 120}, - new FlatMidiEventGenerator.ControlChangeEvent { Tick = 0, Track = 0, Channel=info.PrimaryChannel, Controller = (byte) ControllerType.PanCoarse, Value = 64}, - new FlatMidiEventGenerator.ControlChangeEvent { Tick = 0, Track = 0, Channel=info.PrimaryChannel, Controller = (byte) ControllerType.ExpressionControllerCoarse, Value = 127}, - new FlatMidiEventGenerator.ControlChangeEvent { Tick = 0, Track = 0, Channel=info.PrimaryChannel, Controller = (byte) ControllerType.RegisteredParameterFine, Value = 0}, - new FlatMidiEventGenerator.ControlChangeEvent { Tick = 0, Track = 0, Channel=info.PrimaryChannel, Controller = (byte) ControllerType.RegisteredParameterCourse, Value = 0}, - new FlatMidiEventGenerator.ControlChangeEvent { Tick = 0, Track = 0, Channel=info.PrimaryChannel, Controller = (byte) ControllerType.DataEntryFine, Value = 0}, - new FlatMidiEventGenerator.ControlChangeEvent { Tick = 0, Track = 0, Channel=info.PrimaryChannel, Controller = (byte) ControllerType.DataEntryCoarse, Value = 12}, - new FlatMidiEventGenerator.ProgramChangeEvent { Tick = 0, Track = 0, Channel=info.PrimaryChannel, Program = (byte) info.Program }, - - new FlatMidiEventGenerator.ControlChangeEvent { Tick = 0, Track = 0, Channel=info.SecondaryChannel, Controller = (byte) ControllerType.VolumeCoarse, Value = 120}, - new FlatMidiEventGenerator.ControlChangeEvent { Tick = 0, Track = 0, Channel=info.SecondaryChannel, Controller = (byte) ControllerType.PanCoarse, Value = 64}, - new FlatMidiEventGenerator.ControlChangeEvent { Tick = 0, Track = 0, Channel=info.SecondaryChannel, Controller = (byte) ControllerType.ExpressionControllerCoarse, Value = 127}, - new FlatMidiEventGenerator.ControlChangeEvent { Tick = 0, Track = 0, Channel=info.SecondaryChannel, Controller = (byte) ControllerType.RegisteredParameterFine, Value = 0}, - new FlatMidiEventGenerator.ControlChangeEvent { Tick = 0, Track = 0, Channel=info.SecondaryChannel, Controller = (byte) ControllerType.RegisteredParameterCourse, Value = 0}, - new FlatMidiEventGenerator.ControlChangeEvent { Tick = 0, Track = 0, Channel=info.SecondaryChannel, Controller = (byte) ControllerType.DataEntryFine, Value = 0}, - new FlatMidiEventGenerator.ControlChangeEvent { Tick = 0, Track = 0, Channel=info.SecondaryChannel, Controller = (byte) ControllerType.DataEntryCoarse, Value = 12}, - new FlatMidiEventGenerator.ProgramChangeEvent { Tick = 0, Track = 0, Channel=info.SecondaryChannel, Program = (byte) info.Program }, - - new FlatMidiEventGenerator.TimeSignatureEvent { Tick = 0, Numerator = 4, Denominator = 4 }, - new FlatMidiEventGenerator.TempoEvent { Tick = 0, Tempo = 120 }, - - // on beat - new FlatMidiEventGenerator.BendEvent { Tick = ticks[0], Track = 0, Channel = info.PrimaryChannel, Value = 64 }, - new FlatMidiEventGenerator.NoteEvent { Tick = ticks[0], Track = 0, Channel = info.PrimaryChannel, DynamicValue = DynamicValue.F, Key = (byte) 67, Length = 3840 }, - - new FlatMidiEventGenerator.BendEvent { Tick = ticks[1], Track = 0, Channel = info.PrimaryChannel, Value = 64 }, - new FlatMidiEventGenerator.NoteEvent { Tick = ticks[1], Track = 0, Channel = info.PrimaryChannel, DynamicValue = DynamicValue.F, Key = (byte) 67, Length = 120 }, - - new FlatMidiEventGenerator.BendEvent { Tick = ticks[2], Track = 0, Channel = info.PrimaryChannel, Value = 64 }, - new FlatMidiEventGenerator.NoteEvent { Tick = ticks[2], Track = 0, Channel = info.PrimaryChannel, DynamicValue = DynamicValue.F, Key = (byte) 67, Length = 3720}, - - - // before beat - new FlatMidiEventGenerator.BendEvent { Tick = ticks[3], Track = 0, Channel = info.PrimaryChannel, Value = 64 }, - new FlatMidiEventGenerator.NoteEvent { Tick = ticks[3], Track = 0, Channel = info.PrimaryChannel, DynamicValue = DynamicValue.F, Key = (byte) 67, Length = 3720 }, - - new FlatMidiEventGenerator.BendEvent { Tick = ticks[4], Track = 0, Channel = info.PrimaryChannel, Value = 64 }, - new FlatMidiEventGenerator.NoteEvent { Tick = ticks[4], Track = 0, Channel = info.PrimaryChannel, DynamicValue = DynamicValue.F, Key = (byte) 67, Length = 120 }, - - new FlatMidiEventGenerator.BendEvent { Tick = ticks[5], Track = 0, Channel = info.PrimaryChannel, Value = 64 }, - new FlatMidiEventGenerator.NoteEvent { Tick = ticks[5], Track = 0, Channel = info.PrimaryChannel, DynamicValue = DynamicValue.F, Key = (byte) 67, Length = 3840}, - - // bend beat - new FlatMidiEventGenerator.BendEvent { Tick = ticks[6], Track = 0, Channel = info.SecondaryChannel, Value = 64}, - new FlatMidiEventGenerator.BendEvent { Tick = ticks[6] + 13 * 0, Track = 0, Channel = info.SecondaryChannel, Value = 64}, - new FlatMidiEventGenerator.BendEvent { Tick = ticks[6] + 13 * 1, Track = 0, Channel = info.SecondaryChannel, Value = 65}, - new FlatMidiEventGenerator.BendEvent { Tick = ticks[6] + 13 * 2, Track = 0, Channel = info.SecondaryChannel, Value = 66}, - new FlatMidiEventGenerator.BendEvent { Tick = ticks[6] + 13 * 3, Track = 0, Channel = info.SecondaryChannel, Value = 67}, - new FlatMidiEventGenerator.BendEvent { Tick = ticks[6] + 13 * 4, Track = 0, Channel = info.SecondaryChannel, Value = 68}, - new FlatMidiEventGenerator.BendEvent { Tick = ticks[6] + 13 * 5, Track = 0, Channel = info.SecondaryChannel, Value = 69}, - new FlatMidiEventGenerator.BendEvent { Tick = ticks[6] + 13 * 6, Track = 0, Channel = info.SecondaryChannel, Value = 70}, - new FlatMidiEventGenerator.BendEvent { Tick = ticks[6] + 13 * 7, Track = 0, Channel = info.SecondaryChannel, Value = 71}, - new FlatMidiEventGenerator.BendEvent { Tick = ticks[6] + 13 * 8, Track = 0, Channel = info.SecondaryChannel, Value = 72}, - new FlatMidiEventGenerator.BendEvent { Tick = ticks[6] + 13 * 9, Track = 0, Channel = info.SecondaryChannel, Value = 73}, - new FlatMidiEventGenerator.BendEvent { Tick = ticks[6] + 13 * 10, Track = 0, Channel = info.SecondaryChannel, Value = 74}, - new FlatMidiEventGenerator.BendEvent { Tick = ticks[6] + 13 * 11 + 1, Track = 0, Channel = info.SecondaryChannel, Value = 75}, - new FlatMidiEventGenerator.NoteEvent { Tick = ticks[6], Track = 0, Channel = info.SecondaryChannel, Length = 3840, Key = 67, DynamicValue = DynamicValue.F}, - - // end of track - new FlatMidiEventGenerator.TrackEndEvent { Tick = 19200, Track = 0 } // 3840 = end of bar - }; - - for (int i = 0; i < handler.MidiEvents.Count; i++) - { - Logger.Info("Test", $"i[{i}] {handler.MidiEvents[i]}"); - if (i < expectedEvents.Length) - { - Assert.AreEqual(expectedEvents[i], handler.MidiEvents[i]); - } - } - - Assert.AreEqual(expectedEvents.Length, handler.MidiEvents.Count); - } - - [TestMethod] - public void TestBendMultiPoint() - { - var tex = ":4 15.6{b(0 4 0)} 15.6"; - var score = ParseTex(tex); - - Assert.AreEqual(1, score.Tracks.Count); - Assert.AreEqual(1, score.Tracks[0].Staves[0].Bars.Count); - Assert.AreEqual(1, score.Tracks[0].Staves[0].Bars[0].Voices.Count); - Assert.AreEqual(2, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats.Count); - Assert.AreEqual(1, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes.Count); - Assert.AreEqual(1, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes.Count); - - var handler = new FlatMidiEventGenerator(); - var generator = new MidiFileGenerator(score, null, handler); - generator.Generate(); - - var info = score.Tracks[0].PlaybackInfo; - var note = score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0]; - - var expectedEvents = new FlatMidiEventGenerator.MidiEvent[] - { - // channel init - new FlatMidiEventGenerator.ControlChangeEvent { Tick = 0, Track = 0, Channel=info.PrimaryChannel, Controller = (byte) ControllerType.VolumeCoarse, Value = 120}, - new FlatMidiEventGenerator.ControlChangeEvent { Tick = 0, Track = 0, Channel=info.PrimaryChannel, Controller = (byte) ControllerType.PanCoarse, Value = 64}, - new FlatMidiEventGenerator.ControlChangeEvent { Tick = 0, Track = 0, Channel=info.PrimaryChannel, Controller = (byte) ControllerType.ExpressionControllerCoarse, Value = 127}, - new FlatMidiEventGenerator.ControlChangeEvent { Tick = 0, Track = 0, Channel=info.PrimaryChannel, Controller = (byte) ControllerType.RegisteredParameterFine, Value = 0}, - new FlatMidiEventGenerator.ControlChangeEvent { Tick = 0, Track = 0, Channel=info.PrimaryChannel, Controller = (byte) ControllerType.RegisteredParameterCourse, Value = 0}, - new FlatMidiEventGenerator.ControlChangeEvent { Tick = 0, Track = 0, Channel=info.PrimaryChannel, Controller = (byte) ControllerType.DataEntryFine, Value = 0}, - new FlatMidiEventGenerator.ControlChangeEvent { Tick = 0, Track = 0, Channel=info.PrimaryChannel, Controller = (byte) ControllerType.DataEntryCoarse, Value = 12}, - new FlatMidiEventGenerator.ProgramChangeEvent { Tick = 0, Track = 0, Channel=info.PrimaryChannel, Program = (byte) info.Program }, - - new FlatMidiEventGenerator.ControlChangeEvent { Tick = 0, Track = 0, Channel=info.SecondaryChannel, Controller = (byte) ControllerType.VolumeCoarse, Value = 120}, - new FlatMidiEventGenerator.ControlChangeEvent { Tick = 0, Track = 0, Channel=info.SecondaryChannel, Controller = (byte) ControllerType.PanCoarse, Value = 64}, - new FlatMidiEventGenerator.ControlChangeEvent { Tick = 0, Track = 0, Channel=info.SecondaryChannel, Controller = (byte) ControllerType.ExpressionControllerCoarse, Value = 127}, - new FlatMidiEventGenerator.ControlChangeEvent { Tick = 0, Track = 0, Channel=info.SecondaryChannel, Controller = (byte) ControllerType.RegisteredParameterFine, Value = 0}, - new FlatMidiEventGenerator.ControlChangeEvent { Tick = 0, Track = 0, Channel=info.SecondaryChannel, Controller = (byte) ControllerType.RegisteredParameterCourse, Value = 0}, - new FlatMidiEventGenerator.ControlChangeEvent { Tick = 0, Track = 0, Channel=info.SecondaryChannel, Controller = (byte) ControllerType.DataEntryFine, Value = 0}, - new FlatMidiEventGenerator.ControlChangeEvent { Tick = 0, Track = 0, Channel=info.SecondaryChannel, Controller = (byte) ControllerType.DataEntryCoarse, Value = 12}, - new FlatMidiEventGenerator.ProgramChangeEvent { Tick = 0, Track = 0, Channel=info.SecondaryChannel, Program = (byte) info.Program }, - - new FlatMidiEventGenerator.TimeSignatureEvent { Tick = 0, Numerator = 4, Denominator = 4 }, - new FlatMidiEventGenerator.TempoEvent { Tick = 0, Tempo = 120 }, - - // bend effect - new FlatMidiEventGenerator.BendEvent { Tick = 0, Track = 0, Channel = info.SecondaryChannel, Value = 64 }, // no bend - new FlatMidiEventGenerator.BendEvent { Tick = 0, Track = 0, Channel = info.SecondaryChannel, Value = 64 }, - new FlatMidiEventGenerator.BendEvent { Tick = 43, Track = 0, Channel = info.SecondaryChannel, Value = 65 }, - new FlatMidiEventGenerator.BendEvent { Tick = 87, Track = 0, Channel = info.SecondaryChannel, Value = 66 }, - new FlatMidiEventGenerator.BendEvent { Tick = 130, Track = 0, Channel = info.SecondaryChannel, Value = 67 }, - new FlatMidiEventGenerator.BendEvent { Tick = 174, Track = 0, Channel = info.SecondaryChannel, Value = 68 }, - new FlatMidiEventGenerator.BendEvent { Tick = 218, Track = 0, Channel = info.SecondaryChannel, Value = 69 }, - new FlatMidiEventGenerator.BendEvent { Tick = 261, Track = 0, Channel = info.SecondaryChannel, Value = 70 }, - new FlatMidiEventGenerator.BendEvent { Tick = 305, Track = 0, Channel = info.SecondaryChannel, Value = 71 }, - new FlatMidiEventGenerator.BendEvent { Tick = 349, Track = 0, Channel = info.SecondaryChannel, Value = 72 }, - new FlatMidiEventGenerator.BendEvent { Tick = 392, Track = 0, Channel = info.SecondaryChannel, Value = 73 }, - new FlatMidiEventGenerator.BendEvent { Tick = 436, Track = 0, Channel = info.SecondaryChannel, Value = 74 }, - new FlatMidiEventGenerator.BendEvent { Tick = 479, Track = 0, Channel = info.SecondaryChannel, Value = 75 }, // full bend - - new FlatMidiEventGenerator.BendEvent { Tick = 480, Track = 0, Channel = info.SecondaryChannel, Value = 75 }, // full bend - new FlatMidiEventGenerator.BendEvent { Tick = 523, Track = 0, Channel = info.SecondaryChannel, Value = 74 }, - new FlatMidiEventGenerator.BendEvent { Tick = 567, Track = 0, Channel = info.SecondaryChannel, Value = 73 }, - new FlatMidiEventGenerator.BendEvent { Tick = 610, Track = 0, Channel = info.SecondaryChannel, Value = 72 }, - new FlatMidiEventGenerator.BendEvent { Tick = 654, Track = 0, Channel = info.SecondaryChannel, Value = 71 }, - new FlatMidiEventGenerator.BendEvent { Tick = 698, Track = 0, Channel = info.SecondaryChannel, Value = 70 }, - new FlatMidiEventGenerator.BendEvent { Tick = 741, Track = 0, Channel = info.SecondaryChannel, Value = 69 }, - new FlatMidiEventGenerator.BendEvent { Tick = 785, Track = 0, Channel = info.SecondaryChannel, Value = 68 }, - new FlatMidiEventGenerator.BendEvent { Tick = 829, Track = 0, Channel = info.SecondaryChannel, Value = 67 }, - new FlatMidiEventGenerator.BendEvent { Tick = 872, Track = 0, Channel = info.SecondaryChannel, Value = 66 }, - new FlatMidiEventGenerator.BendEvent { Tick = 916, Track = 0, Channel = info.SecondaryChannel, Value = 65 }, - new FlatMidiEventGenerator.BendEvent { Tick = 959, Track = 0, Channel = info.SecondaryChannel, Value = 64 }, // no bend - - // note itself - new FlatMidiEventGenerator.NoteEvent { Tick = 0, Track = 0, Channel = info.SecondaryChannel, DynamicValue = note.Dynamic, Key = (byte) note.RealValue, Length = note.Beat.Duration.ToTicks() }, - - // reset bend - new FlatMidiEventGenerator.BendEvent { Tick = 960, Track = 0, Channel = info.PrimaryChannel, Value = 64 }, // finish - new FlatMidiEventGenerator.NoteEvent { Tick = 960, Track = 0, Channel = info.PrimaryChannel, DynamicValue = note.Dynamic, Key = (byte) note.RealValue, Length = note.Beat.Duration.ToTicks() }, - // end of track - new FlatMidiEventGenerator.TrackEndEvent { Tick = 3840, Track = 0 } // 3840 = end of bar - }; - - for (int i = 0; i < handler.MidiEvents.Count; i++) - { - Logger.Info("Test", $"i[{i}] {handler.MidiEvents[i]}"); - if (i < expectedEvents.Length) - { - Assert.AreEqual(expectedEvents[i], handler.MidiEvents[i]); - } - } - - Assert.AreEqual(expectedEvents.Length, handler.MidiEvents.Count); - } - } -} diff --git a/Source/AlphaTab.Test/Audio/MidiPlaybackControllerTest.cs b/Source/AlphaTab.Test/Audio/MidiPlaybackControllerTest.cs deleted file mode 100644 index 1fcc11f0d..000000000 --- a/Source/AlphaTab.Test/Audio/MidiPlaybackControllerTest.cs +++ /dev/null @@ -1,85 +0,0 @@ -using System.Diagnostics; -using AlphaTab.Audio.Generator; -using AlphaTab.Model; -using AlphaTab.Test.Importer; -using AlphaTab.Util; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace AlphaTab.Test.Audio -{ - [TestClass] - public class MidiPlaybackControllerTest : GpImporterTestBase - { - [TestMethod] - public void TestRepeatClose() - { - var reader = PrepareImporterWithFile("GuitarPro5\\RepeatClose.gp5"); - var score = reader.ReadScore(); - var expectedIndexes = new[] - { - 0, 1, 0, 1, 2 - }; - - TestRepeat(score, expectedIndexes); - } - - [TestMethod] - public void TestRepeatCloseMulti() - { - var reader = PrepareImporterWithFile("GuitarPro5\\RepeatCloseMulti.gp5"); - var score = reader.ReadScore(); - var expectedIndexes = new[] - { - 0,1,0,1,0,1,0,1,2 - }; - TestRepeat(score, expectedIndexes); - } - - [TestMethod] - public void TestRepeatCloseWithoutStartAtBeginning() - { - var reader = PrepareImporterWithFile("GuitarPro5\\RepeatCloseWithoutStartAtBeginning.gp5"); - var score = reader.ReadScore(); - var expectedIndexes = new[] - { - 0,1,0,1 - }; - - TestRepeat(score, expectedIndexes); - } - - [TestMethod] - public void TestRepeatCloseAlternateEndings() - { - var reader = PrepareImporterWithFile("GuitarPro5\\RepeatCloseAlternateEndings.gp5"); - var score = reader.ReadScore(); - var expectedIndexes = new[] - { - 0,1,0,2,3,0,1,0,4 - }; - - TestRepeat(score, expectedIndexes); - } - - private void TestRepeat(Score score, int[] expectedIndexes) - { - var controller = new MidiPlaybackController(score); - int i = 0; - while (!controller.Finished) - { - var index = controller.Index; - controller.ProcessCurrent(); - if (controller.ShouldPlay) - { - Logger.Debug("Test", $"Checking index {i}, expected[{expectedIndexes[i]}]"); - Assert.AreEqual(expectedIndexes[i], index); - i++; - } - controller.MoveNext(); - } - - Assert.AreEqual(expectedIndexes.Length, i); - Assert.IsTrue(controller.Finished); - } - } -} diff --git a/Source/AlphaTab.Test/Importer/AlphaTexImporterTest.cs b/Source/AlphaTab.Test/Importer/AlphaTexImporterTest.cs deleted file mode 100644 index 2fd8de014..000000000 --- a/Source/AlphaTab.Test/Importer/AlphaTexImporterTest.cs +++ /dev/null @@ -1,418 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using System.Xml.Linq; -using AlphaTab.Collections; -using AlphaTab.Importer; -using AlphaTab.IO; -using AlphaTab.Model; -using AlphaTab.Rendering; -using AlphaTab.Rendering.Effects; -using AlphaTab.Rendering.Glyphs; -using AlphaTab.Xml; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace AlphaTab.Test.Importer -{ - [TestClass] - public class AlphaTexImporterTest - { - private Score ParseTex(string tex) - { - var importer = new AlphaTexImporter(); - importer.Init(TestPlatform.CreateStringReader(tex)); - return importer.ReadScore(); - } - - [TestMethod] - public void EnsureMetadataParsing_Issue73() - { - var tex = @"\title Test - \words test - \music alphaTab - \copyright test - \tempo 200 - \instrument 30 - \capo 2 - \tuning G3 D2 G2 B2 D3 A4 - . - 0.5.2 1.5.4 3.4.4 | 5.3.8 5.3.8 5.3.8 5.3.8 r.2"; - - var score = ParseTex(tex); - - Assert.AreEqual("Test", score.Title); - Assert.AreEqual("test", score.Words); - Assert.AreEqual("alphaTab", score.Music); - Assert.AreEqual("test", score.Copyright); - Assert.AreEqual(200, score.Tempo); - - Assert.AreEqual(1, score.Tracks.Count); - Assert.AreEqual(30, score.Tracks[0].PlaybackInfo.Program); - Assert.AreEqual(2, score.Tracks[0].Staves[0].Capo); - Assert.AreEqual("55,38,43,47,50,69", string.Join(",", score.Tracks[0].Staves[0].Tuning)); - - Assert.AreEqual(2, score.MasterBars.Count); - - Assert.AreEqual(3, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats.Count); - { - Assert.AreEqual(1, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes.Count); - Assert.AreEqual(Duration.Half, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Duration); - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].Fret); - Assert.AreEqual(2, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].String); - - Assert.AreEqual(1, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes.Count); - Assert.AreEqual(Duration.Quarter, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Duration); - Assert.AreEqual(1, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].Fret); - Assert.AreEqual(2, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].String); - - Assert.AreEqual(1, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[2].Notes.Count); - Assert.AreEqual(Duration.Quarter, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[2].Duration); - Assert.AreEqual(3, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[2].Notes[0].Fret); - Assert.AreEqual(3, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[2].Notes[0].String); - } - - Assert.AreEqual(5, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats.Count); - { - Assert.AreEqual(1, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Notes.Count); - Assert.AreEqual(Duration.Eighth, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Duration); - Assert.AreEqual(5, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Notes[0].Fret); - Assert.AreEqual(4, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Notes[0].String); - - Assert.AreEqual(1, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[1].Notes.Count); - Assert.AreEqual(Duration.Eighth, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[1].Duration); - Assert.AreEqual(5, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[1].Notes[0].Fret); - Assert.AreEqual(4, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[1].Notes[0].String); - - Assert.AreEqual(1, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[2].Notes.Count); - Assert.AreEqual(Duration.Eighth, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[2].Duration); - Assert.AreEqual(5, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[2].Notes[0].Fret); - Assert.AreEqual(4, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[2].Notes[0].String); - - Assert.AreEqual(1, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[3].Notes.Count); - Assert.AreEqual(Duration.Eighth, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[3].Duration); - Assert.AreEqual(5, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[3].Notes[0].Fret); - Assert.AreEqual(4, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[3].Notes[0].String); - - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[4].Notes.Count); - Assert.AreEqual(Duration.Half, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[4].Duration); - Assert.AreEqual(true, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[4].IsRest); - } - - } - - - [TestMethod] - public void TestTuning() - { - var tex = @"\tuning E4 B3 G3 D3 A2 E2 - . - 0.5.1"; - - var score = ParseTex(tex); - - Assert.AreEqual(string.Join(",", Tuning.GetDefaultTuningFor(6).Tunings), string.Join(",", score.Tracks[0].Staves[0].Tuning)); - } - - [TestMethod] - public void DeadNotes1_Issue79() - { - var tex = @":4 x.3"; - - var score = ParseTex(tex); - - Assert.AreEqual(1, score.Tracks.Count); - Assert.AreEqual(1, score.MasterBars.Count); - Assert.AreEqual(1, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats.Count); - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].Fret); - Assert.AreEqual(true, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].IsDead); - } - - [TestMethod] - public void DeadNotes2_Issue79() - { - var tex = @":4 3.3{x}"; - - var score = ParseTex(tex); - - Assert.AreEqual(1, score.Tracks.Count); - Assert.AreEqual(1, score.MasterBars.Count); - Assert.AreEqual(1, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats.Count); - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].Fret); - Assert.AreEqual(true, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].IsDead); - } - - [TestMethod] - public void Trill_Issue79() - { - var tex = @":4 3.3{tr 5 16}"; - - var score = ParseTex(tex); - - Assert.AreEqual(1, score.Tracks.Count); - Assert.AreEqual(1, score.MasterBars.Count); - Assert.AreEqual(1, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats.Count); - Assert.AreEqual(3, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].Fret); - Assert.AreEqual(true, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].IsTrill); - Assert.AreEqual(Duration.Sixteenth, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].TrillSpeed); - Assert.AreEqual(5, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].TrillFret); - } - - [TestMethod] - public void Tremolo_Issue79() - { - var tex = @":4 3.3{tr 5 16}"; - - var score = ParseTex(tex); - - Assert.AreEqual(1, score.Tracks.Count); - Assert.AreEqual(1, score.MasterBars.Count); - Assert.AreEqual(1, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats.Count); - Assert.AreEqual(3, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].Fret); - Assert.AreEqual(true, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].IsTrill); - Assert.AreEqual(Duration.Sixteenth, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].TrillSpeed); - Assert.AreEqual(5, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].TrillFret); - } - - [TestMethod] - public void TremoloPicking_Issue79() - { - var tex = @":4 3.3{tp 16}"; - - var score = ParseTex(tex); - - Assert.AreEqual(1, score.Tracks.Count); - Assert.AreEqual(1, score.MasterBars.Count); - Assert.AreEqual(1, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats.Count); - Assert.AreEqual(3, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].Fret); - Assert.AreEqual(true, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].IsTremolo); - Assert.AreEqual(Duration.Sixteenth, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].TremoloSpeed.Value); - } - - [TestMethod] - public void Hamonics_Issue79() - { - var tex = @":8 3.3{nh} 3.3{ah} 3.3{th} 3.3{ph} 3.3{sh}"; - var score = ParseTex(tex); - - Assert.AreEqual(1, score.Tracks.Count); - Assert.AreEqual(1, score.MasterBars.Count); - Assert.AreEqual(5, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats.Count); - Assert.AreEqual(HarmonicType.Natural, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].HarmonicType); - Assert.AreEqual(HarmonicType.Artificial, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].HarmonicType); - Assert.AreEqual(HarmonicType.Tap, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[2].Notes[0].HarmonicType); - Assert.AreEqual(HarmonicType.Pinch, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[3].Notes[0].HarmonicType); - Assert.AreEqual(HarmonicType.Semi, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[4].Notes[0].HarmonicType); - } - - [TestMethod] - public void HamonicsRenderingText_Issue79() - { - var tex = @":8 3.3{nh} 3.3{ah} 3.3{th} 3.3{ph} 3.3{sh}"; - var score = ParseTex(tex); - - Environment.StaveProfiles["harmonics"] = new BarRendererFactory[] - { - new EffectBarRendererFactory("n-harmonics", new IEffectBarRendererInfo[] {new HarmonicsEffectInfo(HarmonicType.Natural)}), - new EffectBarRendererFactory("a-harmonics", new IEffectBarRendererInfo[] {new HarmonicsEffectInfo(HarmonicType.Artificial)}), - new EffectBarRendererFactory("t-harmonics", new IEffectBarRendererInfo[] {new HarmonicsEffectInfo(HarmonicType.Tap)}), - new EffectBarRendererFactory("p-harmonics", new IEffectBarRendererInfo[] {new HarmonicsEffectInfo(HarmonicType.Pinch)}), - new EffectBarRendererFactory("s-harmonics", new IEffectBarRendererInfo[] {new HarmonicsEffectInfo(HarmonicType.Semi)}), - new EffectBarRendererFactory("f-harmonics", new IEffectBarRendererInfo[] {new HarmonicsEffectInfo(HarmonicType.Feedback)}), - }; - - - var settings = Settings.Defaults; - settings.Engine = "svg"; - settings.Staves = new StaveSettings("harmonics"); - - var renderer = new ScoreRenderer(settings); - var svg = ""; - renderer.PartialRenderFinished += r => - { - svg += r.RenderResult.ToString(); - }; - renderer.Render(score, new[] { 0 }); - - var regexTemplate = @"]+>\s*{0}\s*"; - - Assert.IsTrue(TestPlatform.IsMatch(svg, string.Format(regexTemplate, HarmonicsEffectInfo.HarmonicToString(HarmonicType.Natural)))); - Assert.IsTrue(TestPlatform.IsMatch(svg, string.Format(regexTemplate, HarmonicsEffectInfo.HarmonicToString(HarmonicType.Artificial)))); - Assert.IsTrue(TestPlatform.IsMatch(svg, string.Format(regexTemplate, HarmonicsEffectInfo.HarmonicToString(HarmonicType.Tap)))); - Assert.IsTrue(TestPlatform.IsMatch(svg, string.Format(regexTemplate, HarmonicsEffectInfo.HarmonicToString(HarmonicType.Pinch)))); - Assert.IsTrue(TestPlatform.IsMatch(svg, string.Format(regexTemplate, HarmonicsEffectInfo.HarmonicToString(HarmonicType.Semi)))); - } - - [TestMethod] - public void Grace_Issue79() - { - var tex = @":8 3.3{gr} 3.3{gr ob}"; - var score = ParseTex(tex); - - Assert.AreEqual(1, score.Tracks.Count); - Assert.AreEqual(1, score.MasterBars.Count); - Assert.AreEqual(2, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats.Count); - Assert.AreEqual(GraceType.BeforeBeat, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].GraceType); - Assert.AreEqual(GraceType.OnBeat, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].GraceType); - } - - - [TestMethod] - public void BendRendering_Issue79() - { - var tex = @":4 15.6{b(0 4)} 18.6{b(0 6)} 17.6{b(0 8)} 16.6{b(0 3 0)} | 15.6{b(0 8 4)} 14.6{b(4 4)} 13.6{b(4 6)} 14.6{b(4 0)}"; - var score = ParseTex(tex); - - Environment.StaveProfiles["tabOnly"] = new BarRendererFactory[] - { - new TabBarRendererFactory(false, false, false), - }; - - var settings = Settings.Defaults; - settings.Engine = "svg"; - settings.Layout.Mode = "horizontal"; - settings.Staves = new StaveSettings("tabOnly"); - - var renderer = new ScoreRenderer(settings); - var partials = new FastList(); - renderer.Error += (o, e) => - { - Assert.Fail(e.Message); - }; - renderer.PartialRenderFinished += r => - { - partials.Add(r.RenderResult.ToString()); - }; - renderer.Render(score, new[] { 0 }); - - var tab = new XmlDocument(partials[0]); - - var texts = tab.GetElementsByTagName("text", true); - - var expectedTexts = new[] - { - Platform.Platform.StringFromCharCode((int)MusicFontSymbol.ClefTab), // clef - - "1", // bar number - - "15", "full", - "18", "1½", - "17", "2", - "16", "¾", - - "2", // bar number - - "15", "2", "full", - "14", "full", - "13", "full", "1½", - "14", "full" - }; - - for (int i = 0; i < expectedTexts.Length; i++) - { - var text = texts[i].InnerText.Trim(); - Assert.AreEqual(expectedTexts[i], text, "Mismatch at index {0}", i); - } - } - - [TestMethod] - public void TestLeftHandFingerSingleNote() - { - var tex = @":8 3.3{lf 1} 3.3{lf 2} 3.3{lf 3} 3.3{lf 4} 3.3{lf 5}"; - var score = ParseTex(tex); - - Assert.AreEqual(1, score.Tracks.Count); - Assert.AreEqual(1, score.MasterBars.Count); - Assert.AreEqual(5, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats.Count); - Assert.AreEqual(Fingers.Thumb, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].LeftHandFinger); - Assert.AreEqual(Fingers.IndexFinger, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].LeftHandFinger); - Assert.AreEqual(Fingers.MiddleFinger, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[2].Notes[0].LeftHandFinger); - Assert.AreEqual(Fingers.AnnularFinger, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[3].Notes[0].LeftHandFinger); - Assert.AreEqual(Fingers.LittleFinger, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[4].Notes[0].LeftHandFinger); - } - - [TestMethod] - public void TestRightHandFingerSingleNote() - { - var tex = @":8 3.3{rf 1} 3.3{rf 2} 3.3{rf 3} 3.3{rf 4} 3.3{rf 5}"; - var score = ParseTex(tex); - - Assert.AreEqual(1, score.Tracks.Count); - Assert.AreEqual(1, score.MasterBars.Count); - Assert.AreEqual(5, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats.Count); - Assert.AreEqual(Fingers.Thumb, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].RightHandFinger); - Assert.AreEqual(Fingers.IndexFinger, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].RightHandFinger); - Assert.AreEqual(Fingers.MiddleFinger, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[2].Notes[0].RightHandFinger); - Assert.AreEqual(Fingers.AnnularFinger, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[3].Notes[0].RightHandFinger); - Assert.AreEqual(Fingers.LittleFinger, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[4].Notes[0].RightHandFinger); - } - - [TestMethod] - public void TestLeftHandFingerChord() - { - var tex = @":8 (3.1{lf 1} 3.2{lf 2} 3.3{lf 3} 3.4{lf 4} 3.5{lf 5})"; - var score = ParseTex(tex); - - Assert.AreEqual(1, score.Tracks.Count); - Assert.AreEqual(1, score.MasterBars.Count); - Assert.AreEqual(1, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats.Count); - Assert.AreEqual(5, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes.Count); - Assert.AreEqual(Fingers.Thumb, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].LeftHandFinger); - Assert.AreEqual(Fingers.IndexFinger, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[1].LeftHandFinger); - Assert.AreEqual(Fingers.MiddleFinger, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[2].LeftHandFinger); - Assert.AreEqual(Fingers.AnnularFinger, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[3].LeftHandFinger); - Assert.AreEqual(Fingers.LittleFinger, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[4].LeftHandFinger); - } - - [TestMethod] - public void TestRightHandFingerChord() - { - var tex = @":8 (3.1{rf 1} 3.2{rf 2} 3.3{rf 3} 3.4{rf 4} 3.5{rf 5})"; - var score = ParseTex(tex); - - Assert.AreEqual(1, score.Tracks.Count); - Assert.AreEqual(1, score.MasterBars.Count); - Assert.AreEqual(1, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats.Count); - Assert.AreEqual(5, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes.Count); - Assert.AreEqual(Fingers.Thumb, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].RightHandFinger); - Assert.AreEqual(Fingers.IndexFinger, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[1].RightHandFinger); - Assert.AreEqual(Fingers.MiddleFinger, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[2].RightHandFinger); - Assert.AreEqual(Fingers.AnnularFinger, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[3].RightHandFinger); - Assert.AreEqual(Fingers.LittleFinger, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[4].RightHandFinger); - } - - - [TestMethod] - public void TestUnstringed() - { - var tex = @"\tuning piano . c4 c#4 d4 d#4 | c4 db4 d4 eb4"; - var score = ParseTex(tex); - - Assert.AreEqual(1, score.Tracks.Count); - Assert.AreEqual(2, score.MasterBars.Count); - - Assert.AreEqual(4, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats.Count); - Assert.AreEqual(true, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].IsPiano); - Assert.AreEqual(60, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].RealValue); - Assert.AreEqual(true, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].IsPiano); - Assert.AreEqual(61, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].RealValue); - Assert.AreEqual(true, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].IsPiano); - Assert.AreEqual(62, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[2].Notes[0].RealValue); - Assert.AreEqual(true, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].IsPiano); - Assert.AreEqual(63, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[3].Notes[0].RealValue); - - - Assert.AreEqual(4, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats.Count); - Assert.AreEqual(true, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Notes[0].IsPiano); - Assert.AreEqual(60, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Notes[0].RealValue); - Assert.AreEqual(true, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Notes[0].IsPiano); - Assert.AreEqual(61, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[1].Notes[0].RealValue); - Assert.AreEqual(true, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Notes[0].IsPiano); - Assert.AreEqual(62, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[2].Notes[0].RealValue); - Assert.AreEqual(true, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Notes[0].IsPiano); - Assert.AreEqual(63, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[3].Notes[0].RealValue); - } - } -} diff --git a/Source/AlphaTab.Test/Importer/BinaryStylesheetParserTest.cs b/Source/AlphaTab.Test/Importer/BinaryStylesheetParserTest.cs deleted file mode 100644 index c648c38ae..000000000 --- a/Source/AlphaTab.Test/Importer/BinaryStylesheetParserTest.cs +++ /dev/null @@ -1,1873 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Text; -using AlphaTab.Importer; -using AlphaTab.IO; -using AlphaTab.Model; -using AlphaTab.Rendering.Utils; -using AlphaTab.Platform; -using AlphaTab.Platform.Model; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace AlphaTab.Test.Importer -{ - [TestClass] - public class BinaryStylesheetParserTest - { - internal byte[] Load(string name) - { - const string path = "TestFiles/"; - return TestPlatform.LoadFile(path + name); - } - - [TestMethod] - public void TestRead() - { - var readerBase = new BinaryStylesheetParser(); - readerBase.Parse(Load("GuitarPro7/BinaryStylesheet")); - var stylesheet = readerBase.Stylesheet; - //foreach (var r in stylesheet.Raw) - //{ - // Console.WriteLine("Assert.IsTrue(stylesheet.Raw.ContainsKey(\"" + r + "\"));"); - // var v = stylesheet.Raw[r]; - // if (v is string s) - // { - // Console.WriteLine("Assert.AreEqual(\"" + s + "\", stylesheet.Raw[\"" + r + "\"]);"); - // } - // else if (v is bool b) - // { - // Console.WriteLine("Assert.AreEqual(" + b.ToString().ToLower() + ", stylesheet.Raw[\"" + r + "\"]);"); - // } - // else if (v is float f) - // { - // Console.WriteLine("Assert.IsTrue(((float)stylesheet.Raw[\"" + r + "\"]).IsAlmostEqualTo(" + f.ToString("F9", CultureInfo.InvariantCulture) + "f));"); - // } - // else if (v is BendPoint bp) - // { - // Console.WriteLine("Assert.AreEqual(" + bp.Offset + ", ((BendPoint)stylesheet.Raw[\"" + r + "\"]).Offset);"); - // Console.WriteLine("Assert.AreEqual(" + bp.Value + ", ((BendPoint)stylesheet.Raw[\"" + r + "\"]).Value);"); - // } - // else if (v is Color c) - // { - // Console.WriteLine("Assert.AreEqual(" + c.R + ", ((Color)stylesheet.Raw[\"" + r + "\"]).R);"); - // Console.WriteLine("Assert.AreEqual(" + c.G + ", ((Color)stylesheet.Raw[\"" + r + "\"]).G);"); - // Console.WriteLine("Assert.AreEqual(" + c.B + ", ((Color)stylesheet.Raw[\"" + r + "\"]).B);"); - // Console.WriteLine("Assert.AreEqual(" + c.A + ", ((Color)stylesheet.Raw[\"" + r + "\"]).A);"); - // } - // else if (v is Bounds bd) - // { - // Console.WriteLine("Assert.AreEqual(" + bd.X + ", ((Bounds)stylesheet.Raw[\"" + r + "\"]).X);"); - // Console.WriteLine("Assert.AreEqual(" + bd.Y + ", ((Bounds)stylesheet.Raw[\"" + r + "\"]).Y);"); - // Console.WriteLine("Assert.AreEqual(" + bd.W + ", ((Bounds)stylesheet.Raw[\"" + r + "\"]).W);"); - // Console.WriteLine("Assert.AreEqual(" + bd.H + ", ((Bounds)stylesheet.Raw[\"" + r + "\"]).H);"); - // } - // else - // { - // Console.WriteLine("Assert.AreEqual(" + stylesheet.Raw[r] + ", stylesheet.Raw[\"" + r + "\"]);"); - // } - // Console.WriteLine(); - //} - - Assert.IsNotNull(stylesheet); - - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/chordNameStyle")); - Assert.AreEqual(2, stylesheet.Raw["Global/chordNameStyle"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("StandardNotation/deadNoteSymbol")); - Assert.AreEqual(0, stylesheet.Raw["StandardNotation/deadNoteSymbol"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Header/WordsAndMusic")); - Assert.AreEqual("Words & Music by %MUSIC%", stylesheet.Raw["Header/WordsAndMusic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/PickStrokePriority")); - Assert.AreEqual(1100, stylesheet.Raw["Global/PickStrokePriority"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Odd/drawOddFooter")); - Assert.AreEqual(true, stylesheet.Raw["Odd/drawOddFooter"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("TablatureNotation/tabRhythmPlacementVoice3")); - Assert.AreEqual(2, stylesheet.Raw["TablatureNotation/tabRhythmPlacementVoice3"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/HideTupletBracket")); - Assert.AreEqual(true, stylesheet.Raw["Global/HideTupletBracket"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/DrawChords")); - Assert.AreEqual(true, stylesheet.Raw["Global/DrawChords"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("System/codaSplitWidth")); - Assert.IsTrue(((float)stylesheet.Raw["System/codaSplitWidth"]).IsAlmostEqualTo(2.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/HarmonicPriority")); - Assert.AreEqual(2200, stylesheet.Raw["Global/HarmonicPriority"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/LetRingThroughoutPriority")); - Assert.AreEqual(2500, stylesheet.Raw["Global/LetRingThroughoutPriority"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/stretchFactor")); - Assert.IsTrue(((float)stylesheet.Raw["Global/stretchFactor"]).IsAlmostEqualTo(0.500000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("StandardNotation/bendHeight")); - Assert.IsTrue(((float)stylesheet.Raw["StandardNotation/bendHeight"]).IsAlmostEqualTo(2.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/ChordDiagramPriority")); - Assert.AreEqual(3000, stylesheet.Raw["Global/ChordDiagramPriority"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/AlternateEndingPriority")); - Assert.AreEqual(2800, stylesheet.Raw["Global/AlternateEndingPriority"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("StandardNotation/tieOffsetX")); - Assert.IsTrue(((float)stylesheet.Raw["StandardNotation/tieOffsetX"]).IsAlmostEqualTo(0.104999500f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/PalmMutePriority")); - Assert.AreEqual(1200, stylesheet.Raw["Global/PalmMutePriority"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("System/hideLyrics")); - Assert.AreEqual(false, stylesheet.Raw["System/hideLyrics"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/drawArpeggioArrow")); - Assert.AreEqual(true, stylesheet.Raw["Global/drawArpeggioArrow"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/HoPoPriority")); - Assert.AreEqual(800, stylesheet.Raw["Global/HoPoPriority"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Staff/repeatWidth")); - Assert.IsTrue(((float)stylesheet.Raw["Staff/repeatWidth"]).IsAlmostEqualTo(0.500000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("System/bracketWidth")); - Assert.IsTrue(((float)stylesheet.Raw["System/bracketWidth"]).IsAlmostEqualTo(0.500000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/TuningSpaceInFrontOfStaff")); - Assert.IsTrue(((float)stylesheet.Raw["Global/TuningSpaceInFrontOfStaff"]).IsAlmostEqualTo(2.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("StandardNotation/drawWholeRestOnEmptyBars")); - Assert.AreEqual(false, stylesheet.Raw["StandardNotation/drawWholeRestOnEmptyBars"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/miniBrowserPosition")); - Assert.AreEqual(0, stylesheet.Raw["Global/miniBrowserPosition"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("StandardNotation/hideUselessRests")); - Assert.AreEqual(true, stylesheet.Raw["StandardNotation/hideUselessRests"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/SpacingAffectFontsSize")); - Assert.AreEqual(true, stylesheet.Raw["Global/SpacingAffectFontsSize"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Even/drawEvenCopyright")); - Assert.AreEqual(true, stylesheet.Raw["Even/drawEvenCopyright"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/RepeatTargetPriority")); - Assert.AreEqual(3300, stylesheet.Raw["Global/RepeatTargetPriority"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/SVGFont")); - Assert.AreEqual(":/renderer/resources/notes.svg", stylesheet.Raw["Global/SVGFont"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Footer/PageNumberAlignment")); - Assert.AreEqual(2, stylesheet.Raw["Footer/PageNumberAlignment"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/graceFlatScaleFactor")); - Assert.IsTrue(((float)stylesheet.Raw["Global/graceFlatScaleFactor"]).IsAlmostEqualTo(0.833328200f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/shadowColorEnd")); - Assert.AreEqual(90, ((Color)stylesheet.Raw["Global/shadowColorEnd"]).R); - Assert.AreEqual(90, ((Color)stylesheet.Raw["Global/shadowColorEnd"]).G); - Assert.AreEqual(90, ((Color)stylesheet.Raw["Global/shadowColorEnd"]).B); - Assert.AreEqual(10, ((Color)stylesheet.Raw["Global/shadowColorEnd"]).A); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Even/EvenCopyright")); - Assert.AreEqual("%COPYRIGHT%", stylesheet.Raw["Even/EvenCopyright"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/GolpePriority")); - Assert.AreEqual(350, stylesheet.Raw["Global/GolpePriority"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/spaceSizeMM")); - Assert.IsTrue(((float)stylesheet.Raw["Global/spaceSizeMM"]).IsAlmostEqualTo(0.500000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("TablatureNotation/drawSecondNoteTrill")); - Assert.AreEqual(true, stylesheet.Raw["TablatureNotation/drawSecondNoteTrill"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("System/insertSize")); - Assert.AreEqual(2, stylesheet.Raw["System/insertSize"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("TablatureNotation/minimalInformationForHarmonic")); - Assert.AreEqual(true, stylesheet.Raw["TablatureNotation/minimalInformationForHarmonic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("PageSetup/pageTopMargin")); - Assert.IsTrue(((float)stylesheet.Raw["PageSetup/pageTopMargin"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("StandardNotation/augmentationDotRadius")); - Assert.IsTrue(((float)stylesheet.Raw["StandardNotation/augmentationDotRadius"]).IsAlmostEqualTo(0.125000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Odd/drawOddCopyright")); - Assert.AreEqual(false, stylesheet.Raw["Odd/drawOddCopyright"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("TablatureNotation/forceRhythmicBand")); - Assert.AreEqual(false, stylesheet.Raw["TablatureNotation/forceRhythmicBand"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("System/codaSplit")); - Assert.AreEqual(true, stylesheet.Raw["System/codaSplit"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("StandardNotation/tieMaxHeight")); - Assert.IsTrue(((float)stylesheet.Raw["StandardNotation/tieMaxHeight"]).IsAlmostEqualTo(2.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Header/WordsAndMusicAlignment")); - Assert.AreEqual(2, stylesheet.Raw["Header/WordsAndMusicAlignment"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Even/drawEvenFooter")); - Assert.AreEqual(true, stylesheet.Raw["Even/drawEvenFooter"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("StandardNotation/rightFingeringPositionSN")); - Assert.AreEqual(1, stylesheet.Raw["StandardNotation/rightFingeringPositionSN"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("System/bracketCurveHeight")); - Assert.IsTrue(((float)stylesheet.Raw["System/bracketCurveHeight"]).IsAlmostEqualTo(1.600006000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/FreeTimePriority")); - Assert.AreEqual(2700, stylesheet.Raw["Global/FreeTimePriority"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/ChordSpacingMillimeter")); - Assert.IsTrue(((float)stylesheet.Raw["Global/ChordSpacingMillimeter"]).IsAlmostEqualTo(2.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Header/drawAlbum")); - Assert.AreEqual(true, stylesheet.Raw["Header/drawAlbum"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("System/trackNameModeMulti")); - Assert.AreEqual(1, stylesheet.Raw["System/trackNameModeMulti"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("System/insertSizeSameTrack")); - Assert.AreEqual(1, stylesheet.Raw["System/insertSizeSameTrack"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("System/marginMinimalBeforeFirstNote")); - Assert.IsTrue(((float)stylesheet.Raw["System/marginMinimalBeforeFirstNote"]).IsAlmostEqualTo(0.500000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Header/Subtitle")); - Assert.AreEqual("%SUBTITLE%", stylesheet.Raw["Header/Subtitle"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/alphaSuggested")); - Assert.IsTrue(((float)stylesheet.Raw["Global/alphaSuggested"]).IsAlmostEqualTo(0.500000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Even/EvenHeaderAlignment")); - Assert.AreEqual(0, stylesheet.Raw["Even/EvenHeaderAlignment"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/TechniqueSymbol")); - Assert.AreEqual(25, stylesheet.Raw["Global/TechniqueSymbol"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/tuningBoxed")); - Assert.AreEqual(false, stylesheet.Raw["Global/tuningBoxed"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("StandardNotation/drawBends")); - Assert.AreEqual(true, stylesheet.Raw["StandardNotation/drawBends"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/mouseClickMaxTime")); - Assert.AreEqual(200, stylesheet.Raw["Global/mouseClickMaxTime"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/graceSharpScaleFactor")); - Assert.IsTrue(((float)stylesheet.Raw["Global/graceSharpScaleFactor"]).IsAlmostEqualTo(1.333344000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/GrayedOpacity")); - Assert.IsTrue(((float)stylesheet.Raw["Global/GrayedOpacity"]).IsAlmostEqualTo(0.400001500f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/WhammyBarVibratoPriority")); - Assert.AreEqual(1400, stylesheet.Raw["Global/WhammyBarVibratoPriority"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("TablatureNotation/noStaffLineForSlashs")); - Assert.AreEqual(false, stylesheet.Raw["TablatureNotation/noStaffLineForSlashs"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("StandardNotation/forceHorizontalBeam")); - Assert.AreEqual(false, stylesheet.Raw["StandardNotation/forceHorizontalBeam"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Staff/drawTimeSignatureC")); - Assert.AreEqual(false, stylesheet.Raw["Staff/drawTimeSignatureC"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("TablatureNotation/colorizeFretNumberInTablature")); - Assert.AreEqual(false, stylesheet.Raw["TablatureNotation/colorizeFretNumberInTablature"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Odd/OddHeaderAlignment")); - Assert.AreEqual(2, stylesheet.Raw["Odd/OddHeaderAlignment"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("TablatureNotation/tabRhythmPlacementVoice1")); - Assert.AreEqual(1, stylesheet.Raw["TablatureNotation/tabRhythmPlacementVoice1"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Header/drawTitle")); - Assert.AreEqual(true, stylesheet.Raw["Header/drawTitle"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Header/Words")); - Assert.AreEqual("Words by %WORDS%", stylesheet.Raw["Header/Words"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Header/MusicAlignment")); - Assert.AreEqual(2, stylesheet.Raw["Header/MusicAlignment"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/OctavePriority")); - Assert.AreEqual(1800, stylesheet.Raw["Global/OctavePriority"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("TablatureNotation/deadNoteLetter")); - Assert.AreEqual(88, stylesheet.Raw["TablatureNotation/deadNoteLetter"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Even/EvenCopyrightAlignment")); - Assert.AreEqual(1, stylesheet.Raw["Even/EvenCopyrightAlignment"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Odd/OddCopyrightAlignment")); - Assert.AreEqual(1, stylesheet.Raw["Odd/OddCopyrightAlignment"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("TablatureNotation/BeamsInsideStaff")); - Assert.AreEqual(false, stylesheet.Raw["TablatureNotation/BeamsInsideStaff"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("System/showTrackNameMulti")); - Assert.AreEqual(true, stylesheet.Raw["System/showTrackNameMulti"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/renderingStyleMode")); - Assert.AreEqual(0, stylesheet.Raw["Global/renderingStyleMode"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/LyricPriority")); - Assert.AreEqual(2450, stylesheet.Raw["Global/LyricPriority"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/widgetWaitTime")); - Assert.AreEqual(750, stylesheet.Raw["Global/widgetWaitTime"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("PageSetup/pageOrientation")); - Assert.AreEqual(0, stylesheet.Raw["PageSetup/pageOrientation"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("System/bracketExtendMode")); - Assert.AreEqual(0, stylesheet.Raw["System/bracketExtendMode"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Even/EvenFooter")); - Assert.AreEqual("%PAGE%/%PAGES%", stylesheet.Raw["Even/EvenFooter"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("StandardNotation/stemLineWidth")); - Assert.IsTrue(((float)stylesheet.Raw["StandardNotation/stemLineWidth"]).IsAlmostEqualTo(0.500000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/CapoPriority")); - Assert.AreEqual(2600, stylesheet.Raw["Global/CapoPriority"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/backgroundColor")); - Assert.AreEqual(99, ((Color)stylesheet.Raw["Global/backgroundColor"]).R); - Assert.AreEqual(104, ((Color)stylesheet.Raw["Global/backgroundColor"]).G); - Assert.AreEqual(101, ((Color)stylesheet.Raw["Global/backgroundColor"]).B); - Assert.AreEqual(255, ((Color)stylesheet.Raw["Global/backgroundColor"]).A); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("System/drawLyricsUpside")); - Assert.AreEqual(false, stylesheet.Raw["System/drawLyricsUpside"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Header/Tabber")); - Assert.AreEqual("Tabbed by %TABBER%", stylesheet.Raw["Header/Tabber"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Even/EvenCopyright2")); - Assert.AreEqual("All Rights Reserved - International Copyright Secured", stylesheet.Raw["Even/EvenCopyright2"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("System/strictOrDirectedIsSystemWide")); - Assert.AreEqual(false, stylesheet.Raw["System/strictOrDirectedIsSystemWide"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Odd/OddCopyright2")); - Assert.AreEqual("All Rights Reserved - International Copyright Secured", stylesheet.Raw["Odd/OddCopyright2"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("StandardNotation/tieWeight")); - Assert.IsTrue(((float)stylesheet.Raw["StandardNotation/tieWeight"]).IsAlmostEqualTo(0.174999200f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Staff/fingeringMode")); - Assert.AreEqual(0, stylesheet.Raw["Staff/fingeringMode"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("System/horizontalTrackNameOnFirstSystem")); - Assert.AreEqual(false, stylesheet.Raw["System/horizontalTrackNameOnFirstSystem"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/chordMode")); - Assert.AreEqual(0, stylesheet.Raw["Global/chordMode"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/firstSystemMarginMillimeter")); - Assert.AreEqual(0, stylesheet.Raw["Global/firstSystemMarginMillimeter"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("System/drawChordInScore")); - Assert.AreEqual(false, stylesheet.Raw["System/drawChordInScore"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("TablatureNotation/slashStemDown")); - Assert.AreEqual(false, stylesheet.Raw["TablatureNotation/slashStemDown"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("StandardNotation/stemMinYDeviation")); - Assert.IsTrue(((float)stylesheet.Raw["StandardNotation/stemMinYDeviation"]).IsAlmostEqualTo(0.699996900f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Header/AlbumAlignment")); - Assert.AreEqual(1, stylesheet.Raw["Header/AlbumAlignment"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("StandardNotation/ledgerLineSize")); - Assert.IsTrue(((float)stylesheet.Raw["StandardNotation/ledgerLineSize"]).IsAlmostEqualTo(0.500000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/cursorInsertSize")); - Assert.AreEqual(2, stylesheet.Raw["Global/cursorInsertSize"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Footer/drawCopyright2")); - Assert.AreEqual(true, stylesheet.Raw["Footer/drawCopyright2"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/placementMargin")); - Assert.AreEqual(40, stylesheet.Raw["Global/placementMargin"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Header/Title")); - Assert.AreEqual("%TITLE%", stylesheet.Raw["Header/Title"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/BarNumberStyle")); - Assert.AreEqual(0, stylesheet.Raw["Global/BarNumberStyle"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Odd/OddCopyright")); - Assert.AreEqual("%COPYRIGHT%", stylesheet.Raw["Odd/OddCopyright"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("TablatureNotation/leftFingeringPositionTAB")); - Assert.AreEqual(1, stylesheet.Raw["TablatureNotation/leftFingeringPositionTAB"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("StandardNotation/accidentalSpaceAfter")); - Assert.IsTrue(((float)stylesheet.Raw["StandardNotation/accidentalSpaceAfter"]).IsAlmostEqualTo(0.400001500f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/WhammyBarPriority")); - Assert.AreEqual(2300, stylesheet.Raw["Global/WhammyBarPriority"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("System/showTrackNameSingle")); - Assert.AreEqual(true, stylesheet.Raw["System/showTrackNameSingle"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("TablatureNotation/rightFingeringPositionTAB")); - Assert.AreEqual(0, stylesheet.Raw["TablatureNotation/rightFingeringPositionTAB"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/WahPriority")); - Assert.AreEqual(600, stylesheet.Raw["Global/WahPriority"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("TablatureNotation/tabRhythmPlacementVoice4")); - Assert.AreEqual(2, stylesheet.Raw["TablatureNotation/tabRhythmPlacementVoice4"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("StandardNotation/stemSecondariesSpace")); - Assert.IsTrue(((float)stylesheet.Raw["StandardNotation/stemSecondariesSpace"]).IsAlmostEqualTo(0.500000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/TempoPriority")); - Assert.AreEqual(3600, stylesheet.Raw["Global/TempoPriority"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("PageSetup/pageRightMargin")); - Assert.IsTrue(((float)stylesheet.Raw["PageSetup/pageRightMargin"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("StandardNotation/flagsSpacing")); - Assert.IsTrue(((float)stylesheet.Raw["StandardNotation/flagsSpacing"]).IsAlmostEqualTo(1.600006000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("System/horizontalTrackNameOnOtherSystems")); - Assert.AreEqual(true, stylesheet.Raw["System/horizontalTrackNameOnOtherSystems"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/TripletFeelPriority")); - Assert.AreEqual(3500, stylesheet.Raw["Global/TripletFeelPriority"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("StandardNotation/alignNotes")); - Assert.AreEqual(true, stylesheet.Raw["StandardNotation/alignNotes"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("PageSetup/pagePortraitWidth")); - Assert.IsTrue(((float)stylesheet.Raw["PageSetup/pagePortraitWidth"]).IsAlmostEqualTo(128.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("System/shortTrackNameOnFirstSystem")); - Assert.AreEqual(true, stylesheet.Raw["System/shortTrackNameOnFirstSystem"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("TablatureNotation/circleAroundHalfAndWhole")); - Assert.AreEqual(true, stylesheet.Raw["TablatureNotation/circleAroundHalfAndWhole"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("StandardNotation/stemLineSize")); - Assert.IsTrue(((float)stylesheet.Raw["StandardNotation/stemLineSize"]).IsAlmostEqualTo(2.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("StandardNotation/forceHorizontalBeamForDrums")); - Assert.AreEqual(true, stylesheet.Raw["StandardNotation/forceHorizontalBeamForDrums"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("PageSetup/pagePortraitHeight")); - Assert.IsTrue(((float)stylesheet.Raw["PageSetup/pagePortraitHeight"]).IsAlmostEqualTo(256.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/FermataPriority")); - Assert.AreEqual(2400, stylesheet.Raw["Global/FermataPriority"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Even/drawEvenCopyright2")); - Assert.AreEqual(true, stylesheet.Raw["Even/drawEvenCopyright2"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/SlapPopPriority")); - Assert.AreEqual(1700, stylesheet.Raw["Global/SlapPopPriority"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Header/drawSubtitle")); - Assert.AreEqual(true, stylesheet.Raw["Header/drawSubtitle"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/AccentPriority")); - Assert.AreEqual(100, stylesheet.Raw["Global/AccentPriority"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Slash/displayFretStringOfSingleNotesInSlash")); - Assert.AreEqual(false, stylesheet.Raw["Slash/displayFretStringOfSingleNotesInSlash"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Even/enable")); - Assert.AreEqual(false, stylesheet.Raw["Even/enable"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Staff/tupletMode")); - Assert.AreEqual(0, stylesheet.Raw["Staff/tupletMode"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("StandardNotation/accidentalSpaceBefore")); - Assert.IsTrue(((float)stylesheet.Raw["StandardNotation/accidentalSpaceBefore"]).IsAlmostEqualTo(0.300003100f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/pageColor")); - Assert.AreEqual(245, ((Color)stylesheet.Raw["Global/pageColor"]).R); - Assert.AreEqual(245, ((Color)stylesheet.Raw["Global/pageColor"]).G); - Assert.AreEqual(245, ((Color)stylesheet.Raw["Global/pageColor"]).B); - Assert.AreEqual(255, ((Color)stylesheet.Raw["Global/pageColor"]).A); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Odd/drawOddHeader")); - Assert.AreEqual(false, stylesheet.Raw["Odd/drawOddHeader"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Staff/showTupletDenominator")); - Assert.AreEqual(false, stylesheet.Raw["Staff/showTupletDenominator"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Header/TabberAlignment")); - Assert.AreEqual(2, stylesheet.Raw["Header/TabberAlignment"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("PageSetup/pageScale")); - Assert.IsTrue(((float)stylesheet.Raw["PageSetup/pageScale"]).IsAlmostEqualTo(0.500000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/SoundbankChangePriority")); - Assert.AreEqual(3250, stylesheet.Raw["Global/SoundbankChangePriority"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/shadowColorBegin")); - Assert.AreEqual(0, ((Color)stylesheet.Raw["Global/shadowColorBegin"]).R); - Assert.AreEqual(0, ((Color)stylesheet.Raw["Global/shadowColorBegin"]).G); - Assert.AreEqual(0, ((Color)stylesheet.Raw["Global/shadowColorBegin"]).B); - Assert.AreEqual(255, ((Color)stylesheet.Raw["Global/shadowColorBegin"]).A); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Header/ArtistAlignment")); - Assert.AreEqual(1, stylesheet.Raw["Header/ArtistAlignment"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Footer/Copyright")); - Assert.AreEqual("%COPYRIGHT%", stylesheet.Raw["Footer/Copyright"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("System/marginAfterLastNote")); - Assert.IsTrue(((float)stylesheet.Raw["System/marginAfterLastNote"]).IsAlmostEqualTo(0.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Odd/OddFooter")); - Assert.AreEqual("%PAGE%/%PAGES%", stylesheet.Raw["Odd/OddFooter"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Header/drawWords")); - Assert.AreEqual(true, stylesheet.Raw["Header/drawWords"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/useSystemSignSeparator")); - Assert.AreEqual(false, stylesheet.Raw["Global/useSystemSignSeparator"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("StandardNotation/leftFingeringPositionSN")); - Assert.AreEqual(1, stylesheet.Raw["StandardNotation/leftFingeringPositionSN"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/ChordSizeMillimeter")); - Assert.IsTrue(((float)stylesheet.Raw["Global/ChordSizeMillimeter"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/LeftHandTapPriority")); - Assert.AreEqual(1650, stylesheet.Raw["Global/LeftHandTapPriority"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Staff/staffLinesColor")); - Assert.AreEqual(180, ((Color)stylesheet.Raw["Staff/staffLinesColor"]).R); - Assert.AreEqual(180, ((Color)stylesheet.Raw["Staff/staffLinesColor"]).G); - Assert.AreEqual(180, ((Color)stylesheet.Raw["Staff/staffLinesColor"]).B); - Assert.AreEqual(255, ((Color)stylesheet.Raw["Staff/staffLinesColor"]).A); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Footer/PageNumber")); - Assert.AreEqual("%PAGE%/%PAGES%", stylesheet.Raw["Footer/PageNumber"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/LeftFingeringPriority")); - Assert.AreEqual(175, stylesheet.Raw["Global/LeftFingeringPriority"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("TablatureNotation/extendRhythmicInTablature")); - Assert.AreEqual(true, stylesheet.Raw["TablatureNotation/extendRhythmicInTablature"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/TuningSpacingMillimeter")); - Assert.IsTrue(((float)stylesheet.Raw["Global/TuningSpacingMillimeter"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("TablatureNotation/ShowBeamsForRest")); - Assert.AreEqual(false, stylesheet.Raw["TablatureNotation/ShowBeamsForRest"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Header/drawWordsAndMusic")); - Assert.AreEqual(true, stylesheet.Raw["Header/drawWordsAndMusic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("TablatureNotation/unselectedVoiceOpacity")); - Assert.IsTrue(((float)stylesheet.Raw["TablatureNotation/unselectedVoiceOpacity"]).IsAlmostEqualTo(0.400001500f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/HairpinPriority")); - Assert.AreEqual(400, stylesheet.Raw["Global/HairpinPriority"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/SlashFretStringPriority")); - Assert.AreEqual(3050, stylesheet.Raw["Global/SlashFretStringPriority"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Header/drawTabber")); - Assert.AreEqual(false, stylesheet.Raw["Header/drawTabber"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/debugger")); - Assert.AreEqual(false, stylesheet.Raw["Global/debugger"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("System/bendMinimalWidth")); - Assert.IsTrue(((float)stylesheet.Raw["System/bendMinimalWidth"]).IsAlmostEqualTo(0.100000400f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("StandardNotation/stemLineSizeMinimum")); - Assert.IsTrue(((float)stylesheet.Raw["StandardNotation/stemLineSizeMinimum"]).IsAlmostEqualTo(0.500000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("TablatureNotation/noBarLineForSlashs")); - Assert.AreEqual(false, stylesheet.Raw["TablatureNotation/noBarLineForSlashs"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("System/sameSizeForAllBars")); - Assert.AreEqual(false, stylesheet.Raw["System/sameSizeForAllBars"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/hideGlobalTempo")); - Assert.AreEqual(false, stylesheet.Raw["Global/hideGlobalTempo"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("System/bracketCurveWidth")); - Assert.IsTrue(((float)stylesheet.Raw["System/bracketCurveWidth"]).IsAlmostEqualTo(6.400024000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/DisplayTuning")); - Assert.AreEqual(true, stylesheet.Raw["Global/DisplayTuning"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/TimerPriority")); - Assert.AreEqual(3100, stylesheet.Raw["Global/TimerPriority"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/GuitarStringNumberPriority")); - Assert.AreEqual(700, stylesheet.Raw["Global/GuitarStringNumberPriority"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/cursorMultipleSelectionBorderColor")); - Assert.AreEqual(169, ((Color)stylesheet.Raw["Global/cursorMultipleSelectionBorderColor"]).R); - Assert.AreEqual(179, ((Color)stylesheet.Raw["Global/cursorMultipleSelectionBorderColor"]).G); - Assert.AreEqual(141, ((Color)stylesheet.Raw["Global/cursorMultipleSelectionBorderColor"]).B); - Assert.AreEqual(255, ((Color)stylesheet.Raw["Global/cursorMultipleSelectionBorderColor"]).A); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/LeftHandVibratoPriority")); - Assert.AreEqual(1500, stylesheet.Raw["Global/LeftHandVibratoPriority"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/FreeTextPriority")); - Assert.AreEqual(3200, stylesheet.Raw["Global/FreeTextPriority"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("StandardNotation/tieOffsetY")); - Assert.IsTrue(((float)stylesheet.Raw["StandardNotation/tieOffsetY"]).IsAlmostEqualTo(0.125000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Odd/drawOddCopyright2")); - Assert.AreEqual(true, stylesheet.Raw["Odd/drawOddCopyright2"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("StandardNotation/stemMaxYDeviation")); - Assert.IsTrue(((float)stylesheet.Raw["StandardNotation/stemMaxYDeviation"]).IsAlmostEqualTo(0.500000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("System/trackNameModeSingle")); - Assert.AreEqual(0, stylesheet.Raw["System/trackNameModeSingle"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/TrillPriority")); - Assert.AreEqual(1900, stylesheet.Raw["Global/TrillPriority"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Staff/barLinesColor")); - Assert.AreEqual(100, ((Color)stylesheet.Raw["Staff/barLinesColor"]).R); - Assert.AreEqual(100, ((Color)stylesheet.Raw["Staff/barLinesColor"]).G); - Assert.AreEqual(100, ((Color)stylesheet.Raw["Staff/barLinesColor"]).B); - Assert.AreEqual(255, ((Color)stylesheet.Raw["Staff/barLinesColor"]).A); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/OrnamentPriority")); - Assert.AreEqual(2100, stylesheet.Raw["Global/OrnamentPriority"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("System/bracketBetweenNSAndTab")); - Assert.AreEqual(true, stylesheet.Raw["System/bracketBetweenNSAndTab"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Footer/Copyright2")); - Assert.AreEqual("All Rights Reserved - International Copyright Secured", stylesheet.Raw["Footer/Copyright2"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/SectionPriority")); - Assert.AreEqual(3400, stylesheet.Raw["Global/SectionPriority"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("StandardNotation/tieMinHeight")); - Assert.IsTrue(((float)stylesheet.Raw["StandardNotation/tieMinHeight"]).IsAlmostEqualTo(0.500000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/translateTunings")); - Assert.AreEqual(false, stylesheet.Raw["Global/translateTunings"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/RightFingeringPriority")); - Assert.AreEqual(150, stylesheet.Raw["Global/RightFingeringPriority"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("StandardNotation/accidentalIntersSpacing")); - Assert.IsTrue(((float)stylesheet.Raw["StandardNotation/accidentalIntersSpacing"]).IsAlmostEqualTo(0.026249890f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/RepeatCountPriority")); - Assert.AreEqual(300, stylesheet.Raw["Global/RepeatCountPriority"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Staff/repeatSpace")); - Assert.IsTrue(((float)stylesheet.Raw["Staff/repeatSpace"]).IsAlmostEqualTo(0.500000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/TapPriority")); - Assert.AreEqual(1600, stylesheet.Raw["Global/TapPriority"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/LetRingPriority")); - Assert.AreEqual(1250, stylesheet.Raw["Global/LetRingPriority"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Header/Album")); - Assert.AreEqual("%ALBUM%", stylesheet.Raw["Header/Album"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("System/marginBeforeFirstNote")); - Assert.IsTrue(((float)stylesheet.Raw["System/marginBeforeFirstNote"]).IsAlmostEqualTo(2.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("StandardNotation/tieHeightExpansion")); - Assert.IsTrue(((float)stylesheet.Raw["StandardNotation/tieHeightExpansion"]).IsAlmostEqualTo(0.125000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/TuningPosition")); - Assert.AreEqual(0, stylesheet.Raw["Global/TuningPosition"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("System/shortTrackNameOnOtherSystems")); - Assert.AreEqual(true, stylesheet.Raw["System/shortTrackNameOnOtherSystems"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/forceBarCountBySystem")); - Assert.AreEqual(false, stylesheet.Raw["Global/forceBarCountBySystem"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("System/barIndexDrawType")); - Assert.AreEqual(0, stylesheet.Raw["System/barIndexDrawType"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/ChordMarginMillimeter")); - Assert.IsTrue(((float)stylesheet.Raw["Global/ChordMarginMillimeter"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("TablatureNotation/showCapoRelative")); - Assert.AreEqual(false, stylesheet.Raw["TablatureNotation/showCapoRelative"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Odd/OddFooterAlignment")); - Assert.AreEqual(2, stylesheet.Raw["Odd/OddFooterAlignment"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/DynamicPriority")); - Assert.AreEqual(200, stylesheet.Raw["Global/DynamicPriority"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("StandardNotation/stemPartialSecondarySize")); - Assert.IsTrue(((float)stylesheet.Raw["StandardNotation/stemPartialSecondarySize"]).IsAlmostEqualTo(0.699996900f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/widgetTransitionTime")); - Assert.AreEqual(500, stylesheet.Raw["Global/widgetTransitionTime"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("System/inScoreChordSize")); - Assert.IsTrue(((float)stylesheet.Raw["System/inScoreChordSize"]).IsAlmostEqualTo(11.199950000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Staff/hideMultipleTiedNotes")); - Assert.AreEqual(false, stylesheet.Raw["Staff/hideMultipleTiedNotes"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("StandardNotation/beamLineWidth")); - Assert.IsTrue(((float)stylesheet.Raw["StandardNotation/beamLineWidth"]).IsAlmostEqualTo(0.400001500f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/textTransitionTime")); - Assert.AreEqual(500, stylesheet.Raw["Global/textTransitionTime"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("TablatureNotation/displayBeamsOnRestInTablature")); - Assert.AreEqual(false, stylesheet.Raw["TablatureNotation/displayBeamsOnRestInTablature"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/BarrePriority")); - Assert.AreEqual(2900, stylesheet.Raw["Global/BarrePriority"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/FadePriority")); - Assert.AreEqual(1300, stylesheet.Raw["Global/FadePriority"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Header/TitleAlignment")); - Assert.AreEqual(1, stylesheet.Raw["Header/TitleAlignment"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Even/EvenHeader")); - Assert.AreEqual("%TITLE% by %ARTIST%", stylesheet.Raw["Even/EvenHeader"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("TablatureNotation/tabRhythmPlacementVoice2")); - Assert.AreEqual(1, stylesheet.Raw["TablatureNotation/tabRhythmPlacementVoice2"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("StandardNotation/stemLineSizeMaximum")); - Assert.IsTrue(((float)stylesheet.Raw["StandardNotation/stemLineSizeMaximum"]).IsAlmostEqualTo(2.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/showClefAtEachSystemStart")); - Assert.AreEqual(true, stylesheet.Raw["Global/showClefAtEachSystemStart"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Header/drawArtist")); - Assert.AreEqual(true, stylesheet.Raw["Header/drawArtist"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/TuningMarginMillimeter")); - Assert.IsTrue(((float)stylesheet.Raw["Global/TuningMarginMillimeter"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Header/drawMusic")); - Assert.AreEqual(true, stylesheet.Raw["Header/drawMusic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("TablatureNotation/maxBendExpansionFactor")); - Assert.IsTrue(((float)stylesheet.Raw["TablatureNotation/maxBendExpansionFactor"]).IsAlmostEqualTo(0.500000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/spaceSizePixel")); - Assert.AreEqual(5, stylesheet.Raw["Global/spaceSizePixel"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Staff/leftFingeringMode")); - Assert.AreEqual(0, stylesheet.Raw["Staff/leftFingeringMode"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/TuningColumnCount")); - Assert.AreEqual(2, stylesheet.Raw["Global/TuningColumnCount"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("PageSetup/pageBottomMargin")); - Assert.IsTrue(((float)stylesheet.Raw["PageSetup/pageBottomMargin"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("TablatureNotation/showTiedNotesLikeGhostNotes")); - Assert.AreEqual(false, stylesheet.Raw["TablatureNotation/showTiedNotesLikeGhostNotes"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("StandardNotation/noteHeadColor")); - Assert.AreEqual(false, stylesheet.Raw["StandardNotation/noteHeadColor"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/barCountBySystem")); - Assert.AreEqual(0, stylesheet.Raw["Global/barCountBySystem"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/SlidePriority")); - Assert.AreEqual(850, stylesheet.Raw["Global/SlidePriority"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Header/Music")); - Assert.AreEqual("Music by %MUSIC%", stylesheet.Raw["Header/Music"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Footer/drawCopyright")); - Assert.AreEqual(true, stylesheet.Raw["Footer/drawCopyright"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/TuningDisplayMode")); - Assert.AreEqual(2, stylesheet.Raw["Global/TuningDisplayMode"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/PickScrapePriority")); - Assert.AreEqual(875, stylesheet.Raw["Global/PickScrapePriority"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/allowMultirests")); - Assert.AreEqual(true, stylesheet.Raw["Global/allowMultirests"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("PageSetup/pageLeftMargin")); - Assert.IsTrue(((float)stylesheet.Raw["PageSetup/pageLeftMargin"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Header/Artist")); - Assert.AreEqual("%ARTIST%", stylesheet.Raw["Header/Artist"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Staff/hopoMode")); - Assert.AreEqual(1, stylesheet.Raw["Staff/hopoMode"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Even/drawEvenHeader")); - Assert.AreEqual(true, stylesheet.Raw["Even/drawEvenHeader"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/TuningMode")); - Assert.AreEqual(4, stylesheet.Raw["Global/TuningMode"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("StandardNotation/hideDynamics")); - Assert.AreEqual(false, stylesheet.Raw["StandardNotation/hideDynamics"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/RasgueadoPriority")); - Assert.AreEqual(2000, stylesheet.Raw["Global/RasgueadoPriority"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/graceNoteScaleFactor")); - Assert.IsTrue(((float)stylesheet.Raw["Global/graceNoteScaleFactor"]).IsAlmostEqualTo(0.899993900f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/cursorMultipleSelectionInnerColor")); - Assert.AreEqual(116, ((Color)stylesheet.Raw["Global/cursorMultipleSelectionInnerColor"]).R); - Assert.AreEqual(117, ((Color)stylesheet.Raw["Global/cursorMultipleSelectionInnerColor"]).G); - Assert.AreEqual(112, ((Color)stylesheet.Raw["Global/cursorMultipleSelectionInnerColor"]).B); - Assert.AreEqual(50, ((Color)stylesheet.Raw["Global/cursorMultipleSelectionInnerColor"]).A); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("StandardNotation/tieCurve")); - Assert.IsTrue(((float)stylesheet.Raw["StandardNotation/tieCurve"]).IsAlmostEqualTo(0.125000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("StandardNotation/translatedTuningImpactNotation")); - Assert.AreEqual(true, stylesheet.Raw["StandardNotation/translatedTuningImpactNotation"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/RepeatJumpPriority")); - Assert.AreEqual(3300, stylesheet.Raw["Global/RepeatJumpPriority"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Header/SubtitleAlignment")); - Assert.AreEqual(1, stylesheet.Raw["Header/SubtitleAlignment"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Even/EvenFooterAlignment")); - Assert.AreEqual(0, stylesheet.Raw["Even/EvenFooterAlignment"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Header/WordsAlignment")); - Assert.AreEqual(0, stylesheet.Raw["Header/WordsAlignment"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Staff/drawSlOnSlides")); - Assert.AreEqual(true, stylesheet.Raw["Staff/drawSlOnSlides"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("System/ExtendedBarLines")); - Assert.AreEqual(false, stylesheet.Raw["System/ExtendedBarLines"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Footer/drawPageNumber")); - Assert.AreEqual(true, stylesheet.Raw["Footer/drawPageNumber"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Odd/OddHeader")); - Assert.AreEqual("%TITLE% by %ARTIST%", stylesheet.Raw["Odd/OddHeader"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/fontSizeAdjustment")); - Assert.IsTrue(((float)stylesheet.Raw["Global/fontSizeAdjustment"]).IsAlmostEqualTo(0.419998200f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Footer/CopyrightAlignment")); - Assert.AreEqual(1, stylesheet.Raw["Footer/CopyrightAlignment"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/rhythmPower")); - Assert.IsTrue(((float)stylesheet.Raw["Global/rhythmPower"]).IsAlmostEqualTo(0.500000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Lyric/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Lyric/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Album/name")); - Assert.AreEqual("Times New Roman", stylesheet.Raw["Global/Font/Album/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/VerticalTrackName/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/VerticalTrackName/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/DirectionText/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/DirectionText/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("System/showTrackNameOnFirstSystem")); - Assert.AreEqual(true, stylesheet.Raw["System/showTrackNameOnFirstSystem"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Header/DrawWords")); - Assert.AreEqual(true, stylesheet.Raw["Header/DrawWords"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Fingerings/italic")); - Assert.AreEqual(true, stylesheet.Raw["Global/Font/Fingerings/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Tuplet/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Tuplet/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/LetRing/name")); - Assert.AreEqual("@musicalFontText()", stylesheet.Raw["Global/Font/LetRing/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/TrackShortName/name")); - Assert.AreEqual("Arial", stylesheet.Raw["Global/Font/TrackShortName/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Title/name")); - Assert.AreEqual("Times New Roman", stylesheet.Raw["Global/Font/Title/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/PalmMute/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/PalmMute/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Big/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/Big/size"]).IsAlmostEqualTo(3.600037000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/HorizontalTrackName/bold")); - Assert.AreEqual(true, stylesheet.Raw["Global/Font/HorizontalTrackName/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/EvenCopyright/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/EvenCopyright/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/AutoLetRing/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/AutoLetRing/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/SmallFret/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/SmallFret/size"]).IsAlmostEqualTo(2.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Slide/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Slide/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/DirectionText/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/DirectionText/size"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/EvenFooter/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/EvenFooter/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Barre/name")); - Assert.AreEqual("Times New Roman", stylesheet.Raw["Global/Font/Barre/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Chord/bold")); - Assert.AreEqual(true, stylesheet.Raw["Global/Font/Chord/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Fingerings/name")); - Assert.AreEqual("@musicalFontText()", stylesheet.Raw["Global/Font/Fingerings/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Header/Copyright1Alignment")); - Assert.AreEqual(1, stylesheet.Raw["Header/Copyright1Alignment"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/BarIndex/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/BarIndex/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Tuplet/name")); - Assert.AreEqual("@musicalFontText()", stylesheet.Raw["Global/Font/Tuplet/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Copyright1/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Copyright1/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Fret/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Fret/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/SlashFretString/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/SlashFretString/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/LetRing/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/LetRing/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Album/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Album/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Normal/name")); - Assert.AreEqual("Arial", stylesheet.Raw["Global/Font/Normal/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Subtitle/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Subtitle/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Tuning/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/Tuning/size"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Copyright1/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/Copyright1/size"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Album/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Album/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Copyright2/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Copyright2/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/DirectionText/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/DirectionText/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/WordsAndMusic/name")); - Assert.AreEqual("Times New Roman", stylesheet.Raw["Global/Font/WordsAndMusic/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/OddFooter/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/OddFooter/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Multirest/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Multirest/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/PageNumber/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/PageNumber/size"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Header/DrawCopyright1")); - Assert.AreEqual(true, stylesheet.Raw["Header/DrawCopyright1"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Tabber/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Tabber/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Tiny/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/Tiny/size"]).IsAlmostEqualTo(2.799988000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Tuning/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Tuning/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Timer/border")); - Assert.AreEqual(true, stylesheet.Raw["Global/Font/Timer/border"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/BarIndex/name")); - Assert.AreEqual("Times New Roman", stylesheet.Raw["Global/Font/BarIndex/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Title/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Title/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/DirectionGlyph/proportionnal")); - Assert.AreEqual(true, stylesheet.Raw["Global/Font/DirectionGlyph/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Copyright2/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Copyright2/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Tiny/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Tiny/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Tempo/bold")); - Assert.AreEqual(true, stylesheet.Raw["Global/Font/Tempo/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Big/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Big/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Chord/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Chord/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/PageNumber/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/PageNumber/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/BarIndex/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/BarIndex/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/SlashFretString/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/SlashFretString/size"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/HarmonicText/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/HarmonicText/size"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Fret/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Fret/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Chord/name")); - Assert.AreEqual("Times New Roman", stylesheet.Raw["Global/Font/Chord/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Section/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Section/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/EvenFooter/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/EvenFooter/size"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/OddFooter/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/OddFooter/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/SmallFret/name")); - Assert.AreEqual("Arial", stylesheet.Raw["Global/Font/SmallFret/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/TrackShortName/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/TrackShortName/size"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/PalmMute/name")); - Assert.AreEqual("@musicalFontText()", stylesheet.Raw["Global/Font/PalmMute/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Tempo/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Tempo/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Artist/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Artist/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Album/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/Album/size"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/FreeTime/italic")); - Assert.AreEqual(true, stylesheet.Raw["Global/Font/FreeTime/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Tuning/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Tuning/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Lyric/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Lyric/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/PalmMute/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/PalmMute/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/TrackName/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/TrackName/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/OddHeader/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/OddHeader/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Music/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Music/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/RepeatBarCount/name")); - Assert.AreEqual("@musicalFontText()", stylesheet.Raw["Global/Font/RepeatBarCount/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Header/Copyright2")); - Assert.AreEqual("%COPYRIGHT2%", stylesheet.Raw["Header/Copyright2"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/BigBold/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/BigBold/size"]).IsAlmostEqualTo(14.400150000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Capo/name")); - Assert.AreEqual("Times New Roman", stylesheet.Raw["Global/Font/Capo/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Header/DrawCopyright2")); - Assert.AreEqual(true, stylesheet.Raw["Header/DrawCopyright2"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Slide/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/Slide/size"]).IsAlmostEqualTo(2.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Tuning/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Tuning/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Copyright/name")); - Assert.AreEqual("Times New Roman", stylesheet.Raw["Global/Font/Copyright/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/EvenHeader/name")); - Assert.AreEqual("Times New Roman", stylesheet.Raw["Global/Font/EvenHeader/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/HarmonicText/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/HarmonicText/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Capo/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Capo/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Slide/name")); - Assert.AreEqual("@musicalFontText()", stylesheet.Raw["Global/Font/Slide/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/BarIndex/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/BarIndex/size"]).IsAlmostEqualTo(2.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/EvenHeader/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/EvenHeader/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Tempo/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Tempo/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/OddHeader/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/OddHeader/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Section/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Section/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Copyright2/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Copyright2/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/WordsAndMusic/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/WordsAndMusic/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Small/name")); - Assert.AreEqual("Arial", stylesheet.Raw["Global/Font/Small/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Words/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Words/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Barre/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Barre/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/cursorMultilpeSelectionMargin")); - Assert.AreEqual(1, stylesheet.Raw["Global/cursorMultilpeSelectionMargin"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/AutoLetRing/name")); - Assert.AreEqual("@musicalFontText()", stylesheet.Raw["Global/Font/AutoLetRing/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Fingerings/proportionnal")); - Assert.AreEqual(true, stylesheet.Raw["Global/Font/Fingerings/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Rasgueado/italic")); - Assert.AreEqual(true, stylesheet.Raw["Global/Font/Rasgueado/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Header/Copyright2Alignment")); - Assert.AreEqual(1, stylesheet.Raw["Header/Copyright2Alignment"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Music/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/Music/size"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Copyright/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Copyright/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/OddCopyright/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/OddCopyright/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("System/bracketExtendByFamily")); - Assert.AreEqual(false, stylesheet.Raw["System/bracketExtendByFamily"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Tiny/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Tiny/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Huge/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Huge/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Rasgueado/name")); - Assert.AreEqual("@musicalFontText()", stylesheet.Raw["Global/Font/Rasgueado/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Words/name")); - Assert.AreEqual("Times New Roman", stylesheet.Raw["Global/Font/Words/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/OddCopyright/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/OddCopyright/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/HoPo/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/HoPo/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/SlashFretStringText/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/SlashFretStringText/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/SoundAutomation/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/SoundAutomation/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/SoundAutomation/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/SoundAutomation/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Staff/hideLyrics")); - Assert.AreEqual(false, stylesheet.Raw["Staff/hideLyrics"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/DirectionGlyph/name")); - Assert.AreEqual("@musicalFont()", stylesheet.Raw["Global/Font/DirectionGlyph/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Fret/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/Fret/size"]).IsAlmostEqualTo(2.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Capo/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Capo/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/OddHeader/name")); - Assert.AreEqual("Times New Roman", stylesheet.Raw["Global/Font/OddHeader/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/AlternateEnding/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/AlternateEnding/size"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Artist/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/Artist/size"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/BendsLabel/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/BendsLabel/size"]).IsAlmostEqualTo(2.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/DirectionGlyph/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/DirectionGlyph/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/LetRing/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/LetRing/size"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/HarmonicText/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/HarmonicText/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/SegmentLine/name")); - Assert.AreEqual("@musicalFontText()", stylesheet.Raw["Global/Font/SegmentLine/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Header/DrawAlbum")); - Assert.AreEqual(true, stylesheet.Raw["Header/DrawAlbum"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/AlternateEnding/name")); - Assert.AreEqual("@musicalFontText()", stylesheet.Raw["Global/Font/AlternateEnding/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Subtitle/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Subtitle/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/TrackName/name")); - Assert.AreEqual("Arial", stylesheet.Raw["Global/Font/TrackName/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/WordsAndMusic/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/WordsAndMusic/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("bracketExtendMode")); - Assert.AreEqual(0, stylesheet.Raw["bracketExtendMode"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/FreeText/name")); - Assert.AreEqual("Arial", stylesheet.Raw["Global/Font/FreeText/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Big/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Big/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Header/PageNumber")); - Assert.AreEqual("%PAGE%/%PAGES%", stylesheet.Raw["Header/PageNumber"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Huge/name")); - Assert.AreEqual("Arial", stylesheet.Raw["Global/Font/Huge/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/HoPo/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/HoPo/size"]).IsAlmostEqualTo(2.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/OddHeader/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/OddHeader/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/HoPo/name")); - Assert.AreEqual("@musicalFontText()", stylesheet.Raw["Global/Font/HoPo/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/AlternateEnding/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/AlternateEnding/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/BendsLabel/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/BendsLabel/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Music/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Music/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/FreeText/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/FreeText/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Timer/name")); - Assert.AreEqual("Arial", stylesheet.Raw["Global/Font/Timer/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/SoundAutomation/name")); - Assert.AreEqual("@musicalFontText()", stylesheet.Raw["Global/Font/SoundAutomation/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Multirest/proportionnal")); - Assert.AreEqual(true, stylesheet.Raw["Global/Font/Multirest/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("System/ignoreAccidentalsForPlacement")); - Assert.AreEqual(true, stylesheet.Raw["System/ignoreAccidentalsForPlacement"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Tiny/name")); - Assert.AreEqual("Arial", stylesheet.Raw["Global/Font/Tiny/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("TablatureNotation/invertFingeringPlacement")); - Assert.AreEqual(true, stylesheet.Raw["TablatureNotation/invertFingeringPlacement"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Barre/bold")); - Assert.AreEqual(true, stylesheet.Raw["Global/Font/Barre/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Copyright/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Copyright/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/SmallFret/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/SmallFret/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/FreeText/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/FreeText/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Tuplet/italic")); - Assert.AreEqual(true, stylesheet.Raw["Global/Font/Tuplet/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Artist/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Artist/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/SoundAutomation/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/SoundAutomation/size"]).IsAlmostEqualTo(2.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Words/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/Words/size"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/OddFooter/name")); - Assert.AreEqual("Times New Roman", stylesheet.Raw["Global/Font/OddFooter/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Barre/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Barre/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/SegmentLine/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/SegmentLine/size"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Timer/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Timer/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/RepeatBarCount/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/RepeatBarCount/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Tabber/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/Tabber/size"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Small/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Small/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/EvenFooter/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/EvenFooter/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/SlashFretString/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/SlashFretString/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Multirest/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/Multirest/size"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/WordsAndMusic/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/WordsAndMusic/size"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Rasgueado/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Rasgueado/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/AutoLetRing/size")); - Assert.AreEqual("8", stylesheet.Raw["Global/Font/AutoLetRing/size"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Normal/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Normal/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Copyright2/name")); - Assert.AreEqual("@musicalFontText()", stylesheet.Raw["Global/Font/Copyright2/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/HarmonicText/name")); - Assert.AreEqual("@musicalFontText()", stylesheet.Raw["Global/Font/HarmonicText/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/RepeatBarCount/bold")); - Assert.AreEqual(true, stylesheet.Raw["Global/Font/RepeatBarCount/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("System/strictPlacementForMultivoices")); - Assert.AreEqual(false, stylesheet.Raw["System/strictPlacementForMultivoices"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Multirest/name")); - Assert.AreEqual("@musicalFont()", stylesheet.Raw["Global/Font/Multirest/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/OddFooter/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/OddFooter/size"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Fingerings/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Fingerings/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Fingerings/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/Fingerings/size"]).IsAlmostEqualTo(2.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/AutoLetRing/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/AutoLetRing/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Slap/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Slap/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Music/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Music/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Fret/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Fret/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Copyright/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Copyright/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Pop/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Pop/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/firstSystemMinimalMarginMillimeter")); - Assert.AreEqual(20, stylesheet.Raw["Global/firstSystemMinimalMarginMillimeter"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Timer/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/Timer/size"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Tabber/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Tabber/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Music/name")); - Assert.AreEqual("Times New Roman", stylesheet.Raw["Global/Font/Music/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/AlternateEnding/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/AlternateEnding/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Pop/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Pop/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Huge/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Huge/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/BigBold/name")); - Assert.AreEqual("Arial", stylesheet.Raw["Global/Font/BigBold/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/VerticalTrackName/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/VerticalTrackName/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/OddCopyright/name")); - Assert.AreEqual("Times New Roman", stylesheet.Raw["Global/Font/OddCopyright/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/EvenFooter/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/EvenFooter/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/RepeatBarCount/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/RepeatBarCount/size"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/SegmentLine/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/SegmentLine/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/EvenHeader/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/EvenHeader/size"]).IsAlmostEqualTo(2.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/DirectionGlyph/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/DirectionGlyph/size"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/SlashFretString/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/SlashFretString/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Chord/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/Chord/size"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Chord/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Chord/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Words/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Words/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Subtitle/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Subtitle/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/SlashFretStringText/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/SlashFretStringText/size"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/HorizontalTrackName/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/HorizontalTrackName/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/BigBold/bold")); - Assert.AreEqual(true, stylesheet.Raw["Global/Font/BigBold/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Tabber/name")); - Assert.AreEqual("Times New Roman", stylesheet.Raw["Global/Font/Tabber/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/SmallFret/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/SmallFret/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/HorizontalTrackName/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/HorizontalTrackName/size"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/EvenCopyright/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/EvenCopyright/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/FreeTime/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/FreeTime/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/DirectionGlyph/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/DirectionGlyph/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Header/Copyright1")); - Assert.AreEqual("%COPYRIGHT1%", stylesheet.Raw["Header/Copyright1"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Huge/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/Huge/size"]).IsAlmostEqualTo(19.199710000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/EvenHeader/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/EvenHeader/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Header/DrawTabber")); - Assert.AreEqual(true, stylesheet.Raw["Header/DrawTabber"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Header/DrawPageNumber")); - Assert.AreEqual(true, stylesheet.Raw["Header/DrawPageNumber"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Slide/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Slide/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/VerticalTrackName/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/VerticalTrackName/size"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Lyric/name")); - Assert.AreEqual("Times New Roman", stylesheet.Raw["Global/Font/Lyric/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Fret/name")); - Assert.AreEqual("Arial", stylesheet.Raw["Global/Font/Fret/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Header/DrawArtist")); - Assert.AreEqual(true, stylesheet.Raw["Header/DrawArtist"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/LetRing/italic")); - Assert.AreEqual(true, stylesheet.Raw["Global/Font/LetRing/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Slap/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/Slap/size"]).IsAlmostEqualTo(4.800049000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Slide/italic")); - Assert.AreEqual(true, stylesheet.Raw["Global/Font/Slide/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/RepeatBarCount/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/RepeatBarCount/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Rasgueado/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/Rasgueado/size"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Section/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/Section/size"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Copyright2/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/Copyright2/size"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Header/DrawMusic")); - Assert.AreEqual(true, stylesheet.Raw["Header/DrawMusic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/EvenCopyright/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/EvenCopyright/size"]).IsAlmostEqualTo(2.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Tempo/name")); - Assert.AreEqual("Times New Roman", stylesheet.Raw["Global/Font/Tempo/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/HorizontalTrackName/name")); - Assert.AreEqual("Times New Roman", stylesheet.Raw["Global/Font/HorizontalTrackName/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/TrackName/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/TrackName/size"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/SegmentLine/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/SegmentLine/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/FreeTime/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/FreeTime/size"]).IsAlmostEqualTo(2.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/FreeText/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/FreeText/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/FreeTime/name")); - Assert.AreEqual("@musicalFontText()", stylesheet.Raw["Global/Font/FreeTime/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Tempo/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/Tempo/size"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/HoPo/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/HoPo/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Normal/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Normal/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/SegmentLine/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/SegmentLine/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/BendsLabel/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/BendsLabel/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/DirectionText/name")); - Assert.AreEqual("@musicalFontText()", stylesheet.Raw["Global/Font/DirectionText/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/PalmMute/bold")); - Assert.AreEqual(true, stylesheet.Raw["Global/Font/PalmMute/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/FreeTime/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/FreeTime/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/HorizontalTrackName/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/HorizontalTrackName/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Header/PageNumberAlignment")); - Assert.AreEqual(2, stylesheet.Raw["Header/PageNumberAlignment"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Pop/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Pop/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Words/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Words/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Copyright1/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Copyright1/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/BendsLabel/name")); - Assert.AreEqual("@musicalFontText()", stylesheet.Raw["Global/Font/BendsLabel/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Capo/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/Capo/size"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/OddCopyright/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/OddCopyright/size"]).IsAlmostEqualTo(2.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Title/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/Title/size"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/LetRing/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/LetRing/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Tabber/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Tabber/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Capo/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Capo/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Pop/name")); - Assert.AreEqual("@musicalFontText()", stylesheet.Raw["Global/Font/Pop/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/HoPo/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/HoPo/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/EvenFooter/name")); - Assert.AreEqual("Times New Roman", stylesheet.Raw["Global/Font/EvenFooter/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Subtitle/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/Subtitle/size"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Header/DrawSubtitle")); - Assert.AreEqual(true, stylesheet.Raw["Header/DrawSubtitle"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/VerticalTrackName/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/VerticalTrackName/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/TrackShortName/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/TrackShortName/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Tuning/name")); - Assert.AreEqual("Times New Roman", stylesheet.Raw["Global/Font/Tuning/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Section/bold")); - Assert.AreEqual(true, stylesheet.Raw["Global/Font/Section/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Artist/bold")); - Assert.AreEqual(true, stylesheet.Raw["Global/Font/Artist/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Section/name")); - Assert.AreEqual("Times New Roman", stylesheet.Raw["Global/Font/Section/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Normal/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/Normal/size"]).IsAlmostEqualTo(2.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Barre/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/Barre/size"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Slap/name")); - Assert.AreEqual("@musicalFontText()", stylesheet.Raw["Global/Font/Slap/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Copyright1/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Copyright1/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Lyric/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Lyric/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/PageNumber/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/PageNumber/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/DirectionText/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/DirectionText/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/EvenCopyright/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/EvenCopyright/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/HarmonicText/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/HarmonicText/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Subtitle/name")); - Assert.AreEqual("Times New Roman", stylesheet.Raw["Global/Font/Subtitle/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Big/name")); - Assert.AreEqual("Arial", stylesheet.Raw["Global/Font/Big/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/PageNumber/name")); - Assert.AreEqual("Times New Roman", stylesheet.Raw["Global/Font/PageNumber/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/BigBold/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/BigBold/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/OddHeader/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/OddHeader/size"]).IsAlmostEqualTo(2.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/OddCopyright/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/OddCopyright/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Timer/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Timer/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Title/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Title/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Timer/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Timer/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/PalmMute/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/PalmMute/size"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/EvenCopyright/name")); - Assert.AreEqual("Times New Roman", stylesheet.Raw["Global/Font/EvenCopyright/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/firstSystemMaximalMarginMillimeter")); - Assert.AreEqual(100, stylesheet.Raw["Global/firstSystemMaximalMarginMillimeter"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/AlternateEnding/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/AlternateEnding/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/AutoLetRing/italic")); - Assert.AreEqual(true, stylesheet.Raw["Global/Font/AutoLetRing/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Copyright/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/Copyright/size"]).IsAlmostEqualTo(2.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Tuplet/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/Tuplet/size"]).IsAlmostEqualTo(8.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Copyright1/name")); - Assert.AreEqual("@musicalFontText()", stylesheet.Raw["Global/Font/Copyright1/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/BarIndex/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/BarIndex/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Header/DrawTitle")); - Assert.AreEqual(true, stylesheet.Raw["Header/DrawTitle"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Pop/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/Pop/size"]).IsAlmostEqualTo(4.800049000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Small/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Small/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Slap/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Slap/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/SlashFretStringText/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/SlashFretStringText/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Rasgueado/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Rasgueado/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/PageNumber/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/PageNumber/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Staff/drawLyricsUpside")); - Assert.AreEqual(false, stylesheet.Raw["Staff/drawLyricsUpside"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/OddFooter/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/OddFooter/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/SlashFretStringText/name")); - Assert.AreEqual("Arial", stylesheet.Raw["Global/Font/SlashFretStringText/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/SoundAutomation/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/SoundAutomation/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Multirest/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Multirest/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Title/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Title/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/BendsLabel/proportionnal")); - Assert.AreEqual(true, stylesheet.Raw["Global/Font/BendsLabel/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/TrackName/bold")); - Assert.AreEqual(true, stylesheet.Raw["Global/Font/TrackName/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Lyric/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/Lyric/size"]).IsAlmostEqualTo(2.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Artist/name")); - Assert.AreEqual("Times New Roman", stylesheet.Raw["Global/Font/Artist/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Tuplet/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Tuplet/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/SmallFret/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/SmallFret/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/SlashFretString/name")); - Assert.AreEqual("@musicalFont()", stylesheet.Raw["Global/Font/SlashFretString/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/VerticalTrackName/name")); - Assert.AreEqual("Times New Roman", stylesheet.Raw["Global/Font/VerticalTrackName/name"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Album/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Album/italic"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Slap/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/Slap/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/Small/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/Small/size"]).IsAlmostEqualTo(4.799927000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/TrackShortName/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/TrackShortName/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/SlashFretStringText/bold")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/SlashFretStringText/bold"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/FreeText/size")); - Assert.IsTrue(((float)stylesheet.Raw["Global/Font/FreeText/size"]).IsAlmostEqualTo(2.000000000f)); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/WordsAndMusic/proportionnal")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/WordsAndMusic/proportionnal"]); - - Assert.IsTrue(stylesheet.Raw.ContainsKey("Global/Font/EvenHeader/italic")); - Assert.AreEqual(false, stylesheet.Raw["Global/Font/EvenHeader/italic"]); - } - } -} diff --git a/Source/AlphaTab.Test/Importer/Gp3ImporterTest.cs b/Source/AlphaTab.Test/Importer/Gp3ImporterTest.cs deleted file mode 100644 index bcdc16f15..000000000 --- a/Source/AlphaTab.Test/Importer/Gp3ImporterTest.cs +++ /dev/null @@ -1,214 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Model; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace AlphaTab.Test.Importer -{ - [TestClass] - public class Gp3ImporterTest : GpImporterTestBase - { - [TestMethod] - public void TestScoreInfo() - { - var reader = PrepareImporterWithFile("GuitarPro3/Test01.gp3"); - var score = reader.ReadScore(); - - Assert.AreEqual("Title", score.Title); - Assert.AreEqual("Subtitle", score.SubTitle); - Assert.AreEqual("Artist", score.Artist); - Assert.AreEqual("Album", score.Album); - Assert.AreEqual("Music", score.Words); // no words in gp4 - Assert.AreEqual("Music", score.Music); - Assert.AreEqual("Copyright", score.Copyright); - Assert.AreEqual("Tab", score.Tab); - Assert.AreEqual("Instructions", score.Instructions); - Assert.AreEqual("Notice1\r\nNotice2", score.Notices); - Assert.AreEqual(5, score.MasterBars.Count); - Assert.AreEqual(1, score.Tracks.Count); - Assert.AreEqual("Track 1", score.Tracks[0].Name); - - Render(score); - } - - [TestMethod] - public void TestNotes() - { - var reader = PrepareImporterWithFile("GuitarPro3/Test02.gp3"); - var score = reader.ReadScore(); - CheckTest02Score(score); - Render(score); - } - - [TestMethod] - public void TestTimeSignatures() - { - var reader = PrepareImporterWithFile("GuitarPro3/Test03.gp3"); - var score = reader.ReadScore(); - - CheckTest03Score(score); - Render(score); - } - - [TestMethod] - public void TestDead() - { - var reader = PrepareImporterWithFile("GuitarPro3/TestDead.gp3"); - var score = reader.ReadScore(); - CheckDead(score); - Render(score); - } - - [TestMethod] - public void TestAccentuation() - { - var reader = PrepareImporterWithFile("GuitarPro3/TestAccentuations.gp3"); - var score = reader.ReadScore(); - - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].IsGhost); - // it seems accentuation is handled as Forte Fortissimo - Assert.AreEqual(DynamicValue.FFF, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].Dynamic); - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[3].Notes[0].IsLetRing); - Render(score); - } - - [TestMethod] - public void TestGuitarPro3Harmonics() - { - // TODO: Find out about GP3 harmonics! - //var reader = PrepareImporterWithFile("GuitarPro3/TestHarmonics.gp3"); - //var score = reader.ReadScore(); - - //Assert.AreEqual(HarmonicType.Natural, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].HarmonicType); - //Assert.AreEqual(HarmonicType.Artificial, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].HarmonicType); - } - - [TestMethod] - public void TestHammer() - { - var reader = PrepareImporterWithFile("GuitarPro3/TestHammer.gp3"); - var score = reader.ReadScore(); - CheckHammer(score); - Render(score); - } - - [TestMethod] - public void TestBend() - { - var reader = PrepareImporterWithFile("GuitarPro3/TestBends.gp3"); - var score = reader.ReadScore(); - CheckBend(score); - Render(score); - } - - [TestMethod] - public void TestSlides() - { - var reader = PrepareImporterWithFile("GuitarPro3/TestSlides.gp3"); - var score = reader.ReadScore(); - - Assert.AreEqual(SlideType.Shift, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].GetNoteOnString(5).SlideType); - Assert.AreEqual(SlideType.Shift, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[2].GetNoteOnString(2).SlideType); - Render(score); - } - - [TestMethod] - public void TestGuitarPro3Vibrato() - { - // TODO: Check why this vibrato is not recognized - var reader = PrepareImporterWithFile("GuitarPro3/TestVibrato.gp3"); - var score = reader.ReadScore(); - CheckVibrato(score, false); - Render(score); - } - - [TestMethod] - public void TestOtherEffects() - { - var reader = PrepareImporterWithFile("GuitarPro3/TestOtherEffects.gp3"); - var score = reader.ReadScore(); - - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[2].Tap); - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[3].Slap); - - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Pop); - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[1].FadeIn); - - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[0].HasChord); - Assert.AreEqual("C", score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[0].Chord.Name); - Assert.AreEqual("Text", score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[1].Text); - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[4].Voices[0].Beats[0].GetAutomation(AutomationType.Tempo) != null); - Assert.AreEqual(120.0, score.Tracks[0].Staves[0].Bars[4].Voices[0].Beats[0].GetAutomation(AutomationType.Tempo).Value); - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[4].Voices[0].Beats[0].GetAutomation(AutomationType.Instrument) != null); - Assert.AreEqual(25.0, score.Tracks[0].Staves[0].Bars[4].Voices[0].Beats[0].GetAutomation(AutomationType.Instrument).Value); - Render(score); - } - - [TestMethod] - public void TestStroke() - { - var reader = PrepareImporterWithFile("GuitarPro3/TestStrokes.gp3"); - var score = reader.ReadScore(); - - Assert.AreEqual(BrushType.BrushDown, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].BrushType); - Assert.AreEqual(BrushType.BrushUp, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].BrushType); - Render(score); - } - - [TestMethod] - public void TestTuplets() - { - var reader = PrepareImporterWithFile("GuitarPro3/TestTuplets.gp3"); - var score = reader.ReadScore(); - CheckTuplets(score); - Render(score); - } - - [TestMethod] - public void TestRanges() - { - var reader = PrepareImporterWithFile("GuitarPro3/TestRanges.gp3"); - var score = reader.ReadScore(); - - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[1].Notes[0].IsLetRing); - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[2].Notes[0].IsLetRing); - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[3].Notes[0].IsLetRing); - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[0].Notes[0].IsLetRing); - Render(score); - } - - [TestMethod] - public void TestEffects() - { - var reader = PrepareImporterWithFile("GuitarPro3/Effects.gp3"); - var score = reader.ReadScore(); - CheckEffects(score); - Render(score); - } - - [TestMethod] - public void TestStrings() - { - var reader = PrepareImporterWithFile("GuitarPro3/TestStrings.gp3"); - var score = reader.ReadScore(); - CheckStrings(score); - Render(score); - } - } -} diff --git a/Source/AlphaTab.Test/Importer/Gp4ImporterTest.cs b/Source/AlphaTab.Test/Importer/Gp4ImporterTest.cs deleted file mode 100644 index 67cb046c7..000000000 --- a/Source/AlphaTab.Test/Importer/Gp4ImporterTest.cs +++ /dev/null @@ -1,230 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace AlphaTab.Test.Importer -{ - [TestClass] - public class Gp4ImporterTest : GpImporterTestBase - { - [TestMethod] - public void TestScoreInfo() - { - var reader = PrepareImporterWithFile("GuitarPro4/Test01.gp4"); - var score = reader.ReadScore(); - - Assert.AreEqual("Title", score.Title); - Assert.AreEqual("Subtitle", score.SubTitle); - Assert.AreEqual("Artist", score.Artist); - Assert.AreEqual("Album", score.Album); - Assert.AreEqual("Music", score.Words); // no words in gp4 - Assert.AreEqual("Music", score.Music); - Assert.AreEqual("Copyright", score.Copyright); - Assert.AreEqual("Tab", score.Tab); - Assert.AreEqual("Instructions", score.Instructions); - Assert.AreEqual("Notice1\r\nNotice2", score.Notices); - Assert.AreEqual(5, score.MasterBars.Count); - Assert.AreEqual(1, score.Tracks.Count); - Assert.AreEqual("Track 1", score.Tracks[0].Name); - Render(score); - } - - [TestMethod] - public void TestNotes() - { - var reader = PrepareImporterWithFile("GuitarPro4/Test02.gp4"); - var score = reader.ReadScore(); - CheckTest02Score(score); - Render(score); - } - - [TestMethod] - public void TestTimeSignatures() - { - var reader = PrepareImporterWithFile("GuitarPro4/Test03.gp4"); - var score = reader.ReadScore(); - - CheckTest03Score(score); - Render(score); - } - - [TestMethod] - public void TestDead() - { - var reader = PrepareImporterWithFile("GuitarPro4/TestDead.gp4"); - var score = reader.ReadScore(); - CheckDead(score); - Render(score); - } - - [TestMethod] - public void TestGrace() - { - var reader = PrepareImporterWithFile("GuitarPro4/TestGrace.gp4"); - var score = reader.ReadScore(); - CheckGrace(score); - Render(score); - } - - [TestMethod] - public void TestAccentuation() - { - var reader = PrepareImporterWithFile("GuitarPro4/TestAccentuations.gp4"); - var score = reader.ReadScore(); - CheckAccentuation(score, false); - Render(score); - } - - [TestMethod] - public void TestHarmonics() - { - var reader = PrepareImporterWithFile("GuitarPro4/TestHarmonics.gp4"); - var score = reader.ReadScore(); - CheckHarmonics(score); - Render(score); - } - - [TestMethod] - public void TestHammer() - { - var reader = PrepareImporterWithFile("GuitarPro4/TestHammer.gp4"); - var score = reader.ReadScore(); - CheckHammer(score); - Render(score); - } - - [TestMethod] - public void TestBend() - { - var reader = PrepareImporterWithFile("GuitarPro4/TestBends.gp4"); - var score = reader.ReadScore(); - CheckBend(score); - Render(score); - } - - [TestMethod] - public void TestTremolo() - { - var reader = PrepareImporterWithFile("GuitarPro4/TestTremolo.gp4"); - var score = reader.ReadScore(); - CheckTremolo(score); - Render(score); - } - - [TestMethod] - public void TestSlides() - { - var reader = PrepareImporterWithFile("GuitarPro4/TestSlides.gp4"); - var score = reader.ReadScore(); - CheckSlides(score); - Render(score); - } - - [TestMethod] - public void TestVibrato() - { - var reader = PrepareImporterWithFile("GuitarPro4/TestVibrato.gp4"); - var score = reader.ReadScore(); - CheckVibrato(score, true); - Render(score); - } - - [TestMethod] - public void TestTrills() - { - var reader = PrepareImporterWithFile("GuitarPro4/TestTrills.gp4"); - var score = reader.ReadScore(); - CheckTrills(score); - Render(score); - } - - [TestMethod] - public void TestOtherEffects() - { - var reader = PrepareImporterWithFile("GuitarPro4/TestOtherEffects.gp4"); - var score = reader.ReadScore(); - CheckOtherEffects(score); - Render(score); - } - - [TestMethod] - public void TestFingering() - { - var reader = PrepareImporterWithFile("GuitarPro4/TestFingering.gp4"); - var score = reader.ReadScore(); - CheckFingering(score); - Render(score); - } - - [TestMethod] - public void TestStroke() - { - var reader = PrepareImporterWithFile("GuitarPro4/TestStrokes.gp4"); - var score = reader.ReadScore(); - CheckStroke(score); - Render(score); - } - - [TestMethod] - public void TestTuplets() - { - var reader = PrepareImporterWithFile("GuitarPro4/TestTuplets.gp4"); - var score = reader.ReadScore(); - CheckTuplets(score); - Render(score); - } - - [TestMethod] - public void TestRanges() - { - var reader = PrepareImporterWithFile("GuitarPro4/TestRanges.gp4"); - var score = reader.ReadScore(); - CheckRanges(score); - Render(score); - } - - [TestMethod] - public void TestEffects() - { - var reader = PrepareImporterWithFile("GuitarPro4/Effects.gp4"); - var score = reader.ReadScore(); - CheckEffects(score); - Render(score); - } - - [TestMethod] - public void TestStrings() - { - var reader = PrepareImporterWithFile("GuitarPro4/TestStrings.gp4"); - var score = reader.ReadScore(); - CheckStrings(score); - Render(score); - } - - [TestMethod] - public void TestColors() - { - var reader = PrepareImporterWithFile("GuitarPro4/Colors.gp4"); - var score = reader.ReadScore(); - - CheckColors(score); - Render(score); - } - - } -} diff --git a/Source/AlphaTab.Test/Importer/Gp5ImporterTest.cs b/Source/AlphaTab.Test/Importer/Gp5ImporterTest.cs deleted file mode 100644 index fdc7a7bde..000000000 --- a/Source/AlphaTab.Test/Importer/Gp5ImporterTest.cs +++ /dev/null @@ -1,286 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace AlphaTab.Test.Importer -{ - [TestClass] - public class Gp5ImporterTest : GpImporterTestBase - { - [TestMethod] - public void TestScoreInfo() - { - var reader = PrepareImporterWithFile("GuitarPro5/Test01.gp5"); - var score = reader.ReadScore(); - - Assert.AreEqual("Title", score.Title); - Assert.AreEqual("Subtitle", score.SubTitle); - Assert.AreEqual("Artist", score.Artist); - Assert.AreEqual("Album", score.Album); - Assert.AreEqual("Words", score.Words); - Assert.AreEqual("Music", score.Music); - Assert.AreEqual("Copyright", score.Copyright); - Assert.AreEqual("Tab", score.Tab); - Assert.AreEqual("Instructions", score.Instructions); - Assert.AreEqual("Notice1\r\nNotice2", score.Notices); - Assert.AreEqual(5, score.MasterBars.Count); - Assert.AreEqual(2, score.Tracks.Count); - Assert.AreEqual("Track 1", score.Tracks[0].Name); - Assert.AreEqual("Track 2", score.Tracks[1].Name); - Render(score); - } - - [TestMethod] - public void TestNotes() - { - var reader = PrepareImporterWithFile("GuitarPro5/Test02.gp5"); - var score = reader.ReadScore(); - CheckTest02Score(score); - Render(score); - } - - [TestMethod] - public void TestTimeSignatures() - { - var reader = PrepareImporterWithFile("GuitarPro5/Test03.gp5"); - var score = reader.ReadScore(); - - CheckTest03Score(score); - Render(score); - } - - [TestMethod] - public void TestDead() - { - var reader = PrepareImporterWithFile("GuitarPro5/TestDead.gp5"); - var score = reader.ReadScore(); - CheckDead(score); - Render(score); - } - - [TestMethod] - public void TestGrace() - { - var reader = PrepareImporterWithFile("GuitarPro5/TestGrace.gp5"); - var score = reader.ReadScore(); - CheckGrace(score); - Render(score); - } - - [TestMethod] - public void TestAccentuation() - { - var reader = PrepareImporterWithFile("GuitarPro5/TestAccentuations.gp5"); - var score = reader.ReadScore(); - CheckAccentuation(score, true); - Render(score); - } - - [TestMethod] - public void TestHarmonics() - { - var reader = PrepareImporterWithFile("GuitarPro5/TestHarmonics.gp5"); - var score = reader.ReadScore(); - CheckHarmonics(score); - Render(score); - } - - [TestMethod] - public void TestHammer() - { - var reader = PrepareImporterWithFile("GuitarPro5/TestHammer.gp5"); - var score = reader.ReadScore(); - CheckHammer(score); - Render(score); - } - - [TestMethod] - public void TestBend() - { - var reader = PrepareImporterWithFile("GuitarPro5/TestBends.gp5"); - var score = reader.ReadScore(); - CheckBend(score); - Render(score); - } - - [TestMethod] - public void TestTremolo() - { - var reader = PrepareImporterWithFile("GuitarPro5/TestTremolo.gp5"); - var score = reader.ReadScore(); - CheckTremolo(score); - Render(score); - } - - [TestMethod] - public void TestSlides() - { - var reader = PrepareImporterWithFile("GuitarPro5/TestSlides.gp5"); - var score = reader.ReadScore(); - CheckSlides(score); - Render(score); - } - - [TestMethod] - public void TestVibrato() - { - var reader = PrepareImporterWithFile("GuitarPro5/TestVibrato.gp5"); - var score = reader.ReadScore(); - CheckVibrato(score, true); - Render(score); - } - - [TestMethod] - public void TestTrills() - { - var reader = PrepareImporterWithFile("GuitarPro5/TestTrills.gp5"); - var score = reader.ReadScore(); - CheckTrills(score); - Render(score); - } - - [TestMethod] - public void TestOtherEffects() - { - var reader = PrepareImporterWithFile("GuitarPro5/TestOtherEffects.gp5"); - var score = reader.ReadScore(); - CheckOtherEffects(score); - Render(score); - } - - [TestMethod] - public void TestFingering() - { - var reader = PrepareImporterWithFile("GuitarPro5/TestFingering.gp5"); - var score = reader.ReadScore(); - CheckFingering(score); - Render(score); - } - - [TestMethod] - public void TestStroke() - { - var reader = PrepareImporterWithFile("GuitarPro5/TestStrokes.gp5"); - var score = reader.ReadScore(); - CheckStroke(score); - Render(score); - } - - [TestMethod] - public void TestTuplets() - { - var reader = PrepareImporterWithFile("GuitarPro5/TestTuplets.gp5"); - var score = reader.ReadScore(); - CheckTuplets(score); - Render(score); - } - - [TestMethod] - public void TestRanges() - { - var reader = PrepareImporterWithFile("GuitarPro5/TestRanges.gp5"); - var score = reader.ReadScore(); - CheckRanges(score); - Render(score); - } - - [TestMethod] - public void TestEffects() - { - var reader = PrepareImporterWithFile("GuitarPro5/Effects.gp5"); - var score = reader.ReadScore(); - CheckEffects(score); - Render(score); - } - - [TestMethod] - public void TestSerenade() - { - var reader = PrepareImporterWithFile("GuitarPro5/Serenade.gp5"); - var score = reader.ReadScore();// only Check reading - Render(score); - } - - [TestMethod] - public void TestStrings() - { - var reader = PrepareImporterWithFile("GuitarPro5/TestStrings.gp5"); - var score = reader.ReadScore(); - CheckStrings(score); - Render(score); - } - - [TestMethod] - public void TestKeySignatures() - { - var reader = PrepareImporterWithFile("GuitarPro5/TestKeySignatures.gp5"); - var score = reader.ReadScore(); - CheckKeySignatures(score); - Render(score); - } - - [TestMethod] - public void TestChords() - { - var reader = PrepareImporterWithFile("GuitarPro5/TestChords.gp5"); - var score = reader.ReadScore(); - - CheckChords(score); - Render(score); - } - - [TestMethod] - public void TestColors() - { - var reader = PrepareImporterWithFile("GuitarPro5/Colors.gp5"); - var score = reader.ReadScore(); - - CheckColors(score); - Render(score); - } - - [TestMethod] - public void TestCanon() - { - var reader = PrepareImporterWithFile("GuitarPro5/Canon.gp5"); - var score = reader.ReadScore(); - - Assert.AreEqual("Canon Rock", score.Title); - Assert.AreEqual("", score.SubTitle); - Assert.AreEqual("JerryC", score.Artist); - Assert.AreEqual("", score.Album); - Assert.AreEqual("", score.Words); - Assert.AreEqual("JerryC", score.Music); - Assert.AreEqual("", score.Copyright); - Assert.AreEqual("", score.Tab); - Assert.AreEqual("", score.Instructions); - Assert.AreEqual("", score.Notices); - Assert.AreEqual(224, score.MasterBars.Count); - Assert.AreEqual(9, score.Tracks.Count); - Assert.AreEqual("Guitar Player", score.Tracks[0].Name); - Assert.AreEqual("Low Bassy Sound", score.Tracks[1].Name); - Assert.AreEqual("High Soundy Thing", score.Tracks[2].Name); - Assert.AreEqual("Second Guitar", score.Tracks[3].Name); - Assert.AreEqual("Drums", score.Tracks[4].Name); - Assert.AreEqual("Harmonizer", score.Tracks[5].Name); - Assert.AreEqual("The clean guitar", score.Tracks[6].Name); - Assert.AreEqual("Track 8", score.Tracks[7].Name); - Assert.AreEqual("Percussion", score.Tracks[8].Name); - } - } -} diff --git a/Source/AlphaTab.Test/Importer/Gp7ImporterTest.cs b/Source/AlphaTab.Test/Importer/Gp7ImporterTest.cs deleted file mode 100644 index 7763bb454..000000000 --- a/Source/AlphaTab.Test/Importer/Gp7ImporterTest.cs +++ /dev/null @@ -1,1108 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using System.IO; -using System.Linq; -using AlphaTab.Audio; -using AlphaTab.Importer; -using AlphaTab.IO; -using AlphaTab.Model; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace AlphaTab.Test.Importer -{ - [TestClass] - public class Gp7ImporterTest : GpImporterTestBase - { - internal byte[] Load(string name) - { - const string path = "TestFiles/"; - return TestPlatform.LoadFile(path + name); - } - - internal Gp7Importer PrepareGp7ImporterWithFile(string name) - { - return PrepareGp7ImporterWithBytes(Load(name)); - } - - internal Gp7Importer PrepareGp7ImporterWithBytes(byte[] buffer) - { - var readerBase = new Gp7Importer(); - readerBase.Init(ByteBuffer.FromBuffer(buffer)); - return readerBase; - } - - [TestMethod] - public void TestScoreInfo() - { - var reader = PrepareGp7ImporterWithFile("GuitarPro7/Test01.gp"); - var score = reader.ReadScore(); - - Assert.AreEqual("Title", score.Title); - Assert.AreEqual("Subtitle", score.SubTitle); - Assert.AreEqual("Artist", score.Artist); - Assert.AreEqual("Album", score.Album); - Assert.AreEqual("Words", score.Words); - Assert.AreEqual("Music", score.Music); - Assert.AreEqual("Copyright", score.Copyright); - Assert.AreEqual("Tab", score.Tab); - Assert.AreEqual("Instructions", score.Instructions); - Assert.AreEqual("Notice1\nNotice2", score.Notices); - Assert.AreEqual(5, score.MasterBars.Count); - Assert.AreEqual(2, score.Tracks.Count); - Assert.AreEqual("Track 1", score.Tracks[0].Name); - Assert.AreEqual("Track 2", score.Tracks[1].Name); - Render(score); - } - - [TestMethod] - public void TestNotes() - { - var reader = PrepareGp7ImporterWithFile("GuitarPro7/Test02.gp"); - var score = reader.ReadScore(); - CheckTest02Score(score); - Render(score); - } - - [TestMethod] - public void TestTimeSignatures() - { - var reader = PrepareGp7ImporterWithFile("GuitarPro7/Test03.gp"); - var score = reader.ReadScore(); - - CheckTest03Score(score); - Render(score); - } - - [TestMethod] - public void TestDead() - { - var reader = PrepareGp7ImporterWithFile("GuitarPro7/TestDead.gp"); - var score = reader.ReadScore(); - CheckDead(score); - Render(score); - } - - [TestMethod] - public void TestGrace() - { - var reader = PrepareGp7ImporterWithFile("GuitarPro7/TestGrace.gp"); - var score = reader.ReadScore(); - CheckGrace(score); - Render(score); - } - - [TestMethod] - public void TestAccentuation() - { - var reader = PrepareGp7ImporterWithFile("GuitarPro7/TestAccentuations.gp"); - var score = reader.ReadScore(); - CheckAccentuation(score, true); - Render(score); - } - - [TestMethod] - public void TestHarmonics() - { - var reader = PrepareGp7ImporterWithFile("GuitarPro7/TestHarmonics.gp"); - var score = reader.ReadScore(); - CheckHarmonics(score); - Render(score); - } - - [TestMethod] - public void TestHammer() - { - var reader = PrepareGp7ImporterWithFile("GuitarPro7/TestHammer.gp"); - var score = reader.ReadScore(); - CheckHammer(score); - Render(score); - } - - [TestMethod] - public void TestNumber() - { - Assert.AreEqual(4, Convert(100)); - } - - private const float ConversionFactor = 1 / 25f; - private int Convert(float value) - { - var converted = value * ConversionFactor; - return (int)(converted); - } - - [TestMethod] - public void TestBend() - { - var reader = PrepareGp7ImporterWithFile("GuitarPro7/TestBends.gp"); - var score = reader.ReadScore(); - - Assert.AreEqual(BendType.Bend, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].BendType); - Assert.AreEqual(2, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].BendPoints.Count); - - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].BendPoints[0].Offset); - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].BendPoints[0].Value); - - Assert.AreEqual(60, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].BendPoints[1].Offset); - Assert.AreEqual(4, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].BendPoints[1].Value); - - Assert.AreEqual(BendType.Bend, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].BendType); - Assert.AreEqual(2, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].BendPoints.Count); - - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].BendPoints[0].Offset); - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].BendPoints[0].Value); - - Assert.AreEqual(60, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].BendPoints[1].Offset); - Assert.AreEqual(4, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].BendPoints[1].Value); - - Assert.AreEqual(BendType.BendRelease, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Notes[0].BendType); - Assert.AreEqual(4, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Notes[0].BendPoints.Count); - - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Notes[0].BendPoints[0].Offset); - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Notes[0].BendPoints[0].Value); - - Assert.AreEqual(30, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Notes[0].BendPoints[1].Offset); - Assert.AreEqual(12, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Notes[0].BendPoints[1].Value); - - Assert.AreEqual(30, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Notes[0].BendPoints[2].Offset); - Assert.AreEqual(12, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Notes[0].BendPoints[2].Value); - - Assert.AreEqual(60, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Notes[0].BendPoints[3].Offset); - Assert.AreEqual(6, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Notes[0].BendPoints[3].Value); - Render(score); - } - - [TestMethod] - public void TestBendAdvanced() - { - var reader = PrepareGp7ImporterWithFile("GuitarPro7/BendsAdvanced.gp"); - var score = reader.ReadScore(); - - #region Simple Standalone Bends - - #region Bar 1 - - var note = score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0]; - Assert.AreEqual(BendType.Bend, note.BendType); - Assert.AreEqual(2, note.BendPoints.Count); - Assert.AreEqual(0, note.BendPoints[0].Offset); - Assert.AreEqual(0, note.BendPoints[0].Value); - Assert.AreEqual(15, note.BendPoints[1].Offset); - Assert.AreEqual(4, note.BendPoints[1].Value); - - note = score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0]; - Assert.AreEqual(BendType.BendRelease, note.BendType); - Assert.AreEqual(4, note.BendPoints.Count); - Assert.AreEqual(0, note.BendPoints[0].Offset); - Assert.AreEqual(0, note.BendPoints[0].Value); - Assert.AreEqual(10, note.BendPoints[1].Offset); - Assert.AreEqual(4, note.BendPoints[1].Value); - Assert.AreEqual(20, note.BendPoints[2].Offset); - Assert.AreEqual(4, note.BendPoints[2].Value); - Assert.AreEqual(30, note.BendPoints[3].Offset); - Assert.AreEqual(0, note.BendPoints[3].Value); - - #endregion - - #region Bar 2 - - note = score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Notes[0]; - Assert.AreEqual(BendType.Bend, note.BendType); - Assert.AreEqual(2, note.BendPoints.Count); - Assert.AreEqual(0, note.BendPoints[0].Offset); - Assert.AreEqual(0, note.BendPoints[0].Value); - Assert.AreEqual(59, note.BendPoints[1].Offset); - Assert.AreEqual(4, note.BendPoints[1].Value); - - note = score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[1].Notes[0]; - Assert.AreEqual(BendType.BendRelease, note.BendType); - Assert.AreEqual(4, note.BendPoints.Count); - Assert.AreEqual(0, note.BendPoints[0].Offset); - Assert.AreEqual(0, note.BendPoints[0].Value); - Assert.AreEqual(10, note.BendPoints[1].Offset); - Assert.AreEqual(4, note.BendPoints[1].Value); - Assert.AreEqual(45, note.BendPoints[2].Offset); - Assert.AreEqual(4, note.BendPoints[2].Value); - Assert.AreEqual(59, note.BendPoints[3].Offset); - Assert.AreEqual(0, note.BendPoints[3].Value); - - #endregion - - #region Bar 3 - - note = score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[0].Notes[0]; - Assert.AreEqual(BendType.Prebend, note.BendType); - Assert.AreEqual(2, note.BendPoints.Count); - Assert.AreEqual(0, note.BendPoints[0].Offset); - Assert.AreEqual(4, note.BendPoints[0].Value); - Assert.AreEqual(60, note.BendPoints[1].Offset); - Assert.AreEqual(4, note.BendPoints[1].Value); - - note = score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[1].Notes[0]; - Assert.AreEqual(BendType.PrebendBend, note.BendType); - Assert.AreEqual(2, note.BendPoints.Count); - Assert.AreEqual(0, note.BendPoints[0].Offset); - Assert.AreEqual(4, note.BendPoints[0].Value); - Assert.AreEqual(15, note.BendPoints[1].Offset); - Assert.AreEqual(6, note.BendPoints[1].Value); - - #endregion - - #region Bar 4 - - note = score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[0].Notes[0]; - Assert.AreEqual(BendType.PrebendRelease, note.BendType); - Assert.AreEqual(2, note.BendPoints.Count); - Assert.AreEqual(0, note.BendPoints[0].Offset); - Assert.AreEqual(4, note.BendPoints[0].Value); - Assert.AreEqual(15, note.BendPoints[1].Offset); - Assert.AreEqual(0, note.BendPoints[1].Value); - - #endregion - - #region Bar 5 - - note = score.Tracks[0].Staves[0].Bars[4].Voices[0].Beats[0].Notes[0]; - Assert.AreEqual(BendType.Bend, note.BendType); - Assert.AreEqual(2, note.BendPoints.Count); - Assert.AreEqual(0, note.BendPoints[0].Offset); - Assert.AreEqual(0, note.BendPoints[0].Value); - Assert.AreEqual(14, note.BendPoints[1].Offset); - Assert.AreEqual(8, note.BendPoints[1].Value); - - note = score.Tracks[0].Staves[0].Bars[4].Voices[0].Beats[1].Notes[0]; - Assert.AreEqual(BendType.BendRelease, note.BendType); - Assert.AreEqual(4, note.BendPoints.Count); - Assert.AreEqual(0, note.BendPoints[0].Offset); - Assert.AreEqual(0, note.BendPoints[0].Value); - Assert.AreEqual(9, note.BendPoints[1].Offset); - Assert.AreEqual(8, note.BendPoints[1].Value); - Assert.AreEqual(20, note.BendPoints[2].Offset); - Assert.AreEqual(8, note.BendPoints[2].Value); - Assert.AreEqual(31, note.BendPoints[3].Offset); - Assert.AreEqual(4, note.BendPoints[3].Value); - - #endregion - - #region Bar 6 - - note = score.Tracks[0].Staves[0].Bars[5].Voices[0].Beats[0].Notes[0]; - Assert.AreEqual(BendType.Prebend, note.BendType); - Assert.AreEqual(2, note.BendPoints.Count); - Assert.AreEqual(0, note.BendPoints[0].Offset); - Assert.AreEqual(8, note.BendPoints[0].Value); - Assert.AreEqual(60, note.BendPoints[1].Offset); - Assert.AreEqual(8, note.BendPoints[1].Value); - - note = score.Tracks[0].Staves[0].Bars[5].Voices[0].Beats[1].Notes[0]; - Assert.AreEqual(BendType.PrebendBend, note.BendType); - Assert.AreEqual(2, note.BendPoints.Count); - Assert.AreEqual(0, note.BendPoints[0].Offset); - Assert.AreEqual(8, note.BendPoints[0].Value); - Assert.AreEqual(16, note.BendPoints[1].Offset); - Assert.AreEqual(12, note.BendPoints[1].Value); - - #endregion - - #region Bar 7 - - note = score.Tracks[0].Staves[0].Bars[6].Voices[0].Beats[0].Notes[0]; - Assert.AreEqual(BendType.PrebendRelease, note.BendType); - Assert.AreEqual(2, note.BendPoints.Count); - Assert.AreEqual(0, note.BendPoints[0].Offset); - Assert.AreEqual(8, note.BendPoints[0].Value); - Assert.AreEqual(14, note.BendPoints[1].Offset); - Assert.AreEqual(4, note.BendPoints[1].Value); - - #endregion - - #region Bar 8 - - note = score.Tracks[0].Staves[0].Bars[7].Voices[0].Beats[0].Notes[0]; - Assert.AreEqual(BendType.Bend, note.BendType); - Assert.AreEqual(2, note.BendPoints.Count); - Assert.AreEqual(0, note.BendPoints[0].Offset); - Assert.AreEqual(0, note.BendPoints[0].Value); - Assert.AreEqual(15, note.BendPoints[1].Offset); - Assert.AreEqual(4, note.BendPoints[1].Value); - - #endregion - - #region Bar 9 - - note = score.Tracks[0].Staves[0].Bars[8].Voices[0].Beats[0].Notes[0]; - Assert.AreEqual(BendType.BendRelease, note.BendType); - Assert.AreEqual(4, note.BendPoints.Count); - Assert.AreEqual(0, note.BendPoints[0].Offset); - Assert.AreEqual(0, note.BendPoints[0].Value); - Assert.AreEqual(10, note.BendPoints[1].Offset); - Assert.AreEqual(4, note.BendPoints[1].Value); - Assert.AreEqual(20, note.BendPoints[2].Offset); - Assert.AreEqual(4, note.BendPoints[2].Value); - Assert.AreEqual(30, note.BendPoints[3].Offset); - Assert.AreEqual(0, note.BendPoints[3].Value); - - #endregion - - #endregion - - #region Combined Bends - - #region Bar 10 - - note = score.Tracks[0].Staves[0].Bars[9].Voices[0].Beats[0].Notes[0]; - Assert.AreEqual(BendType.Bend, note.BendType); - Assert.AreEqual(2, note.BendPoints.Count); - Assert.AreEqual(0, note.BendPoints[0].Offset); - Assert.AreEqual(0, note.BendPoints[0].Value); - Assert.AreEqual(15, note.BendPoints[1].Offset); - Assert.AreEqual(4, note.BendPoints[1].Value); - - note = score.Tracks[0].Staves[0].Bars[9].Voices[0].Beats[1].Notes[0]; - Assert.AreEqual(BendType.Release, note.BendType); - Assert.IsTrue(note.IsContinuedBend); - Assert.AreEqual(2, note.BendPoints.Count); - Assert.AreEqual(0, note.BendPoints[0].Offset); - Assert.AreEqual(4, note.BendPoints[0].Value); - Assert.AreEqual(15, note.BendPoints[1].Offset); - Assert.AreEqual(0, note.BendPoints[1].Value); - - note = score.Tracks[0].Staves[0].Bars[9].Voices[0].Beats[2].Notes[0]; - Assert.AreEqual(BendType.Bend, note.BendType); - Assert.IsFalse(note.IsContinuedBend); - Assert.AreEqual(2, note.BendPoints.Count); - Assert.AreEqual(0, note.BendPoints[0].Offset); - Assert.AreEqual(0, note.BendPoints[0].Value); - Assert.AreEqual(15, note.BendPoints[1].Offset); - Assert.AreEqual(4, note.BendPoints[1].Value); - - #endregion - - - #region Bar 11 - - note = score.Tracks[0].Staves[0].Bars[10].Voices[0].Beats[0].Notes[0]; - Assert.AreEqual(BendType.Bend, note.BendType); - Assert.AreEqual(2, note.BendPoints.Count); - Assert.AreEqual(0, note.BendPoints[0].Offset); - Assert.AreEqual(0, note.BendPoints[0].Value); - Assert.AreEqual(15, note.BendPoints[1].Offset); - Assert.AreEqual(4, note.BendPoints[1].Value); - - note = score.Tracks[0].Staves[0].Bars[10].Voices[0].Beats[1].Notes[0]; - Assert.AreEqual(BendType.Bend, note.BendType); - Assert.IsTrue(note.IsContinuedBend); - Assert.AreEqual(2, note.BendPoints.Count); - Assert.AreEqual(0, note.BendPoints[0].Offset); - Assert.AreEqual(4, note.BendPoints[0].Value); - Assert.AreEqual(15, note.BendPoints[1].Offset); - Assert.AreEqual(8, note.BendPoints[1].Value); - - note = score.Tracks[0].Staves[0].Bars[10].Voices[0].Beats[2].Notes[0]; - Assert.AreEqual(BendType.Release, note.BendType); - Assert.IsTrue(note.IsContinuedBend); - Assert.AreEqual(2, note.BendPoints.Count); - Assert.AreEqual(0, note.BendPoints[0].Offset); - Assert.AreEqual(8, note.BendPoints[0].Value); - Assert.AreEqual(15, note.BendPoints[1].Offset); - Assert.AreEqual(4, note.BendPoints[1].Value); - - note = score.Tracks[0].Staves[0].Bars[10].Voices[0].Beats[3].Notes[0]; - Assert.AreEqual(BendType.Release, note.BendType); - Assert.IsTrue(note.IsContinuedBend); - Assert.AreEqual(2, note.BendPoints.Count); - Assert.AreEqual(0, note.BendPoints[0].Offset); - Assert.AreEqual(4, note.BendPoints[0].Value); - Assert.AreEqual(15, note.BendPoints[1].Offset); - Assert.AreEqual(0, note.BendPoints[1].Value); - - #endregion - - - #endregion - - #region Grace Bends - - #region Bar 12 - - note = score.Tracks[0].Staves[0].Bars[11].Voices[0].Beats[0].Notes[0]; - Assert.AreEqual(GraceType.BeforeBeat, note.Beat.GraceType); - Assert.AreEqual(BendType.Bend, note.BendType); - Assert.AreEqual(2, note.BendPoints.Count); - Assert.AreEqual(0, note.BendPoints[0].Offset); - Assert.AreEqual(0, note.BendPoints[0].Value); - Assert.AreEqual(15, note.BendPoints[1].Offset); - Assert.AreEqual(4, note.BendPoints[1].Value); - - #endregion - - #region Bar 13 - - note = score.Tracks[0].Staves[0].Bars[12].Voices[0].Beats[0].Notes[0]; - Assert.AreEqual(GraceType.BeforeBeat, note.Beat.GraceType); - Assert.AreEqual(BendType.Bend, note.BendType); - Assert.AreEqual(2, note.BendPoints.Count); - Assert.AreEqual(0, note.BendPoints[0].Offset); - Assert.AreEqual(0, note.BendPoints[0].Value); - Assert.AreEqual(15, note.BendPoints[1].Offset); - Assert.AreEqual(4, note.BendPoints[1].Value); - - note = score.Tracks[0].Staves[0].Bars[12].Voices[0].Beats[1].Notes[0]; - Assert.IsTrue(note.IsContinuedBend); - Assert.AreEqual(BendType.Hold, note.BendType); - Assert.AreEqual(2, note.BendPoints.Count); - Assert.AreEqual(0, note.BendPoints[0].Offset); - Assert.AreEqual(4, note.BendPoints[0].Value); - Assert.AreEqual(60, note.BendPoints[1].Offset); - Assert.AreEqual(4, note.BendPoints[1].Value); - - #endregion - - #region Bar 14 - - note = score.Tracks[0].Staves[0].Bars[13].Voices[0].Beats[0].Notes[0]; - Assert.AreEqual(GraceType.OnBeat, note.Beat.GraceType); - Assert.AreEqual(BendType.Bend, note.BendType); - Assert.AreEqual(2, note.BendPoints.Count); - Assert.AreEqual(0, note.BendPoints[0].Offset); - Assert.AreEqual(0, note.BendPoints[0].Value); - Assert.AreEqual(18, note.BendPoints[1].Offset); - Assert.AreEqual(1, note.BendPoints[1].Value); - - note = score.Tracks[0].Staves[0].Bars[13].Voices[0].Beats[1].Notes[0]; - Assert.IsTrue(note.IsContinuedBend); - Assert.AreEqual(BendType.Hold, note.BendType); - Assert.AreEqual(2, note.BendPoints.Count); - Assert.AreEqual(0, note.BendPoints[0].Offset); - Assert.AreEqual(1, note.BendPoints[0].Value); - Assert.AreEqual(60, note.BendPoints[1].Offset); - Assert.AreEqual(1, note.BendPoints[1].Value); - - #endregion - - #region Bar 15 - - note = score.Tracks[0].Staves[0].Bars[14].Voices[0].Beats[0].Notes[0]; - Assert.AreEqual(GraceType.BeforeBeat, note.Beat.GraceType); - Assert.AreEqual(BendType.Bend, note.BendType); - Assert.AreEqual(2, note.BendPoints.Count); - Assert.AreEqual(0, note.BendPoints[0].Offset); - Assert.AreEqual(0, note.BendPoints[0].Value); - Assert.AreEqual(15, note.BendPoints[1].Offset); - Assert.AreEqual(4, note.BendPoints[1].Value); - - note = score.Tracks[0].Staves[0].Bars[14].Voices[0].Beats[1].Notes[0]; - Assert.AreEqual(12, note.Fret); - Assert.IsTrue(note.IsTieDestination); - Assert.IsTrue(note.IsContinuedBend); - Assert.AreEqual(BendType.Hold, note.BendType); - Assert.AreEqual(2, note.BendPoints.Count); - Assert.AreEqual(0, note.BendPoints[0].Offset); - Assert.AreEqual(4, note.BendPoints[0].Value); - Assert.AreEqual(60, note.BendPoints[1].Offset); - Assert.AreEqual(4, note.BendPoints[1].Value); - - note = score.Tracks[0].Staves[0].Bars[14].Voices[0].Beats[1].Notes[1]; - Assert.AreEqual(10, note.Fret); - Assert.IsFalse(note.IsContinuedBend); - Assert.IsFalse(note.HasBend); - Assert.AreEqual(BendType.None, note.BendType); - - #endregion - - #region Bar 16 - - note = score.Tracks[0].Staves[0].Bars[15].Voices[0].Beats[0].Notes[0]; - Assert.AreEqual(10, note.Fret); - Assert.AreEqual(BendType.None, note.BendType); - - note = score.Tracks[0].Staves[0].Bars[15].Voices[0].Beats[0].Notes[1]; - Assert.AreEqual(BendType.Bend, note.BendType); - Assert.AreEqual(2, note.BendPoints.Count); - Assert.AreEqual(0, note.BendPoints[0].Offset); - Assert.AreEqual(0, note.BendPoints[0].Value); - Assert.AreEqual(15, note.BendPoints[1].Offset); - Assert.AreEqual(4, note.BendPoints[1].Value); - - #endregion - - - - #endregion - - Render(score); - } - - [TestMethod] - public void TestWhammyAdvanced() - { - var reader = PrepareGp7ImporterWithFile("GuitarPro7/WhammyAdvanced.gp"); - var score = reader.ReadScore(); - - #region Bar 1 - - var beat = score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0]; - - Assert.AreEqual(WhammyType.Dive, beat.WhammyBarType); - Assert.AreEqual(2, beat.WhammyBarPoints.Count); - Assert.AreEqual(0, beat.WhammyBarPoints[0].Offset); - Assert.AreEqual(0, beat.WhammyBarPoints[0].Value); - Assert.AreEqual(45, beat.WhammyBarPoints[1].Offset); - Assert.AreEqual(-4, beat.WhammyBarPoints[1].Value); - - beat = score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[2]; - - Assert.AreEqual(WhammyType.PrediveDive, beat.WhammyBarType); - Assert.AreEqual(2, beat.WhammyBarPoints.Count); - Assert.AreEqual(0, beat.WhammyBarPoints[0].Offset); - Assert.AreEqual(-4, beat.WhammyBarPoints[0].Value); - Assert.AreEqual(60, beat.WhammyBarPoints[1].Offset); - Assert.AreEqual(-16, beat.WhammyBarPoints[1].Value); - - #endregion - - #region Bar 2 - - beat = score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0]; - - Assert.AreEqual(WhammyType.Dip, beat.WhammyBarType); - Assert.AreEqual(3, beat.WhammyBarPoints.Count); - Assert.AreEqual(0, beat.WhammyBarPoints[0].Offset); - Assert.AreEqual(0, beat.WhammyBarPoints[0].Value); - Assert.AreEqual(15, beat.WhammyBarPoints[1].Offset); - Assert.AreEqual(-16, beat.WhammyBarPoints[1].Value); - Assert.AreEqual(30, beat.WhammyBarPoints[2].Offset); - Assert.AreEqual(0, beat.WhammyBarPoints[2].Value); - - - beat = score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[2]; - - Assert.AreEqual(WhammyType.Dip, beat.WhammyBarType); - Assert.AreEqual(4, beat.WhammyBarPoints.Count); - Assert.AreEqual(0, beat.WhammyBarPoints[0].Offset); - Assert.AreEqual(0, beat.WhammyBarPoints[0].Value); - Assert.AreEqual(14, beat.WhammyBarPoints[1].Offset); - Assert.AreEqual(-12, beat.WhammyBarPoints[1].Value); - Assert.AreEqual(31, beat.WhammyBarPoints[2].Offset); - Assert.AreEqual(-12, beat.WhammyBarPoints[2].Value); - Assert.AreEqual(53, beat.WhammyBarPoints[3].Offset); - Assert.AreEqual(0, beat.WhammyBarPoints[3].Value); - - #endregion - - #region Bar 3 - - beat = score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[0]; - - Assert.AreEqual(WhammyType.Dip, beat.WhammyBarType); - Assert.AreEqual(3, beat.WhammyBarPoints.Count); - Assert.AreEqual(0, beat.WhammyBarPoints[0].Offset); - Assert.AreEqual(0, beat.WhammyBarPoints[0].Value); - Assert.AreEqual(15, beat.WhammyBarPoints[1].Offset); - Assert.AreEqual(-16, beat.WhammyBarPoints[1].Value); - Assert.AreEqual(30, beat.WhammyBarPoints[2].Offset); - Assert.AreEqual(0, beat.WhammyBarPoints[2].Value); - - - beat = score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[2]; - - Assert.AreEqual(WhammyType.Dip, beat.WhammyBarType); - Assert.AreEqual(4, beat.WhammyBarPoints.Count); - Assert.AreEqual(0, beat.WhammyBarPoints[0].Offset); - Assert.AreEqual(0, beat.WhammyBarPoints[0].Value); - Assert.AreEqual(14, beat.WhammyBarPoints[1].Offset); - Assert.AreEqual(-12, beat.WhammyBarPoints[1].Value); - Assert.AreEqual(31, beat.WhammyBarPoints[2].Offset); - Assert.AreEqual(-12, beat.WhammyBarPoints[2].Value); - Assert.AreEqual(53, beat.WhammyBarPoints[3].Offset); - Assert.AreEqual(0, beat.WhammyBarPoints[3].Value); - - #endregion - - #region Bar 4 - - beat = score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[0]; - - Assert.AreEqual(WhammyType.Predive, beat.WhammyBarType); - Assert.AreEqual(2, beat.WhammyBarPoints.Count); - Assert.AreEqual(0, beat.WhammyBarPoints[0].Offset); - Assert.AreEqual(-8, beat.WhammyBarPoints[0].Value); - Assert.AreEqual(60, beat.WhammyBarPoints[1].Offset); - Assert.AreEqual(-8, beat.WhammyBarPoints[1].Value); - - #endregion - - #region Bar 5 - - beat = score.Tracks[0].Staves[0].Bars[4].Voices[0].Beats[0]; - - Assert.AreEqual(WhammyType.PrediveDive, beat.WhammyBarType); - Assert.AreEqual(2, beat.WhammyBarPoints.Count); - Assert.AreEqual(0, beat.WhammyBarPoints[0].Offset); - Assert.AreEqual(-4, beat.WhammyBarPoints[0].Value); - Assert.AreEqual(30, beat.WhammyBarPoints[1].Offset); - Assert.AreEqual(0, beat.WhammyBarPoints[1].Value); - - #endregion - - #region Bar 6 - - beat = score.Tracks[0].Staves[0].Bars[5].Voices[0].Beats[0]; - - Assert.AreEqual(WhammyType.PrediveDive, beat.WhammyBarType); - Assert.AreEqual(2, beat.WhammyBarPoints.Count); - Assert.AreEqual(0, beat.WhammyBarPoints[0].Offset); - Assert.AreEqual(-4, beat.WhammyBarPoints[0].Value); - Assert.AreEqual(29, beat.WhammyBarPoints[1].Offset); - Assert.AreEqual(-12, beat.WhammyBarPoints[1].Value); - - - beat = score.Tracks[0].Staves[0].Bars[5].Voices[0].Beats[1]; - - Assert.AreEqual(WhammyType.Dive, beat.WhammyBarType); - Assert.AreEqual(2, beat.WhammyBarPoints.Count); - Assert.AreEqual(0, beat.WhammyBarPoints[0].Offset); - Assert.AreEqual(-12, beat.WhammyBarPoints[0].Value); - Assert.AreEqual(45, beat.WhammyBarPoints[1].Offset); - Assert.AreEqual(0, beat.WhammyBarPoints[1].Value); - - #endregion - - #region Bar 7 - - beat = score.Tracks[0].Staves[0].Bars[6].Voices[0].Beats[0]; - - Assert.AreEqual(WhammyType.Dive, beat.WhammyBarType); - Assert.AreEqual(2, beat.WhammyBarPoints.Count); - Assert.AreEqual(0, beat.WhammyBarPoints[0].Offset); - Assert.AreEqual(0, beat.WhammyBarPoints[0].Value); - Assert.AreEqual(45, beat.WhammyBarPoints[1].Offset); - Assert.AreEqual(-4, beat.WhammyBarPoints[1].Value); - - - beat = score.Tracks[0].Staves[0].Bars[6].Voices[0].Beats[1]; - Assert.AreEqual(WhammyType.Hold, beat.WhammyBarType); - Assert.AreEqual(2, beat.WhammyBarPoints.Count); - Assert.AreEqual(0, beat.WhammyBarPoints[0].Offset); - Assert.AreEqual(-4, beat.WhammyBarPoints[0].Value); - Assert.AreEqual(60, beat.WhammyBarPoints[1].Offset); - Assert.AreEqual(-4, beat.WhammyBarPoints[1].Value); - - #endregion - - #region Bar 8 - - beat = score.Tracks[0].Staves[0].Bars[7].Voices[0].Beats[0]; - - Assert.AreEqual(WhammyType.Dive, beat.WhammyBarType); - Assert.AreEqual(2, beat.WhammyBarPoints.Count); - Assert.AreEqual(0, beat.WhammyBarPoints[0].Offset); - Assert.AreEqual(-4, beat.WhammyBarPoints[0].Value); - Assert.AreEqual(46, beat.WhammyBarPoints[1].Offset); - Assert.AreEqual(-12, beat.WhammyBarPoints[1].Value); - - beat = score.Tracks[0].Staves[0].Bars[7].Voices[0].Beats[1]; - - Assert.AreEqual(WhammyType.Dive, beat.WhammyBarType); - Assert.AreEqual(2, beat.WhammyBarPoints.Count); - Assert.AreEqual(0, beat.WhammyBarPoints[0].Offset); - Assert.AreEqual(-12, beat.WhammyBarPoints[0].Value); - Assert.AreEqual(44, beat.WhammyBarPoints[1].Offset); - Assert.AreEqual(8, beat.WhammyBarPoints[1].Value); - - #endregion - - #region Bar 9 - - beat = score.Tracks[0].Staves[0].Bars[8].Voices[0].Beats[0]; - - Assert.AreEqual(WhammyType.Dip, beat.WhammyBarType); - Assert.AreEqual(3, beat.WhammyBarPoints.Count); - Assert.AreEqual(0, beat.WhammyBarPoints[0].Offset); - Assert.AreEqual(8, beat.WhammyBarPoints[0].Value); - Assert.AreEqual(15, beat.WhammyBarPoints[1].Offset); - Assert.AreEqual(12, beat.WhammyBarPoints[1].Value); - Assert.AreEqual(30, beat.WhammyBarPoints[2].Offset); - Assert.AreEqual(0, beat.WhammyBarPoints[2].Value); - - beat = score.Tracks[0].Staves[0].Bars[8].Voices[0].Beats[1]; - - Assert.AreEqual(WhammyType.Dip, beat.WhammyBarType); - Assert.AreEqual(3, beat.WhammyBarPoints.Count); - Assert.AreEqual(0, beat.WhammyBarPoints[0].Offset); - Assert.AreEqual(0, beat.WhammyBarPoints[0].Value); - Assert.AreEqual(15, beat.WhammyBarPoints[1].Offset); - Assert.AreEqual(-4, beat.WhammyBarPoints[1].Value); - Assert.AreEqual(30, beat.WhammyBarPoints[2].Offset); - Assert.AreEqual(0, beat.WhammyBarPoints[2].Value); - - #endregion - - Render(score); - } - - [TestMethod] - public void TestTremolo() - { - var reader = PrepareGp7ImporterWithFile("GuitarPro7/TestTremolo.gp"); - var score = reader.ReadScore(); - - Assert.AreEqual(3, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].WhammyBarPoints.Count); - - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].WhammyBarPoints[0].Offset); - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].WhammyBarPoints[0].Value); - - Assert.AreEqual(30, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].WhammyBarPoints[1].Offset); - Assert.AreEqual(-4, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].WhammyBarPoints[1].Value); - - Assert.AreEqual(60, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].WhammyBarPoints[2].Offset); - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].WhammyBarPoints[2].Value); - - - Assert.AreEqual(2, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].WhammyBarPoints.Count); - - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].WhammyBarPoints[0].Offset); - Assert.AreEqual(-4, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].WhammyBarPoints[0].Value); - - Assert.AreEqual(60, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].WhammyBarPoints[1].Offset); - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].WhammyBarPoints[1].Value); - - Assert.AreEqual(4, score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[0].WhammyBarPoints.Count); - - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[0].WhammyBarPoints[0].Offset); - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[0].WhammyBarPoints[0].Value); - - Assert.AreEqual(30, score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[0].WhammyBarPoints[1].Offset); - Assert.AreEqual(-4, score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[0].WhammyBarPoints[1].Value); - - Assert.AreEqual(30, score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[0].WhammyBarPoints[2].Offset); - Assert.AreEqual(-4, score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[0].WhammyBarPoints[2].Value); - - Assert.AreEqual(60, score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[0].WhammyBarPoints[3].Offset); - Assert.AreEqual(-4, score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[0].WhammyBarPoints[3].Value); - - Assert.AreEqual(4, score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[0].WhammyBarPoints.Count); - - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[0].WhammyBarPoints[0].Offset); - Assert.AreEqual(-4, score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[0].WhammyBarPoints[0].Value); - - Assert.AreEqual(15, score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[0].WhammyBarPoints[1].Offset); - Assert.AreEqual(-12, score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[0].WhammyBarPoints[1].Value); - - Assert.AreEqual(30, score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[0].WhammyBarPoints[2].Offset); - Assert.AreEqual(-12, score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[0].WhammyBarPoints[2].Value); - - Assert.AreEqual(45, score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[0].WhammyBarPoints[3].Offset); - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[0].WhammyBarPoints[3].Value); - - Render(score); - } - - [TestMethod] - public void TestSlides() - { - var reader = PrepareGp7ImporterWithFile("GuitarPro7/TestSlides.gp"); - var score = reader.ReadScore(); - CheckSlides(score); - Render(score); - } - - [TestMethod] - public void TestVibrato() - { - var reader = PrepareGp7ImporterWithFile("GuitarPro7/TestVibrato.gp"); - var score = reader.ReadScore(); - CheckVibrato(score, true); - Render(score); - } - - [TestMethod] - public void TestTrills() - { - var reader = PrepareGp7ImporterWithFile("GuitarPro7/TestTrills.gp"); - var score = reader.ReadScore(); - CheckTrills(score); - Render(score); - } - - [TestMethod] - public void TestOtherEffects() - { - var reader = PrepareGp7ImporterWithFile("GuitarPro7/TestOtherEffects.gp"); - var score = reader.ReadScore(); - CheckOtherEffects(score, true /* GPX doesn't support instrument changes */); - Render(score); - } - - [TestMethod] - public void TestFingering() - { - var reader = PrepareGp7ImporterWithFile("GuitarPro7/TestFingering.gp"); - var score = reader.ReadScore(); - CheckFingering(score); - Render(score); - } - - [TestMethod] - public void TestStroke() - { - var reader = PrepareGp7ImporterWithFile("GuitarPro7/TestStrokes.gp"); - var score = reader.ReadScore(); - CheckStroke(score); - Render(score); - } - - [TestMethod] - public void TestTuplets() - { - var reader = PrepareGp7ImporterWithFile("GuitarPro7/TestTuplets.gp"); - var score = reader.ReadScore(); - CheckTuplets(score); - Render(score); - } - - [TestMethod] - public void TestRanges() - { - var reader = PrepareGp7ImporterWithFile("GuitarPro7/TestRanges.gp"); - var score = reader.ReadScore(); - CheckRanges(score); - Render(score); - } - - [TestMethod] - public void TestEffects() - { - var reader = PrepareGp7ImporterWithFile("GuitarPro7/Effects.gp"); - var score = reader.ReadScore(); - CheckEffects(score); - Render(score); - } - - [TestMethod] - public void TestSerenade() - { - var reader = PrepareGp7ImporterWithFile("GuitarPro7/Serenade.gp"); - var score = reader.ReadScore();// only Check reading - Render(score); - } - - [TestMethod] - public void TestStrings() - { - var reader = PrepareGp7ImporterWithFile("GuitarPro7/TestStrings.gp"); - var score = reader.ReadScore(); - CheckStrings(score); - Render(score); - } - - [TestMethod] - public void TestKeySignatures() - { - var reader = PrepareGp7ImporterWithFile("GuitarPro7/TestKeySignatures.gp"); - var score = reader.ReadScore(); - CheckKeySignatures(score); - Render(score); - } - - [TestMethod] - public void TestChords() - { - var reader = PrepareGp7ImporterWithFile("GuitarPro7/TestChords.gp"); - var score = reader.ReadScore(); - CheckChords(score); - Render(score); - } - - - - [TestMethod] - public void TestColors() - { - var reader = PrepareGp7ImporterWithFile("GuitarPro7/Colors.gp"); - var score = reader.ReadScore(); - - CheckColors(score); - Render(score); - } - - - [TestMethod] - public void TestTremoloVibrato() - { - var reader = PrepareGp7ImporterWithFile("GuitarPro7/TestTremoloVibrato.gp"); - var score = reader.ReadScore(); - - Assert.AreEqual(VibratoType.Slight, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].Vibrato); - - Assert.AreEqual(VibratoType.Wide, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Notes[0].Vibrato); - Assert.AreEqual(VibratoType.Slight, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Notes[1].Vibrato); - - Assert.AreEqual(VibratoType.Slight, score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[0].Vibrato); - - Assert.AreEqual(VibratoType.Wide, score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[0].Vibrato); - - Assert.AreEqual(VibratoType.Wide, score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[0].Vibrato); - - Render(score); - } - - [TestMethod] - public void TestOttavia() - { - var reader = PrepareGp7ImporterWithFile("GuitarPro7/TestOttavia.gp"); - var score = reader.ReadScore(); - - Assert.AreEqual(Ottavia._8va, score.Tracks[0].Staves[0].Bars[0].ClefOttava); - Assert.AreEqual(Ottavia._8vb, score.Tracks[0].Staves[0].Bars[1].ClefOttava); - Assert.AreEqual(Ottavia._15ma, score.Tracks[0].Staves[0].Bars[2].ClefOttava); - Assert.AreEqual(Ottavia._15mb, score.Tracks[0].Staves[0].Bars[3].ClefOttava); - - - Assert.AreEqual(Ottavia._8va, score.Tracks[0].Staves[0].Bars[4].Voices[0].Beats[0].Ottava); - Assert.AreEqual(Ottavia._8vb, score.Tracks[0].Staves[0].Bars[4].Voices[0].Beats[1].Ottava); - Assert.AreEqual(Ottavia._15ma, score.Tracks[0].Staves[0].Bars[4].Voices[0].Beats[2].Ottava); - Assert.AreEqual(Ottavia._15mb, score.Tracks[0].Staves[0].Bars[4].Voices[0].Beats[3].Ottava); - } - - - [TestMethod] - public void TestSimileMark() - { - var reader = PrepareGp7ImporterWithFile("GuitarPro7/TestSimileMark.gp"); - var score = reader.ReadScore(); - - Assert.AreEqual(SimileMark.None, score.Tracks[0].Staves[0].Bars[0].SimileMark); - Assert.AreEqual(SimileMark.Simple, score.Tracks[0].Staves[0].Bars[1].SimileMark); - - Assert.AreEqual(SimileMark.None, score.Tracks[0].Staves[0].Bars[2].SimileMark); - Assert.AreEqual(SimileMark.None, score.Tracks[0].Staves[0].Bars[3].SimileMark); - - Assert.AreEqual(SimileMark.FirstOfDouble, score.Tracks[0].Staves[0].Bars[4].SimileMark); - Assert.AreEqual(SimileMark.SecondOfDouble, score.Tracks[0].Staves[0].Bars[5].SimileMark); - } - - [TestMethod] - public void TestFermata() - { - var reader = PrepareGp7ImporterWithFile("GuitarPro7/TestFermata.gp"); - var score = reader.ReadScore(); - - Assert.AreEqual(5, score.MasterBars[0].Fermata.Count); - Assert.AreEqual(5, score.MasterBars[1].Fermata.Count); - Assert.AreEqual(5, score.MasterBars[2].Fermata.Count); - - // Short - var offsets = new[] - { - 0, - (int) (MidiUtils.QuarterTime * (1f / 2f)), - (int) (MidiUtils.QuarterTime * (1f / 1f)), - (int) (MidiUtils.QuarterTime * (2f / 1f)), - (int) (MidiUtils.QuarterTime * (3f / 1f)) - }; - var types = new[] - { - FermataType.Short, - FermataType.Medium, - FermataType.Long - }; - - for (int i = 0; i < 3; i++) - { - var masterBar = score.MasterBars[i]; - Assert.AreEqual(5, masterBar.Fermata.Count); - foreach (var offset in offsets) - { - var fermata = masterBar.Fermata[offset]; - Assert.IsNotNull(fermata); - Assert.AreEqual(types[i], fermata.Type); - } - - var beats = score.Tracks[0].Staves[0].Bars[i].Voices[0].Beats; - foreach (var beat in beats) - { - var fermata = masterBar.Fermata[beat.PlaybackStart]; - var beatFermata = beat.Fermata; - Assert.IsNotNull(beatFermata); - Assert.IsNotNull(fermata); - Assert.AreEqual(types[i], beatFermata.Type); - Assert.AreEqual(types[i], fermata.Type); - } - } - } - - [TestMethod] - public void TestPickSlide() - { - var reader = PrepareGp7ImporterWithFile("GuitarPro7/TestPickSlide.gp"); - var score = reader.ReadScore(); - - Assert.AreEqual(SlideType.PickSlideUp, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].SlideType); - Assert.AreEqual(10, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].Fret); - Assert.AreEqual(10, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].Fret); - - Assert.AreEqual(SlideType.PickSlideDown, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[2].Notes[0].SlideType); - Assert.AreEqual(10, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[2].Notes[0].Fret); - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[3].Notes[0].Fret); - - - Assert.AreEqual(SlideType.PickSlideUp, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Notes[0].SlideType); - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Notes[0].Fret); - Assert.AreEqual(10, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[1].Notes[0].Fret); - - Assert.AreEqual(SlideType.PickSlideDown, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[2].Notes[0].SlideType); - Assert.AreEqual(10, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[2].Notes[0].Fret); - Assert.AreEqual(5, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[3].Notes[0].Fret); - - - Assert.AreEqual(SlideType.PickSlideDown, score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[0].Notes[0].SlideType); - Assert.AreEqual(20, score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[0].Notes[0].Fret); - - Assert.AreEqual(SlideType.PickSlideDown, score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[1].Notes[0].SlideType); - Assert.AreEqual(12, score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[1].Notes[0].Fret); - - Assert.AreEqual(SlideType.PickSlideDown, score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[2].Notes[0].SlideType); - Assert.AreEqual(5, score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[2].Notes[0].Fret); - - Assert.AreEqual(SlideType.PickSlideDown, score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[3].Notes[0].SlideType); - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[3].Notes[0].Fret); - - - Assert.AreEqual(SlideType.PickSlideDown, score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[0].Notes[0].SlideType); - Assert.AreEqual(20, score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[0].Notes[0].Fret); - - Assert.AreEqual(SlideType.PickSlideDown, score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[1].Notes[0].SlideType); - Assert.AreEqual(12, score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[1].Notes[0].Fret); - - Assert.AreEqual(SlideType.PickSlideUp, score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[2].Notes[0].SlideType); - Assert.AreEqual(5, score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[2].Notes[0].Fret); - - Assert.AreEqual(SlideType.PickSlideUp, score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[3].Notes[0].SlideType); - Assert.AreEqual(10, score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[3].Notes[0].Fret); - - - Assert.AreEqual(SlideType.PickSlideDown, score.Tracks[0].Staves[0].Bars[4].Voices[0].Beats[0].Notes[0].SlideType); - Assert.AreEqual(20, score.Tracks[0].Staves[0].Bars[4].Voices[0].Beats[0].Notes[0].Fret); - - Render(score); - } - } -} diff --git a/Source/AlphaTab.Test/Importer/GpImporterTestBase.cs b/Source/AlphaTab.Test/Importer/GpImporterTestBase.cs deleted file mode 100644 index 9b005a1f4..000000000 --- a/Source/AlphaTab.Test/Importer/GpImporterTestBase.cs +++ /dev/null @@ -1,593 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using System.IO; -using System.Runtime.CompilerServices; -using System.Xml.Linq; -using AlphaTab.Collections; -using AlphaTab.Importer; -using AlphaTab.IO; -using AlphaTab.Model; -using AlphaTab.Rendering; -using AlphaTab.Util; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace AlphaTab.Test.Importer -{ - public class GpImporterTestBase - { - internal Gp3To5Importer PrepareImporterWithFile(string name) - { - const string path = "TestFiles/"; - var buffer = TestPlatform.LoadFile(path + name); - return PrepareImporterWithBytes(buffer); - } - - internal Gp3To5Importer PrepareImporterWithBytes(byte[] buffer) - { - var readerBase = new Gp3To5Importer(); - readerBase.Init(ByteBuffer.FromBuffer(buffer)); - return readerBase; - } - - #region Some general checks for common files - - protected void CheckTest02Score(Score score) - { - // Whole Notes - int beat = 0; - - var durationsInFile = new[] - { - Duration.Whole, - Duration.Half, - Duration.Quarter, - Duration.Eighth, - Duration.Sixteenth, - Duration.ThirtySecond, - Duration.SixtyFourth - }; - foreach (var duration in durationsInFile) - { - Assert.AreEqual(1, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[beat].Notes[0].Fret); - Assert.AreEqual(1, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[beat].Notes[0].String); - Assert.AreEqual(duration, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[beat].Duration); - beat++; - - Assert.AreEqual(2, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[beat].Notes[0].Fret); - Assert.AreEqual(1, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[beat].Notes[0].String); - Assert.AreEqual(duration, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[beat].Duration); - beat++; - - Assert.AreEqual(3, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[beat].Notes[0].Fret); - Assert.AreEqual(1, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[beat].Notes[0].String); - Assert.AreEqual(duration, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[beat].Duration); - beat++; - - Assert.AreEqual(4, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[beat].Notes[0].Fret); - Assert.AreEqual(1, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[beat].Notes[0].String); - Assert.AreEqual(duration, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[beat].Duration); - beat++; - - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[beat].IsRest); - Assert.AreEqual(duration, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[beat].Duration); - beat++; - } - } - - - protected void CheckTest03Score(Score score) - { - Assert.AreEqual(4, score.MasterBars[0].TimeSignatureNumerator); - Assert.AreEqual(4, score.MasterBars[0].TimeSignatureDenominator); - - Assert.AreEqual(3, score.MasterBars[1].TimeSignatureNumerator); - Assert.AreEqual(4, score.MasterBars[1].TimeSignatureDenominator); - - Assert.AreEqual(2, score.MasterBars[2].TimeSignatureNumerator); - Assert.AreEqual(4, score.MasterBars[2].TimeSignatureDenominator); - - Assert.AreEqual(1, score.MasterBars[3].TimeSignatureNumerator); - Assert.AreEqual(4, score.MasterBars[3].TimeSignatureDenominator); - - Assert.AreEqual(20, score.MasterBars[4].TimeSignatureNumerator); - Assert.AreEqual(32, score.MasterBars[4].TimeSignatureDenominator); - } - - protected void CheckDead(Score score) - { - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].IsDead); - Assert.AreEqual(1, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].String); - - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].IsDead); - Assert.AreEqual(2, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].String); - - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[2].Notes[0].IsDead); - Assert.AreEqual(3, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[2].Notes[0].String); - - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[3].Notes[0].IsDead); - Assert.AreEqual(4, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[3].Notes[0].String); - } - - protected void CheckGrace(Score score) - { - Assert.AreEqual(GraceType.BeforeBeat, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].GraceType); - Assert.AreEqual(3, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].Fret); - Assert.AreEqual(Duration.Eighth, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Duration); - Assert.AreEqual(2, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].Fret); - Assert.AreEqual(Duration.Quarter, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Duration); - - Assert.AreEqual(GraceType.BeforeBeat, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[2].GraceType); - Assert.AreEqual(2, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[2].Notes[0].Fret); - Assert.AreEqual(Duration.Eighth, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[2].Duration); - Assert.AreEqual(2, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[3].Notes[0].Fret); - Assert.AreEqual(Duration.Quarter, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[3].Duration); - } - - protected void CheckAccentuation(Score score, bool includeHeavy) - { - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].IsGhost); - Assert.AreEqual(AccentuationType.Normal, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].Accentuated); - if (includeHeavy) - { - Assert.AreEqual(AccentuationType.Heavy, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[2].Notes[0].Accentuated); - } - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[3].Notes[0].IsLetRing); - } - - protected void CheckHarmonics(Score score) - { - Assert.AreEqual(HarmonicType.Natural, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].HarmonicType); - Assert.AreEqual(HarmonicType.Artificial, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].HarmonicType); - Assert.AreEqual(HarmonicType.Tap, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[2].Notes[0].HarmonicType); - Assert.AreEqual(HarmonicType.Semi, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[3].Notes[0].HarmonicType); - Assert.AreEqual(HarmonicType.Pinch, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[4].Notes[0].HarmonicType); - // TODO: Harmonic Values - } - - protected void CheckHammer(Score score) - { - Assert.AreEqual(false, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].IsHammerPullOrigin); - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[1].IsHammerPullOrigin); - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[2].IsHammerPullOrigin); - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[3].IsHammerPullOrigin); - - Assert.IsNull(score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].HammerPullOrigin); - Assert.IsNotNull(score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[1].HammerPullOrigin); - Assert.IsNotNull(score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[2].HammerPullOrigin); - Assert.IsNotNull(score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[3].HammerPullOrigin); - - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Notes[0].IsHammerPullOrigin); - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[1].Notes[0].IsHammerPullOrigin); - Assert.IsNotNull(score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[2].Notes[0].HammerPullOrigin); - } - - protected void CheckBend(Score score) - { - Assert.AreEqual(3, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].BendPoints.Count); - - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].BendPoints[0].Offset); - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].BendPoints[0].Value); - - Assert.AreEqual(15, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].BendPoints[1].Offset); - Assert.AreEqual(4, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].BendPoints[1].Value); - - Assert.AreEqual(60, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].BendPoints[2].Offset); - Assert.AreEqual(4, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].BendPoints[2].Value); - - Assert.AreEqual(7, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].BendPoints.Count); - - - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].BendPoints[0].Offset); - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].BendPoints[0].Value); - - Assert.AreEqual(10, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].BendPoints[1].Offset); - Assert.AreEqual(4, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].BendPoints[1].Value); - - Assert.AreEqual(20, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].BendPoints[2].Offset); - Assert.AreEqual(4, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].BendPoints[2].Value); - - Assert.AreEqual(30, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].BendPoints[3].Offset); - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].BendPoints[3].Value); - - Assert.AreEqual(40, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].BendPoints[4].Offset); - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].BendPoints[4].Value); - - Assert.AreEqual(50, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].BendPoints[5].Offset); - Assert.AreEqual(4, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].BendPoints[5].Value); - - Assert.AreEqual(60, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].BendPoints[6].Offset); - Assert.AreEqual(4, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].BendPoints[6].Value); - } - - protected void CheckTremolo(Score score) - { - Assert.AreEqual(3, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].WhammyBarPoints.Count); - - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].WhammyBarPoints[0].Offset); - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].WhammyBarPoints[0].Value); - - Assert.AreEqual(30, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].WhammyBarPoints[1].Offset); - Assert.AreEqual(-4, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].WhammyBarPoints[1].Value); - - Assert.AreEqual(60, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].WhammyBarPoints[2].Offset); - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].WhammyBarPoints[2].Value); - - - Assert.AreEqual(3, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].WhammyBarPoints.Count); - - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].WhammyBarPoints[0].Offset); - Assert.AreEqual(-4, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].WhammyBarPoints[0].Value); - - Assert.AreEqual(45, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].WhammyBarPoints[1].Offset); - Assert.AreEqual(-4, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].WhammyBarPoints[1].Value); - - Assert.AreEqual(60, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].WhammyBarPoints[2].Offset); - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].WhammyBarPoints[2].Value); - - Assert.AreEqual(3, score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[0].WhammyBarPoints.Count); - - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[0].WhammyBarPoints[0].Offset); - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[0].WhammyBarPoints[0].Value); - - Assert.AreEqual(45, score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[0].WhammyBarPoints[1].Offset); - Assert.AreEqual(-4, score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[0].WhammyBarPoints[1].Value); - - Assert.AreEqual(60, score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[0].WhammyBarPoints[2].Offset); - Assert.AreEqual(-4, score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[0].WhammyBarPoints[2].Value); - - } - - protected void CheckSlides(Score score) - { - Assert.AreEqual(SlideType.Legato, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].GetNoteOnString(5).SlideType); - Assert.AreEqual(SlideType.Shift, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[2].GetNoteOnString(2).SlideType); - Assert.AreEqual(SlideType.IntoFromBelow, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].GetNoteOnString(5).SlideType); - Assert.AreEqual(SlideType.IntoFromAbove, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[1].GetNoteOnString(5).SlideType); - Assert.AreEqual(SlideType.OutDown, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[2].GetNoteOnString(5).SlideType); - Assert.AreEqual(SlideType.OutUp, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[3].GetNoteOnString(5).SlideType); - } - - protected void CheckStrings(Score score) - { - Assert.AreEqual(6, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes.Count); - Assert.AreEqual(6, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].GetNoteOnString(1).Fret); - Assert.AreEqual(5, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].GetNoteOnString(2).Fret); - Assert.AreEqual(4, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].GetNoteOnString(3).Fret); - Assert.AreEqual(3, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].GetNoteOnString(4).Fret); - Assert.AreEqual(2, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].GetNoteOnString(5).Fret); - Assert.AreEqual(1, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].GetNoteOnString(6).Fret); - } - - protected void CheckVibrato(Score score, bool checkNotes) - { - if (checkNotes) - { - Assert.AreEqual(VibratoType.Slight, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].Vibrato); - Assert.AreEqual(VibratoType.Slight, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].Vibrato); - } - - Assert.AreEqual(VibratoType.Slight, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[2].Vibrato); - Assert.AreEqual(VibratoType.Slight, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[3].Vibrato); - } - - protected void CheckTrills(Score score) - { - Assert.AreEqual(2, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].TrillFret); - Assert.AreEqual(Duration.Sixteenth, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].TrillSpeed); - - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].IsTremolo); - Assert.AreEqual(Duration.ThirtySecond, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].TremoloSpeed); - - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[2].IsTremolo); - Assert.AreEqual(Duration.Sixteenth, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[2].TremoloSpeed); - - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[3].IsTremolo); - Assert.AreEqual(Duration.Eighth, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[3].TremoloSpeed); - } - - protected void CheckOtherEffects(Score score, bool skipInstrumentCheck = false) - { - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].IsPalmMute); - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].IsStaccato); - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[2].Tap); - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[3].Slap); - - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Pop); - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[1].FadeIn); - - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[0].HasChord); - Assert.AreEqual("C", score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[0].Chord.Name); - Assert.AreEqual("Text", score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[1].Text); - Assert.IsTrue(score.MasterBars[4].IsDoubleBar); - Assert.IsNotNull(score.MasterBars[4].TempoAutomation); - Assert.AreEqual(120.0, score.MasterBars[4].TempoAutomation.Value); - if (!skipInstrumentCheck) - { - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[4].Voices[0].Beats[0].GetAutomation(AutomationType.Instrument) != null); - Assert.AreEqual(25.0, score.Tracks[0].Staves[0].Bars[4].Voices[0].Beats[0].GetAutomation(AutomationType.Instrument).Value); - } - } - - protected void CheckFingering(Score score) - { - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].IsFingering); - Assert.AreEqual(Fingers.Thumb, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].LeftHandFinger); - Assert.AreEqual(Fingers.IndexFinger, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].LeftHandFinger); - Assert.AreEqual(Fingers.MiddleFinger, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[2].Notes[0].LeftHandFinger); - Assert.AreEqual(Fingers.AnnularFinger, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[3].Notes[0].LeftHandFinger); - Assert.AreEqual(Fingers.LittleFinger, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[4].Notes[0].LeftHandFinger); - Assert.AreEqual(Fingers.Thumb, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[5].Notes[0].RightHandFinger); - Assert.AreEqual(Fingers.IndexFinger, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[6].Notes[0].RightHandFinger); - Assert.AreEqual(Fingers.MiddleFinger, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[7].Notes[0].RightHandFinger); - Assert.AreEqual(Fingers.AnnularFinger, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[8].Notes[0].RightHandFinger); - Assert.AreEqual(Fingers.LittleFinger, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[9].Notes[0].RightHandFinger); - } - - protected void CheckStroke(Score score) - { - Assert.AreEqual(BrushType.BrushDown, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].BrushType); - Assert.AreEqual(BrushType.BrushUp, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].BrushType); - Assert.AreEqual(PickStroke.Up, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[2].PickStroke); - Assert.AreEqual(PickStroke.Down, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[3].PickStroke); - } - - protected void CheckTuplets(Score score) - { - Assert.AreEqual(3, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].TupletNumerator); - Assert.AreEqual(3, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].TupletNumerator); - Assert.AreEqual(3, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[2].TupletNumerator); - - Assert.AreEqual(5, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].TupletNumerator); - Assert.AreEqual(5, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[1].TupletNumerator); - Assert.AreEqual(5, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[2].TupletNumerator); - Assert.AreEqual(5, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[3].TupletNumerator); - Assert.AreEqual(5, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[4].TupletNumerator); - } - - protected void CheckRanges(Score score) - { - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].IsPalmMute); - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].IsPalmMute); - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[2].Notes[0].IsPalmMute); - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[3].Notes[0].IsPalmMute); - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Notes[0].IsPalmMute); - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Notes[0].IsPalmMute); - - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[1].Notes[0].IsLetRing); - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[2].Notes[0].IsLetRing); - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[3].Notes[0].IsLetRing); - Assert.IsTrue(score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[0].Notes[0].IsLetRing); - } - - protected void CheckEffects(Score score) - { - // just check if reading works - Assert.IsTrue(true); - } - - - protected void CheckKeySignatures(Score score) - { - // major - flats - Assert.AreEqual(KeySignature.C, score.MasterBars[0].KeySignature); - Assert.AreEqual(KeySignatureType.Major, score.MasterBars[0].KeySignatureType); - Assert.AreEqual(KeySignature.F, score.MasterBars[1].KeySignature); - Assert.AreEqual(KeySignatureType.Major, score.MasterBars[1].KeySignatureType); - Assert.AreEqual(KeySignature.Bb, score.MasterBars[2].KeySignature); - Assert.AreEqual(KeySignatureType.Major, score.MasterBars[2].KeySignatureType); - Assert.AreEqual(KeySignature.Eb, score.MasterBars[3].KeySignature); - Assert.AreEqual(KeySignatureType.Major, score.MasterBars[3].KeySignatureType); - Assert.AreEqual(KeySignature.Ab, score.MasterBars[4].KeySignature); - Assert.AreEqual(KeySignatureType.Major, score.MasterBars[4].KeySignatureType); - Assert.AreEqual(KeySignature.Db, score.MasterBars[5].KeySignature); - Assert.AreEqual(KeySignatureType.Major, score.MasterBars[5].KeySignatureType); - Assert.AreEqual(KeySignature.Gb, score.MasterBars[6].KeySignature); - Assert.AreEqual(KeySignatureType.Major, score.MasterBars[6].KeySignatureType); - Assert.AreEqual(KeySignature.Cb, score.MasterBars[7].KeySignature); - Assert.AreEqual(KeySignatureType.Major, score.MasterBars[7].KeySignatureType); - // major - sharps - Assert.AreEqual(KeySignature.C, score.MasterBars[8].KeySignature); - Assert.AreEqual(KeySignatureType.Major, score.MasterBars[8].KeySignatureType); - Assert.AreEqual(KeySignature.G, score.MasterBars[9].KeySignature); - Assert.AreEqual(KeySignatureType.Major, score.MasterBars[9].KeySignatureType); - Assert.AreEqual(KeySignature.D, score.MasterBars[10].KeySignature); - Assert.AreEqual(KeySignatureType.Major, score.MasterBars[10].KeySignatureType); - Assert.AreEqual(KeySignature.A, score.MasterBars[11].KeySignature); - Assert.AreEqual(KeySignatureType.Major, score.MasterBars[11].KeySignatureType); - Assert.AreEqual(KeySignature.E, score.MasterBars[12].KeySignature); - Assert.AreEqual(KeySignatureType.Major, score.MasterBars[12].KeySignatureType); - Assert.AreEqual(KeySignature.B, score.MasterBars[13].KeySignature); - Assert.AreEqual(KeySignatureType.Major, score.MasterBars[13].KeySignatureType); - Assert.AreEqual(KeySignature.FSharp, score.MasterBars[14].KeySignature); - Assert.AreEqual(KeySignatureType.Major, score.MasterBars[14].KeySignatureType); - Assert.AreEqual(KeySignature.CSharp, score.MasterBars[15].KeySignature); - Assert.AreEqual(KeySignatureType.Major, score.MasterBars[15].KeySignatureType); - // minor flats - Assert.AreEqual(KeySignature.C, score.MasterBars[16].KeySignature); - Assert.AreEqual(KeySignatureType.Minor, score.MasterBars[16].KeySignatureType); - Assert.AreEqual(KeySignature.F, score.MasterBars[17].KeySignature); - Assert.AreEqual(KeySignatureType.Minor, score.MasterBars[17].KeySignatureType); - Assert.AreEqual(KeySignature.Bb, score.MasterBars[18].KeySignature); - Assert.AreEqual(KeySignatureType.Minor, score.MasterBars[18].KeySignatureType); - Assert.AreEqual(KeySignature.Eb, score.MasterBars[19].KeySignature); - Assert.AreEqual(KeySignatureType.Minor, score.MasterBars[19].KeySignatureType); - Assert.AreEqual(KeySignature.Ab, score.MasterBars[20].KeySignature); - Assert.AreEqual(KeySignatureType.Minor, score.MasterBars[20].KeySignatureType); - Assert.AreEqual(KeySignature.Db, score.MasterBars[21].KeySignature); - Assert.AreEqual(KeySignatureType.Minor, score.MasterBars[21].KeySignatureType); - Assert.AreEqual(KeySignature.Gb, score.MasterBars[22].KeySignature); - Assert.AreEqual(KeySignatureType.Minor, score.MasterBars[22].KeySignatureType); - Assert.AreEqual(KeySignature.Cb, score.MasterBars[23].KeySignature); - Assert.AreEqual(KeySignatureType.Minor, score.MasterBars[23].KeySignatureType); - // minor sharps - Assert.AreEqual(KeySignature.C, score.MasterBars[24].KeySignature); - Assert.AreEqual(KeySignatureType.Minor, score.MasterBars[24].KeySignatureType); - Assert.AreEqual(KeySignature.G, score.MasterBars[25].KeySignature); - Assert.AreEqual(KeySignatureType.Minor, score.MasterBars[25].KeySignatureType); - Assert.AreEqual(KeySignature.D, score.MasterBars[26].KeySignature); - Assert.AreEqual(KeySignatureType.Minor, score.MasterBars[26].KeySignatureType); - Assert.AreEqual(KeySignature.A, score.MasterBars[27].KeySignature); - Assert.AreEqual(KeySignatureType.Minor, score.MasterBars[27].KeySignatureType); - Assert.AreEqual(KeySignature.E, score.MasterBars[28].KeySignature); - Assert.AreEqual(KeySignatureType.Minor, score.MasterBars[28].KeySignatureType); - Assert.AreEqual(KeySignature.B, score.MasterBars[29].KeySignature); - Assert.AreEqual(KeySignatureType.Minor, score.MasterBars[29].KeySignatureType); - Assert.AreEqual(KeySignature.FSharp, score.MasterBars[30].KeySignature); - Assert.AreEqual(KeySignatureType.Minor, score.MasterBars[30].KeySignatureType); - Assert.AreEqual(KeySignature.CSharp, score.MasterBars[31].KeySignature); - Assert.AreEqual(KeySignatureType.Minor, score.MasterBars[31].KeySignatureType); - } - - protected void CheckColors(Score score) - { - Assert.AreEqual("Red", score.Tracks[0].Name); - Assert.AreEqual("#FF0000", score.Tracks[0].Color.RGBA); - Assert.AreEqual("Green", score.Tracks[1].Name); - Assert.AreEqual("#00FF00", score.Tracks[1].Color.RGBA); - Assert.AreEqual("Yellow", score.Tracks[2].Name); - Assert.AreEqual("#FFFF00", score.Tracks[2].Color.RGBA); - Assert.AreEqual("Blue", score.Tracks[3].Name); - Assert.AreEqual("#0000FF", score.Tracks[3].Color.RGBA); - } - - protected void CheckChords(Score score) - { - var track = score.Tracks[0]; - var staff = track.Staves[0]; - Assert.AreEqual(8, staff.Chords.Count); - - CheckChord(new Chord - { - Name = "C", - FirstFret = 1, - Strings = new FastList { 0, 1, 0, 2, 3, -1 } - }, track.Staves[0].Bars[0].Voices[0].Beats[0].Chord); - CheckChord(new Chord - { - Name = "Cm", - FirstFret = 1, - Strings = new FastList { -1, -1, 0, 1, 3, -1 } - }, track.Staves[0].Bars[0].Voices[0].Beats[1].Chord); - CheckChord(new Chord - { - Name = "C", - FirstFret = 1, - Strings = new FastList { 3, 5, 5, 5, 3, -1 }, - BarreFrets = new FastList { 3 } - }, track.Staves[0].Bars[0].Voices[0].Beats[2].Chord); - - CheckChord(new Chord - { - Name = "Cm", - FirstFret = 1, - Strings = new FastList { 3, 4, 5, 5, 3, -1 }, - BarreFrets = new FastList { 3 } - }, track.Staves[0].Bars[0].Voices[0].Beats[3].Chord); - - CheckChord(new Chord - { - Name = "D", - FirstFret = 1, - Strings = new FastList { 2, 3, 2, 0, -1, -1 }, - BarreFrets = new FastList { 2 } - }, track.Staves[0].Bars[1].Voices[0].Beats[0].Chord); - CheckChord(new Chord - { - Name = "Dm", - FirstFret = 1, - Strings = new FastList { 1, 3, 2, 0, -1, -1 } - }, track.Staves[0].Bars[1].Voices[0].Beats[1].Chord); - CheckChord(new Chord - { - Name = "D", - FirstFret = 5, - Strings = new FastList { 5, 7, 7, 7, 5, -1 }, - BarreFrets = new FastList { 5 } - }, track.Staves[0].Bars[1].Voices[0].Beats[2].Chord); - CheckChord(new Chord - { - Name = "Dm", - FirstFret = 5, - Strings = new FastList { 5, 6, 7, 7, 5, -1 }, - BarreFrets = new FastList { 5 } - }, track.Staves[0].Bars[1].Voices[0].Beats[3].Chord); - } - - private void CheckChord(Chord expected, Chord actual) - { - Assert.AreEqual(expected.Name, actual.Name); - Assert.AreEqual(expected.FirstFret, actual.FirstFret); - Assert.AreEqual(expected.Strings.Count, actual.Strings.Count); - Assert.AreEqual(string.Join(",", expected.Strings), string.Join(",", actual.Strings)); - Assert.AreEqual(string.Join(",", expected.BarreFrets), string.Join(",", actual.BarreFrets)); - } - - #endregion - - protected void Render(Score score, [CallerFilePath] string callerFile = null, [CallerMemberName] string caller = null) - { - if (caller == null) - { - throw new ArgumentNullException("caller", "svg rendering failed because caller info was missing"); - } -#if !PHASE - var settings = Settings.Defaults; - settings.Engine = "svg"; - var renderer = new ScoreRenderer(settings); - for (int i = 0; i < score.Tracks.Count; i++) - { - Track track = score.Tracks[i]; - // render track - Logger.Info("Test", $"Rendering track {i + 1} - {track.Name}"); - var totalWidth = 0; - var totalHeight = 0; - var merged = new StringBuilder(); - var currentY = 0f; - renderer.PartialRenderFinished += r => - { - var subSvg = r.RenderResult.ToString() - .Replace("width=", "x=\"0px\" y=\"" + ((int)currentY) + "\" width="); - merged.Append(subSvg); - currentY += r.Height; - }; - renderer.RenderFinished += r => - { - totalWidth = (int)r.TotalWidth; - totalHeight = (int)r.TotalHeight; - }; - renderer.Render(score, new[] { track.Index }); - - var final = new StringBuilder(); - final.Append(""); - final.Append(merged.ToString()); - final.Append(""); - var svg = merged.ToString(); - - var dirName = Path.GetFileNameWithoutExtension(callerFile); - var path = Path.Combine(dirName, caller + "-" + i + ".svg"); - if (!Directory.Exists(dirName)) - { - Directory.CreateDirectory(dirName); - } - File.WriteAllText(path, svg); - } -#endif - } - } -} diff --git a/Source/AlphaTab.Test/Importer/GpxImporterTest.cs b/Source/AlphaTab.Test/Importer/GpxImporterTest.cs deleted file mode 100644 index 0e6542d78..000000000 --- a/Source/AlphaTab.Test/Importer/GpxImporterTest.cs +++ /dev/null @@ -1,375 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using System.IO; -using AlphaTab.Importer; -using AlphaTab.IO; -using AlphaTab.Model; -using AlphaTab.Util; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace AlphaTab.Test.Importer -{ - [TestClass] - public class GpxImporterTest : GpImporterTestBase - { - internal byte[] Load(string name) - { - const string path = "TestFiles/"; - return TestPlatform.LoadFile(path + name); - } - - internal GpxImporter PrepareGpxImporterWithFile(string name) - { - return PrepareGpxImporterWithBytes(Load(name)); - } - - internal GpxImporter PrepareGpxImporterWithBytes(byte[] buffer) - { - var readerBase = new GpxImporter(); - readerBase.Init(ByteBuffer.FromBuffer(buffer)); - return readerBase; - } - - [TestMethod] - public void TestFileSystemCompressed() - { - GpxFileSystem fileSystem = new GpxFileSystem(); - fileSystem.Load(ByteBuffer.FromBuffer(Load("GuitarPro6/Compressed.gpx"))); - - string[] names = {"score.gpif", "misc.xml", "BinaryStylesheet", "PartConfiguration", "LayoutConfiguration"}; - int[] sizes = {8488, 130, 12204, 20, 12}; - - for (int i = 0; i < fileSystem.Files.Count; i++) - { - var file = fileSystem.Files[i]; - Logger.Info("Test", $"{file.FileName} - {file.FileSize}"); - Assert.AreEqual(names[i], file.FileName); - Assert.AreEqual(sizes[i], file.FileSize); - } - } - - [TestMethod] - public void TestScoreInfo() - { - var reader = PrepareGpxImporterWithFile("GuitarPro6/Test01.gpx"); - var score = reader.ReadScore(); - - Assert.AreEqual("Title", score.Title); - Assert.AreEqual("Subtitle", score.SubTitle); - Assert.AreEqual("Artist", score.Artist); - Assert.AreEqual("Album", score.Album); - Assert.AreEqual("Words", score.Words); - Assert.AreEqual("Music", score.Music); - Assert.AreEqual("Copyright", score.Copyright); - Assert.AreEqual("Tab", score.Tab); - Assert.AreEqual("Instructions", score.Instructions); - Assert.AreEqual("Notice1\nNotice2", score.Notices); - Assert.AreEqual(5, score.MasterBars.Count); - Assert.AreEqual(2, score.Tracks.Count); - Assert.AreEqual("Track 1", score.Tracks[0].Name); - Assert.AreEqual("Track 2", score.Tracks[1].Name); - Render(score); - } - - [TestMethod] - public void TestNotes() - { - var reader = PrepareGpxImporterWithFile("GuitarPro6/Test02.gpx"); - var score = reader.ReadScore(); - CheckTest02Score(score); - Render(score); - } - - [TestMethod] - public void TestTimeSignatures() - { - var reader = PrepareGpxImporterWithFile("GuitarPro6/Test03.gpx"); - var score = reader.ReadScore(); - - CheckTest03Score(score); - Render(score); - } - - [TestMethod] - public void TestDead() - { - var reader = PrepareGpxImporterWithFile("GuitarPro6/TestDead.gpx"); - var score = reader.ReadScore(); - CheckDead(score); - Render(score); - } - - [TestMethod] - public void TestGrace() - { - var reader = PrepareGpxImporterWithFile("GuitarPro6/TestGrace.gpx"); - var score = reader.ReadScore(); - CheckGrace(score); - Render(score); - } - - [TestMethod] - public void TestAccentuation() - { - var reader = PrepareGpxImporterWithFile("GuitarPro6/TestAccentuations.gpx"); - var score = reader.ReadScore(); - CheckAccentuation(score, true); - Render(score); - } - - [TestMethod] - public void TestHarmonics() - { - var reader = PrepareGpxImporterWithFile("GuitarPro6/TestHarmonics.gpx"); - var score = reader.ReadScore(); - CheckHarmonics(score); - Render(score); - } - - [TestMethod] - public void TestHammer() - { - var reader = PrepareGpxImporterWithFile("GuitarPro6/TestHammer.gpx"); - var score = reader.ReadScore(); - CheckHammer(score); - Render(score); - } - - [TestMethod] - public void TestBend() - { - var reader = PrepareGpxImporterWithFile("GuitarPro6/TestBends.gpx"); - var score = reader.ReadScore(); - - Assert.AreEqual(2, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].BendPoints.Count); - - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].BendPoints[0].Offset); - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].BendPoints[0].Value); - - Assert.AreEqual(60, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].BendPoints[1].Offset); - Assert.AreEqual(4, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0].BendPoints[1].Value); - - Assert.AreEqual(2, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].BendPoints.Count); - - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].BendPoints[0].Offset); - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].BendPoints[0].Value); - - Assert.AreEqual(60, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].BendPoints[1].Offset); - Assert.AreEqual(4, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Notes[0].BendPoints[1].Value); - - Assert.AreEqual(3, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Notes[0].BendPoints.Count); - - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Notes[0].BendPoints[0].Offset); - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Notes[0].BendPoints[0].Value); - - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Notes[0].BendPoints[0].Offset); - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Notes[0].BendPoints[0].Value); - - Assert.AreEqual(30, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Notes[0].BendPoints[1].Offset); - Assert.AreEqual(12, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Notes[0].BendPoints[1].Value); - - Assert.AreEqual(60, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Notes[0].BendPoints[2].Offset); - Assert.AreEqual(6, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Notes[0].BendPoints[2].Value); - Render(score); - } - - [TestMethod] - public void TestTremolo() - { - var reader = PrepareGpxImporterWithFile("GuitarPro6/TestTremolo.gpx"); - var score = reader.ReadScore(); - - Assert.AreEqual(3, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].WhammyBarPoints.Count); - - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].WhammyBarPoints[0].Offset); - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].WhammyBarPoints[0].Value); - - Assert.AreEqual(30, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].WhammyBarPoints[1].Offset); - Assert.AreEqual(-4, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].WhammyBarPoints[1].Value); - - Assert.AreEqual(60, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].WhammyBarPoints[2].Offset); - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].WhammyBarPoints[2].Value); - - - Assert.AreEqual(2, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].WhammyBarPoints.Count); - - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].WhammyBarPoints[0].Offset); - Assert.AreEqual(-4, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].WhammyBarPoints[0].Value); - - Assert.AreEqual(60, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].WhammyBarPoints[1].Offset); - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].WhammyBarPoints[1].Value); - - - Assert.AreEqual(3, score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[0].WhammyBarPoints.Count); - - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[0].WhammyBarPoints[0].Offset); - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[0].WhammyBarPoints[0].Value); - - Assert.AreEqual(30, score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[0].WhammyBarPoints[1].Offset); - Assert.AreEqual(-4, score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[0].WhammyBarPoints[1].Value); - - Assert.AreEqual(60, score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[0].WhammyBarPoints[2].Offset); - Assert.AreEqual(-4, score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[0].WhammyBarPoints[2].Value); - - Assert.AreEqual(4, score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[0].WhammyBarPoints.Count); - - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[0].WhammyBarPoints[0].Offset); - Assert.AreEqual(-4, score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[0].WhammyBarPoints[0].Value); - - Assert.AreEqual(15, score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[0].WhammyBarPoints[1].Offset); - Assert.AreEqual(-12, score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[0].WhammyBarPoints[1].Value); - - Assert.AreEqual(30, score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[0].WhammyBarPoints[2].Offset); - Assert.AreEqual(-12, score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[0].WhammyBarPoints[2].Value); - - Assert.AreEqual(45, score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[0].WhammyBarPoints[3].Offset); - Assert.AreEqual(0, score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[0].WhammyBarPoints[3].Value); - - Render(score); - } - - [TestMethod] - public void TestSlides() - { - var reader = PrepareGpxImporterWithFile("GuitarPro6/TestSlides.gpx"); - var score = reader.ReadScore(); - CheckSlides(score); - Render(score); - } - - [TestMethod] - public void TestVibrato() - { - var reader = PrepareGpxImporterWithFile("GuitarPro6/TestVibrato.gpx"); - var score = reader.ReadScore(); - CheckVibrato(score, true); - Render(score); - } - - [TestMethod] - public void TestTrills() - { - var reader = PrepareGpxImporterWithFile("GuitarPro6/TestTrills.gpx"); - var score = reader.ReadScore(); - CheckTrills(score); - Render(score); - } - - [TestMethod] - public void TestOtherEffects() - { - var reader = PrepareGpxImporterWithFile("GuitarPro6/TestOtherEffects.gpx"); - var score = reader.ReadScore(); - CheckOtherEffects(score, true /* GPX doesn't support instrument changes */); - Render(score); - } - - [TestMethod] - public void TestFingering() - { - var reader = PrepareGpxImporterWithFile("GuitarPro6/TestFingering.gpx"); - var score = reader.ReadScore(); - CheckFingering(score); - Render(score); - } - - [TestMethod] - public void TestStroke() - { - var reader = PrepareGpxImporterWithFile("GuitarPro6/TestStrokes.gpx"); - var score = reader.ReadScore(); - CheckStroke(score); - Render(score); - } - - [TestMethod] - public void TestTuplets() - { - var reader = PrepareGpxImporterWithFile("GuitarPro6/TestTuplets.gpx"); - var score = reader.ReadScore(); - CheckTuplets(score); - Render(score); - } - - [TestMethod] - public void TestRanges() - { - var reader = PrepareGpxImporterWithFile("GuitarPro6/TestRanges.gpx"); - var score = reader.ReadScore(); - CheckRanges(score); - Render(score); - } - - [TestMethod] - public void TestEffects() - { - var reader = PrepareGpxImporterWithFile("GuitarPro6/Effects.gpx"); - var score = reader.ReadScore(); - CheckEffects(score); - Render(score); - } - - [TestMethod] - public void TestSerenade() - { - var reader = PrepareGpxImporterWithFile("GuitarPro6/Serenade.gpx"); - var score = reader.ReadScore();// only Check reading - Render(score); - } - - [TestMethod] - public void TestStrings() - { - var reader = PrepareGpxImporterWithFile("GuitarPro6/TestStrings.gpx"); - var score = reader.ReadScore(); - CheckStrings(score); - Render(score); - } - - [TestMethod] - public void TestKeySignatures() - { - var reader = PrepareGpxImporterWithFile("GuitarPro6/TestKeySignatures.gpx"); - var score = reader.ReadScore(); - CheckKeySignatures(score); - Render(score); - } - - [TestMethod] - public void TestChords() - { - var reader = PrepareGpxImporterWithFile("GuitarPro6/TestChords.gpx"); - var score = reader.ReadScore(); - CheckChords(score); - Render(score); - } - - - - [TestMethod] - public void TestColors() - { - var reader = PrepareGpxImporterWithFile("GuitarPro6/Colors.gpx"); - var score = reader.ReadScore(); - - CheckColors(score); - Render(score); - } - } -} diff --git a/Source/AlphaTab.Test/Importer/MusicXmlImporterSamplesTest.cs b/Source/AlphaTab.Test/Importer/MusicXmlImporterSamplesTest.cs deleted file mode 100644 index 9b6c6b923..000000000 --- a/Source/AlphaTab.Test/Importer/MusicXmlImporterSamplesTest.cs +++ /dev/null @@ -1,129 +0,0 @@ -using System.IO; -using System.Linq; -using System.Runtime.CompilerServices; -using AlphaTab.Model; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace AlphaTab.Test.Importer -{ - [TestClass] - [Ignore] - public class MusicXmlImporterSamplesTests : MusicXmlImporterTestBase - { - [TestMethod] - public void Test_ActorPreludeSample() - { - TestReference(renderLayout:"horizontal"); - } - - [TestMethod] - public void Test_BeetAnGeSample() - { - TestReference(); - } - - [TestMethod] - public void Test_Binchois() - { - TestReference(); - } - - [TestMethod] - public void Test_BrahWiMeSample() - { - TestReference(); - } - - [TestMethod] - public void Test_BrookeWestSample() - { - TestReference(); - } - - [TestMethod] - public void Test_Chant() - { - TestReference(); - } - - [TestMethod] - public void Test_DebuMandSample() - { - TestReference(); - } - - [TestMethod] - public void Test_Dichterliebe01() - { - TestReference(); - } - - [TestMethod] - public void Test_Echigo() - { - TestReference(); - } - - [TestMethod] - public void Test_FaurReveSample() - { - TestReference(); - } - - [TestMethod] - public void Test_MahlFaGe4Sample() - { - TestReference(); - } - - [TestMethod] - public void Test_MozaChloSample() - { - TestReference(); - } - - [TestMethod] - public void Test_MozartPianoSonata() - { - TestReference(); - } - - [TestMethod] - public void Test_MozartTrio() - { - TestReference(); - } - - [TestMethod] - public void Test_MozaVeilSample() - { - TestReference(); - } - - [TestMethod] - public void Test_Saltarello() - { - TestReference(); - } - - [TestMethod] - public void Test_SchbAvMaSample() - { - TestReference(); - } - - [TestMethod] - public void Test_Telemann() - { - TestReference(); - } - - private Score TestReference([CallerMemberName] string caller = null, string renderLayout = "page", bool renderAllTracks = true) - { - var fileId = caller.Split('_')[1]; - const string path = "TestFiles/MusicXmlSamples"; - var file = path + fileId + ".xml"; - return TestReferenceFile(file, renderLayout, renderAllTracks); - } - } -} diff --git a/Source/AlphaTab.Test/Importer/MusicXmlImporterTestBase.cs b/Source/AlphaTab.Test/Importer/MusicXmlImporterTestBase.cs deleted file mode 100644 index cbde791d3..000000000 --- a/Source/AlphaTab.Test/Importer/MusicXmlImporterTestBase.cs +++ /dev/null @@ -1,394 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Drawing; -using System.Drawing.Imaging; -using System.IO; -using System.Linq; -using System.Linq.Expressions; -using System.Runtime.CompilerServices; -using AlphaTab.Collections; -using AlphaTab.Importer; -using AlphaTab.IO; -using AlphaTab.Model; -using AlphaTab.Rendering; -using AlphaTab.Rendering.Layout; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace AlphaTab.Test.Importer -{ - public class MusicXmlImporterTestBase - { - internal MusicXmlImporter PrepareImporterWithBytes(byte[] buffer) - { - var readerBase = new MusicXmlImporter(); - readerBase.Init(ByteBuffer.FromBuffer(buffer)); - return readerBase; - } - - protected Score TestReferenceFile(string file, string renderLayout = "page", bool renderAllTracks = false) - { - var gpxImporter = new GpxImporter(); - - try - { - var buffer = TestPlatform.LoadFile(file); - var importer = PrepareImporterWithBytes(buffer); - var score = importer.ReadScore(); - var reference = TestPlatform.ChangeExtension(file, ".gpx"); - -#if !PHASE - if (renderAllTracks) - { - Render(score.Tracks.ToArray(), Path.ChangeExtension(file, ".all.png"), renderLayout); - } - else - { - foreach (var track in score.Tracks) - { - Render(new[] { track }, Path.ChangeExtension(file, "." + track.Index + ".png"), renderLayout); - } - } - - if (!File.Exists(reference)) - { - Assert.Inconclusive(); - } -#endif - - gpxImporter.Init(ByteBuffer.FromBuffer(TestPlatform.LoadFile(reference))); - var referenceScore = gpxImporter.ReadScore(); - AreEqual(referenceScore, score); - - return score; - } - catch (UnsupportedFormatException e) - { - Assert.Fail("Failed to load file {0}: {1}", file, e); - throw; - } - } - - protected string GetHierarchy(object node) - { - var note = node as Note; - if (note != null) - { - return GetHierarchy(note.Beat) + "Note[" + note.Index + "]"; - } - - var beat = node as Beat; - if (beat != null) - { - return GetHierarchy(beat.Voice) + "Beat[" + beat.Index + "]"; - } - - var voice = node as Voice; - if (voice != null) - { - return GetHierarchy(voice.Bar) + "Voice[" + voice.Index + "]"; - } - - var bar = node as Bar; - if (bar != null) - { - return GetHierarchy(bar.Staff) + "Bar[" + bar.Index + "]"; - } - - var staff = node as Staff; - if (staff != null) - { - return GetHierarchy(staff.Track) + "Staff[" + staff.Index + "]"; - } - - var track = node as Track; - if (track != null) - { - return "Track[" + track.Index + "]"; - } - - var mb = node as MasterBar; - if (mb != null) - { - return "MasterBar[" + mb.Index + "]"; - } - - return node.GetType().Name; - } - - protected void AreEqual(Score expected, Score actual) - { - Assert.AreEqual(expected.Album, actual.Album, "Mismatch on Album"); - Assert.AreEqual(expected.Artist, actual.Artist, "Mismatch on Artist"); - Assert.AreEqual(expected.Copyright, actual.Copyright, "Mismatch on Copyright"); - Assert.AreEqual(expected.Instructions, actual.Instructions, "Mismatch on Instructions"); - Assert.AreEqual(expected.Music, actual.Music, "Mismatch on Music"); - Assert.AreEqual(expected.Notices, actual.Notices, "Mismatch on Notices"); - Assert.AreEqual(expected.SubTitle, actual.SubTitle, "Mismatch on SubTitle"); - Assert.AreEqual(expected.Title, actual.Title, "Mismatch on Title"); - Assert.AreEqual(expected.Words, actual.Words, "Mismatch on Words"); - Assert.AreEqual(expected.Tab, actual.Tab, "Mismatch on Tab"); - Assert.AreEqual(expected.Tempo, actual.Tempo, "Mismatch on Tempo"); - Assert.AreEqual(expected.TempoLabel, actual.TempoLabel, "Mismatch on TempoLabel"); - Assert.AreEqual(expected.MasterBars.Count, actual.MasterBars.Count, "Mismatch on MasterBars.Count"); - for (int i = 0; i < expected.MasterBars.Count; i++) - { - AreEqual(expected.MasterBars[i], actual.MasterBars[i]); - } - - Assert.AreEqual(expected.Tracks.Count, actual.Tracks.Count, "Mismatch on Tracks.Count"); - for (int i = 0; i < expected.Tracks.Count; i++) - { - AreEqual(expected.Tracks[i], actual.Tracks[i]); - } - } - - protected void AreEqual(Track expected, Track actual) - { - Assert.AreEqual(expected.Index, actual.Index, "Mismatch on Index"); - Assert.AreEqual(expected.Name, actual.Name, "Mismatch on Name"); - //Assert.AreEqual(expected.ShortName, actual.ShortName, "Mismatch on ShortName"); - //Assert.AreEqual(expected.Color.Raw, actual.Color.Raw, "Mismatch on Color.Raw"); - AreEqual(expected.PlaybackInfo, actual.PlaybackInfo); - Assert.AreEqual(expected.Staves.Count, actual.Staves.Count, "Mismatch on Staves.Count"); - for (int i = 0; i < expected.Staves.Count; i++) - { - AreEqual(expected.Staves[i], actual.Staves[i]); - } - } - - protected void AreEqual(Staff expected, Staff actual) - { - Assert.AreEqual(expected.Capo, actual.Capo, "Mismatch on Capo"); - Assert.AreEqual(expected.StaffKind, actual.StaffKind, "Mismatch on StaffKind"); - Assert.AreEqual(string.Join(",", expected.Tuning), string.Join(",", actual.Tuning)); - Assert.AreEqual(expected.Tuning.Length, actual.Tuning.Length, "Mismatch on Tuning.Length"); - Assert.AreEqual(expected.Index, actual.Index, "Mismatch on Index"); - Assert.AreEqual(expected.Bars.Count, actual.Bars.Count, "Mismatch on Bars.Count"); - for (int i = 0; i < expected.Bars.Count; i++) - { - AreEqual(expected.Bars[i], actual.Bars[i]); - } - } - - protected void AreEqual(Bar expected, Bar actual) - { - Assert.AreEqual(expected.Index, actual.Index, "Mismatch on Index"); - Assert.AreEqual(expected.Clef, actual.Clef, "Mismatch on Clef"); - Assert.AreEqual(expected.ClefOttava, actual.ClefOttava, "Mismatch on ClefOttavia"); - //Assert.AreEqual(expected.Voices.Count, actual.Voices.Count, "Mismatch on Voices.Count"); - for (int i = 0; i < Math.Min(expected.Voices.Count, actual.Voices.Count); i++) - { - AreEqual(expected.Voices[i], actual.Voices[i]); - } - } - - protected void AreEqual(Voice expected, Voice actual) - { - Assert.AreEqual(expected.Index, actual.Index, "Mismatch on Index"); - Assert.AreEqual(expected.Beats.Count, actual.Beats.Count, "Mismatch on Beats.Count"); - for (int i = 0; i < expected.Beats.Count; i++) - { - AreEqual(expected.Beats[i], actual.Beats[i]); - } - } - - protected void AreEqual(Beat expected, Beat actual) - { - Assert.AreEqual(expected.Index, actual.Index, "Mismatch on Index"); - Assert.AreEqual(expected.IsEmpty, actual.IsEmpty, "Mismatch on IsEmpty"); - Assert.AreEqual(expected.IsRest, actual.IsRest, "Mismatch on IsRest"); - Assert.AreEqual(expected.Dots, actual.Dots, "Mismatch on Dots"); - Assert.AreEqual(expected.FadeIn, actual.FadeIn, "Mismatch on FadeIn"); - Assert.AreEqual(expected.IsLegatoOrigin, actual.IsLegatoOrigin, "Mismatch on IsLegatoOrigin"); - if (expected.Lyrics == null) - { - Assert.IsNull(actual.Lyrics); - } - else - { - Assert.AreEqual(string.Join(" ", expected.Lyrics), string.Join(" ", actual.Lyrics)); - } - Assert.AreEqual(expected.Pop, actual.Pop, "Mismatch on Pop"); - Assert.AreEqual(expected.HasChord, actual.HasChord, "Mismatch on HasChord"); - Assert.AreEqual(expected.HasRasgueado, actual.HasRasgueado, "Mismatch on HasRasgueado"); - Assert.AreEqual(expected.Slap, actual.Tap); - Assert.AreEqual(expected.Text, actual.Text, "Mismatch on Text"); - Assert.AreEqual(expected.BrushType, actual.BrushType, "Mismatch on BrushType"); - Assert.AreEqual(expected.BrushDuration, actual.BrushDuration, "Mismatch on BrushDuration"); - Assert.AreEqual(expected.TupletDenominator, actual.TupletDenominator, "Mismatch on TupletDenominator"); - Assert.AreEqual(expected.TupletNumerator, actual.TupletNumerator, "Mismatch on TupletNumerator"); - AreEqual(expected.WhammyBarPoints, actual.WhammyBarPoints); - Assert.AreEqual(expected.Vibrato, actual.Vibrato, "Mismatch on Vibrato"); - if (expected.HasChord) - { - AreEqual(expected.Chord, actual.Chord); - } - Assert.AreEqual(expected.GraceType, actual.GraceType, "Mismatch on GraceType"); - Assert.AreEqual(expected.PickStroke, actual.PickStroke, "Mismatch on PickStroke"); - Assert.AreEqual(expected.TremoloSpeed, actual.TremoloSpeed, "Mismatch on TremoloSpeed"); - Assert.AreEqual(expected.Crescendo, actual.Crescendo, "Mismatch on Crescendo"); - Assert.AreEqual(expected.PlaybackStart, actual.PlaybackStart, "Mismatch on Start"); - Assert.AreEqual(expected.DisplayStart, actual.DisplayStart, "Mismatch on Start"); - //Assert.AreEqual(expected.Dynamic, actual.Dynamic, "Mismatch on Dynamic"); - Assert.AreEqual(expected.InvertBeamDirection, actual.InvertBeamDirection, "Mismatch on InvertBeamDirection"); - - Assert.AreEqual(expected.Notes.Count, actual.Notes.Count, "Mismatch on Notes.Count"); - for (int i = 0; i < expected.Notes.Count; i++) - { - AreEqual(expected.Notes[i], actual.Notes[i]); - } - } - - protected void AreEqual(Note expected, Note actual) - { - Assert.AreEqual(expected.Index, actual.Index, "Mismatch on Index"); - Assert.AreEqual(expected.Accentuated, actual.Accentuated, "Mismatch on Accentuated"); - AreEqual(expected.BendPoints, actual.BendPoints); - Assert.AreEqual(expected.IsStringed, actual.IsStringed, "Mismatch on IsStringed"); - if (actual.IsStringed) - { - Assert.AreEqual(expected.Fret, actual.Fret, "Mismatch on Fret"); - Assert.AreEqual(expected.String, actual.String, "Mismatch on String"); - } - Assert.AreEqual(expected.IsPiano, actual.IsPiano, "Mismatch on IsPiano"); - if (actual.IsPiano) - { - Assert.AreEqual(expected.Octave, actual.Octave, "Mismatch on Octave"); - Assert.AreEqual(expected.Tone, actual.Tone, "Mismatch on Tone"); - } - Assert.AreEqual(expected.Variation, actual.Variation, "Mismatch on Variation"); - Assert.AreEqual(expected.Element, actual.Element, "Mismatch on Element"); - Assert.AreEqual(expected.IsHammerPullOrigin, actual.IsHammerPullOrigin, "Mismatch on IsHammerPullOrigin"); - Assert.AreEqual(expected.HarmonicType, actual.HarmonicType, "Mismatch on HarmonicType"); - Assert.AreEqual(expected.HarmonicValue, actual.HarmonicValue, "Mismatch on HarmonicValue"); - Assert.AreEqual(expected.IsGhost, actual.IsGhost, "Mismatch on IsGhost"); - Assert.AreEqual(expected.IsLetRing, actual.IsLetRing, "Mismatch on IsLetRing"); - Assert.AreEqual(expected.IsPalmMute, actual.IsPalmMute, "Mismatch on IsPalmMute"); - Assert.AreEqual(expected.IsDead, actual.IsDead, "Mismatch on IsDead"); - Assert.AreEqual(expected.IsStaccato, actual.IsStaccato, "Mismatch on IsStaccato"); - Assert.AreEqual(expected.SlideType, actual.SlideType, "Mismatch on SlideType"); - Assert.AreEqual(expected.Vibrato, actual.Vibrato, "Mismatch on Vibrato"); - Assert.AreEqual(expected.IsTieDestination, actual.IsTieDestination, "Mismatch on IsTieDestination"); - Assert.AreEqual(expected.IsTieOrigin, actual.IsTieOrigin, "Mismatch on IsTieOrigin"); - Assert.AreEqual(expected.LeftHandFinger, actual.LeftHandFinger, "Mismatch on LeftHandFinger"); - Assert.AreEqual(expected.IsFingering, actual.IsFingering, "Mismatch on IsFingering"); - Assert.AreEqual(expected.TrillValue, actual.TrillValue, "Mismatch on TrillValue"); - Assert.AreEqual(expected.TrillSpeed, actual.TrillSpeed, "Mismatch on TrillSpeed"); - Assert.AreEqual(expected.DurationPercent, actual.DurationPercent, "Mismatch on DurationPercent"); - //Assert.AreEqual(expected.AccidentalMode, actual.AccidentalMode, "Mismatch on AccidentalMode"); - Assert.AreEqual(expected.Dynamic, actual.Dynamic, "Mismatch on Dynamic"); - Assert.AreEqual(expected.RealValue, actual.RealValue, "Mismatch on RealValue"); - } - - protected void AreEqual(Chord expected, Chord actual) - { - Assert.AreEqual(expected == null, actual == null); - if (expected != null) - { - //Assert.AreEqual(expected.Name, actual.Name, "Mismatch on Name"); - } - } - - protected void AreEqual(FastList expected, FastList actual) - { - Assert.AreEqual(expected.Count, actual.Count, "Mismatch on Count"); - for (int i = 0; i < expected.Count; i++) - { - Assert.AreEqual(expected[i].Value, actual[i].Value); - Assert.AreEqual(expected[i].Offset, actual[i].Offset); - } - } - - protected void AreEqual(PlaybackInformation expected, PlaybackInformation actual) - { - Assert.AreEqual(expected.Volume, actual.Volume, "Mismatch on Volume"); - Assert.AreEqual(expected.Balance, actual.Balance, "Mismatch on Balance"); - //Assert.AreEqual(expected.Port, actual.Port, "Mismatch on Port"); - Assert.AreEqual(expected.Program, actual.Program, "Mismatch on Program"); - //Assert.AreEqual(expected.PrimaryChannel, actual.PrimaryChannel, "Mismatch on PrimaryChannel"); - //Assert.AreEqual(expected.SecondaryChannel, actual.SecondaryChannel, "Mismatch on SecondaryChannel"); - Assert.AreEqual(expected.IsMute, actual.IsMute, "Mismatch on IsMute"); - Assert.AreEqual(expected.IsSolo, actual.IsSolo, "Mismatch on IsSolo"); - } - - protected void AreEqual(MasterBar expected, MasterBar actual) - { - Assert.AreEqual(expected.AlternateEndings, actual.AlternateEndings, "Mismatch on AlternateEndings"); - Assert.AreEqual(expected.Index, actual.Index, "Mismatch on Index"); - Assert.AreEqual(expected.KeySignature, actual.KeySignature, "Mismatch on KeySignature"); - Assert.AreEqual(expected.KeySignatureType, actual.KeySignatureType, "Mismatch on KeySignatureType"); - Assert.AreEqual(expected.IsDoubleBar, actual.IsDoubleBar, "Mismatch on IsDoubleBar"); - Assert.AreEqual(expected.IsRepeatStart, actual.IsRepeatStart, "Mismatch on IsRepeatStart"); - Assert.AreEqual(expected.RepeatCount, actual.RepeatCount, "Mismatch on RepeatCount"); - Assert.AreEqual(expected.TimeSignatureNumerator, actual.TimeSignatureNumerator, "Mismatch on TimeSignatureNumerator"); - Assert.AreEqual(expected.TimeSignatureDenominator, actual.TimeSignatureDenominator, "Mismatch on TimeSignatureDenominator"); - Assert.AreEqual(expected.TripletFeel, actual.TripletFeel, "Mismatch on TripletFeel"); - AreEqual(expected.Section, actual.Section); - Assert.AreEqual(expected.Start, actual.Start, "Mismatch on Start"); - } - - protected void AreEqual(Section expected, Section actual) - { - Assert.AreEqual(expected == null, actual == null); - if (expected != null) - { - Assert.AreEqual(expected.Text, actual.Text, "Mismatch on Text"); - Assert.AreEqual(expected.Marker, actual.Marker, "Mismatch on Marker"); - } - } - -#if !PHASE - protected void Render(Track[] tracks, string path, string renderLayout) - { - var settings = Settings.Defaults; - settings.Engine = "gdi"; - settings.Layout = new LayoutSettings - { - Mode = renderLayout - }; - settings.Width = 800; - var renderer = new ScoreRenderer(settings); - var images = new FastList(); - var totalWidth = 0; - var totalHeight = 0; - renderer.Error += (s, exception) => - { - if (exception != null) - { - throw exception; - } - else - { - throw new Exception(s); - } - }; - renderer.PartialRenderFinished += r => - { - images.Add((Image)r.RenderResult); - }; - renderer.RenderFinished += r => - { - totalWidth = (int)r.TotalWidth; - totalHeight = (int)r.TotalHeight; - }; - renderer.Render(tracks[0].Score, tracks.Select(t => t.Index).ToArray()); - using (var bmp = new Bitmap(totalWidth, totalHeight)) - { - int y = 0; - using (var g = Graphics.FromImage(bmp)) - { - g.Clear(Color.White); - foreach (var image in images) - { - g.DrawImage(image, new Rectangle(0, y, image.Width, image.Height), - new Rectangle(0, 0, image.Width, image.Height), GraphicsUnit.Pixel); - y += image.Height; - } - } - bmp.Save(path, ImageFormat.Png); - } - } -#endif - } -} diff --git a/Source/AlphaTab.Test/Importer/MusicXmlImporterTestSuiteTests.cs b/Source/AlphaTab.Test/Importer/MusicXmlImporterTestSuiteTests.cs deleted file mode 100644 index ef4e5dc9b..000000000 --- a/Source/AlphaTab.Test/Importer/MusicXmlImporterTestSuiteTests.cs +++ /dev/null @@ -1,789 +0,0 @@ -using System.IO; -using System.Linq; -using System.Runtime.CompilerServices; -using AlphaTab.Model; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace AlphaTab.Test.Importer -{ - [TestClass] - [Ignore] - public class MusicXmlImporterTestSuiteTests : MusicXmlImporterTestBase - { - [TestMethod] - public void Test_01a_Pitches_Pitches() - { - TestReference(); - } - - [TestMethod] - public void Test_01b_Pitches_Intervals() - { - TestReference(renderLayout: "horizontal"); - } - - [TestMethod] - public void Test_01c_Pitches_NoVoiceElement() - { - TestReference(); - } - - [TestMethod] - public void Test_01d_Pitches_Microtones() - { - TestReference(); - } - - [TestMethod] - public void Test_01e_Pitches_ParenthesizedAccidentals() - { - TestReference(); - } - - [TestMethod] - public void Test_01f_Pitches_ParenthesizedMicrotoneAccidentals() - { - TestReference(); - } - - [TestMethod] - public void Test_02a_Rests_Durations() - { - TestReference(); - } - - [TestMethod] - public void Test_02b_Rests_PitchedRests() - { - TestReference(); - } - - [TestMethod] - public void Test_02c_Rests_MultiMeasureRests() - { - TestReference(); - } - - [TestMethod] - public void Test_02d_Rests_Multimeasure_TimeSignatures() - { - TestReference(); - } - - [TestMethod] - public void Test_02e_Rests_NoType() - { - TestReference(); - } - - [TestMethod] - public void Test_03a_Rhythm_Durations() - { - TestReference(); - } - - [TestMethod] - public void Test_03b_Rhythm_Backup() - { - TestReference(); - } - - [TestMethod] - public void Test_03c_Rhythm_DivisionChange() - { - TestReference(); - } - - [TestMethod] - public void Test_03d_Rhythm_DottedDurations_Factors() - { - TestReference(); - } - - [TestMethod] - public void Test_11a_TimeSignatures() - { - var score = TestReference(); - Assert.IsTrue(score.MasterBars[0].TimeSignatureCommon); - Assert.IsTrue(score.MasterBars[1].TimeSignatureCommon); - } - - [TestMethod] - public void Test_11b_TimeSignatures_NoTime() - { - TestReference(); - } - - [TestMethod] - public void Test_11c_TimeSignatures_CompoundSimple() - { - TestReference(); - } - - [TestMethod] - public void Test_11d_TimeSignatures_CompoundMultiple() - { - TestReference(); - } - - [TestMethod] - public void Test_11e_TimeSignatures_CompoundMixed() - { - TestReference(); - } - - [TestMethod] - public void Test_11f_TimeSignatures_SymbolMeaning() - { - TestReference(); - } - - [TestMethod] - public void Test_11g_TimeSignatures_SingleNumber() - { - TestReference(); - } - - [TestMethod] - public void Test_11h_TimeSignatures_SenzaMisura() - { - TestReference(); - } - - [TestMethod] - public void Test_12a_Clefs() - { - TestReference(); - } - - [TestMethod] - public void Test_12b_Clefs_NoKeyOrClef() - { - TestReference(); - } - - [TestMethod] - public void Test_13a_KeySignatures() - { - TestReference(); - } - - [TestMethod] - public void Test_13b_KeySignatures_ChurchModes() - { - TestReference(); - } - - [TestMethod] - public void Test_13c_KeySignatures_NonTraditional() - { - TestReference(); - } - - [TestMethod] - public void Test_13d_KeySignatures_Microtones() - { - TestReference(); - } - - [TestMethod] - public void Test_14a_StaffDetails_LineChanges() - { - TestReference(); - } - - [TestMethod] - public void Test_21a_Chord_Basic() - { - TestReference(); - } - - [TestMethod] - public void Test_21b_Chords_TwoNotes() - { - TestReference(); - } - - [TestMethod] - public void Test_21c_Chords_ThreeNotesDuration() - { - TestReference(); - } - - [TestMethod] - public void Test_21d_Chords_SchubertStabatMater() - { - TestReference(); - } - - [TestMethod] - public void Test_21e_Chords_PickupMeasures() - { - TestReference(); - } - - [TestMethod] - public void Test_21f_Chord_ElementInBetween() - { - TestReference(); - } - - [TestMethod] - public void Test_22a_Noteheads() - { - TestReference(); - } - - [TestMethod] - public void Test_22b_Staff_Notestyles() - { - TestReference(); - } - - [TestMethod] - public void Test_22c_Noteheads_Chords() - { - TestReference(); - } - - [TestMethod] - public void Test_22d_Parenthesized_Noteheads() - { - TestReference(); - } - - [TestMethod] - public void Test_23a_Tuplets() - { - TestReference(); - } - - [TestMethod] - public void Test_23b_Tuplets_Styles() - { - TestReference(); - } - - [TestMethod] - public void Test_23c_Tuplet_Display_NonStandard() - { - TestReference(); - } - - [TestMethod] - public void Test_23d_Tuplets_Nested() - { - TestReference(); - } - - [TestMethod] - public void Test_23e_Tuplets_Tremolo() - { - TestReference(); - } - - [TestMethod] - public void Test_23f_Tuplets_DurationButNoBracket() - { - TestReference(); - } - - [TestMethod] - public void Test_24a_GraceNotes() - { - TestReference(); - } - - [TestMethod] - public void Test_24b_ChordAsGraceNote() - { - TestReference(); - } - - [TestMethod] - public void Test_24c_GraceNote_MeasureEnd() - { - TestReference(); - } - - [TestMethod] - public void Test_24d_AfterGrace() - { - TestReference(); - } - - [TestMethod] - public void Test_24e_GraceNote_StaffChange() - { - TestReference(); - } - - [TestMethod] - public void Test_24f_GraceNote_Slur() - { - TestReference(); - } - - [TestMethod] - public void Test_31a_Directions() - { - TestReference(); - } - - [TestMethod] - public void Test_31c_MetronomeMarks() - { - TestReference(); - } - - [TestMethod] - public void Test_32a_Notations() - { - TestReference(); - } - - [TestMethod] - public void Test_32b_Articulations_Texts() - { - TestReference(); - } - - [TestMethod] - public void Test_32c_MultipleNotationChildren() - { - TestReference(); - } - - [TestMethod] - public void Test_32d_Arpeggio() - { - TestReference(); - } - - [TestMethod] - public void Test_33a_Spanners() - { - TestReference(); - } - - [TestMethod] - public void Test_33b_Spanners_Tie() - { - TestReference(); - } - - [TestMethod] - public void Test_33c_Spanners_Slurs() - { - TestReference(); - } - - [TestMethod] - public void Test_33d_Spanners_OctaveShifts() - { - TestReference(); - } - - [TestMethod] - public void Test_33e_Spanners_OctaveShifts_InvalidSize() - { - TestReference(); - } - - [TestMethod] - public void Test_33f_Trill_EndingOnGraceNote() - { - TestReference(); - } - - [TestMethod] - public void Test_33g_Slur_ChordedNotes() - { - TestReference(); - } - - [TestMethod] - public void Test_33h_Spanners_Glissando() - { - TestReference(); - } - - [TestMethod] - public void Test_33i_Ties_NotEnded() - { - TestReference(); - } - - [TestMethod] - public void Test_41a_MultiParts_Partorder() - { - TestReference(); - } - - [TestMethod] - public void Test_41b_MultiParts_MoreThan10() - { - TestReference(); - } - - [TestMethod] - public void Test_41c_StaffGroups() - { - TestReference(); - } - - [TestMethod] - public void Test_41d_StaffGroups_Nested() - { - TestReference(); - } - - [TestMethod] - public void Test_41e_StaffGroups_InstrumentNames_Linebroken() - { - TestReference(); - } - - [TestMethod] - public void Test_41f_StaffGroups_Overlapping() - { - TestReference(); - } - - [TestMethod] - public void Test_41g_PartNoId() - { - TestReference(); - } - - [TestMethod] - public void Test_41h_TooManyParts() - { - TestReference(); - } - - [TestMethod] - public void Test_41i_PartNameDisplay_Override() - { - TestReference(); - } - - [TestMethod] - public void Test_42a_MultiVoice_TwoVoicesOnStaff_Lyrics() - { - TestReference(); - } - - [TestMethod] - public void Test_42b_MultiVoice_MidMeasureClefChange() - { - TestReference(); - } - - [TestMethod] - public void Test_43a_PianoStaff() - { - TestReference(); - } - - [TestMethod] - public void Test_43b_MultiStaff_DifferentKeys() - { - TestReference(); - } - - [TestMethod] - public void Test_43c_MultiStaff_DifferentKeysAfterBackup() - { - TestReference(); - } - - [TestMethod] - public void Test_43d_MultiStaff_StaffChange() - { - TestReference(); - } - - [TestMethod] - public void Test_43e_Multistaff_ClefDynamics() - { - TestReference(); - } - - [TestMethod] - public void Test_45a_SimpleRepeat() - { - TestReference(); - } - - [TestMethod] - public void Test_45b_RepeatWithAlternatives() - { - TestReference(); - } - - [TestMethod] - public void Test_45c_RepeatMultipleTimes() - { - TestReference(); - } - - [TestMethod] - public void Test_45d_Repeats_Nested_Alternatives() - { - TestReference(); - } - - [TestMethod] - public void Test_45e_Repeats_Nested_Alternatives() - { - TestReference(); - } - - [TestMethod] - public void Test_45f_Repeats_InvalidEndings() - { - TestReference(); - } - - [TestMethod] - public void Test_45g_Repeats_NotEnded() - { - TestReference(); - } - - [TestMethod] - public void Test_46a_Barlines() - { - TestReference(); - } - - [TestMethod] - public void Test_46b_MidmeasureBarline() - { - TestReference(); - } - - [TestMethod] - public void Test_46c_Midmeasure_Clef() - { - TestReference(); - } - - [TestMethod] - public void Test_46d_PickupMeasure_ImplicitMeasures() - { - TestReference(); - } - - [TestMethod] - public void Test_46e_PickupMeasure_SecondVoiceStartsLater() - { - TestReference(); - } - - [TestMethod] - public void Test_46f_IncompleteMeasures() - { - TestReference(); - } - - [TestMethod] - public void Test_46g_PickupMeasure_Chordnames_FiguredBass() - { - TestReference(); - } - - [TestMethod] - public void Test_51b_Header_Quotes() - { - TestReference(); - } - - [TestMethod] - public void Test_51c_MultipleRights() - { - TestReference(); - } - - [TestMethod] - public void Test_51d_EmptyTitle() - { - TestReference(); - } - - [TestMethod] - public void Test_52a_PageLayout() - { - TestReference(); - } - - [TestMethod] - public void Test_52b_Breaks() - { - TestReference(); - } - - [TestMethod] - public void Test_61a_Lyrics() - { - TestReference(); - } - - [TestMethod] - public void Test_61b_MultipleLyrics() - { - TestReference(); - } - - [TestMethod] - public void Test_61c_Lyrics_Pianostaff() - { - TestReference(); - } - - [TestMethod] - public void Test_61d_Lyrics_Melisma() - { - TestReference(); - } - - [TestMethod] - public void Test_61e_Lyrics_Chords() - { - TestReference(); - } - - [TestMethod] - public void Test_61f_Lyrics_GracedNotes() - { - TestReference(); - } - - [TestMethod] - public void Test_61g_Lyrics_NameNumber() - { - TestReference(); - } - - [TestMethod] - public void Test_61h_Lyrics_BeamsMelismata() - { - TestReference(); - } - - [TestMethod] - public void Test_61i_Lyrics_Chords() - { - TestReference(); - } - - [TestMethod] - public void Test_61j_Lyrics_Elisions() - { - TestReference(); - } - - [TestMethod] - public void Test_61k_Lyrics_SpannersExtenders() - { - TestReference(); - } - - [TestMethod] - public void Test_71a_Chordnames() - { - TestReference(); - } - - [TestMethod] - public void Test_71c_ChordsFrets() - { - TestReference(); - } - - [TestMethod] - public void Test_71d_ChordsFrets_Multistaff() - { - TestReference(); - } - - [TestMethod] - public void Test_71e_TabStaves() - { - TestReference(); - } - - [TestMethod] - public void Test_71f_AllChordTypes() - { - TestReference(); - } - - [TestMethod] - public void Test_71g_MultipleChordnames() - { - TestReference(); - } - - [TestMethod] - public void Test_72a_TransposingInstruments() - { - TestReference(); - } - - [TestMethod] - public void Test_72b_TransposingInstruments_Full() - { - TestReference(); - } - - [TestMethod] - public void Test_72c_TransposingInstruments_Change() - { - TestReference(); - } - - [TestMethod] - public void Test_73a_Percussion() - { - TestReference(); - } - - [TestMethod] - public void Test_74a_FiguredBass() - { - TestReference(); - } - - [TestMethod] - public void Test_75a_AccordionRegistrations() - { - TestReference(); - } - - [TestMethod] - public void Test_99a_Sibelius5_IgnoreBeaming() - { - TestReference(); - } - - [TestMethod] - public void Test_99b_Lyrics_BeamsMelismata_IgnoreBeams() - { - TestReference(); - } - - private Score TestReference([CallerMemberName] string caller = null, string renderLayout = "page") - { -#if !PHASE - var fileId = caller.Split('_')[1]; - const string path = "TestFiles/MusicXmlTestSuite"; - var file = Directory.EnumerateFiles(path, "*.xml").FirstOrDefault(f => f.Contains(fileId)); - return TestReferenceFile(file, renderLayout); -#else - return null; // TODO: how to find testfile on JS -#endif - } - } -} diff --git a/Source/AlphaTab.Test/Model/LyricsTest.cs b/Source/AlphaTab.Test/Model/LyricsTest.cs deleted file mode 100644 index 784a35f33..000000000 --- a/Source/AlphaTab.Test/Model/LyricsTest.cs +++ /dev/null @@ -1,228 +0,0 @@ -using System.IO; -using AlphaTab.Collections; -using AlphaTab.Importer; -using AlphaTab.IO; -using AlphaTab.Model; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace AlphaTab.Test.Model -{ - [TestClass] - public class LyricsTest - { - internal Score LoadLyricsTemplateFile() - { - const string path = "TestFiles/GuitarPro6/LyricsTemplate.gpx"; - var buffer = ByteBuffer.FromBuffer(TestPlatform.LoadFile(path)); - var importer = new GpxImporter(); - importer.Init(buffer); - return importer.ReadScore(); - } - - [TestMethod] - public void TestApplySingleLineFirstBar() - { - var score = LoadLyricsTemplateFile(); - score.Tracks[0].ApplyLyrics(new FastList - { - new Lyrics { Text = "AAA BBB CCC DDD EEE", StartBar = 0 } - }); - Assert.AreEqual(1, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Lyrics.Length); - Assert.AreEqual("AAA", score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Lyrics[0]); - Assert.AreEqual("BBB", score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Lyrics[0]); - Assert.IsNull(score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[2].Lyrics); - Assert.AreEqual("CCC", score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[3].Lyrics[0]); - - Assert.AreEqual(1, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Lyrics.Length); - Assert.AreEqual("DDD", score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Lyrics[0]); - Assert.AreEqual("EEE", score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[1].Lyrics[0]); - } - - [TestMethod] - public void TestApplySingleLineBarOffset() - { - var score = LoadLyricsTemplateFile(); - score.Tracks[0].ApplyLyrics(new FastList - { - new Lyrics { Text = "AAA BBB CCC DDD EEE", StartBar = 2 } - }); - - Assert.AreEqual(1, score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[0].Lyrics.Length); - Assert.AreEqual("AAA", score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[0].Lyrics[0]); - Assert.AreEqual("BBB", score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[1].Lyrics[0]); - Assert.IsNull(score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[2].Lyrics); - Assert.AreEqual("CCC", score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[3].Lyrics[0]); - - Assert.AreEqual(1, score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[0].Lyrics.Length); - Assert.AreEqual("DDD", score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[0].Lyrics[0]); - Assert.AreEqual("EEE", score.Tracks[0].Staves[0].Bars[3].Voices[0].Beats[1].Lyrics[0]); - } - - - [TestMethod] - public void TestApplyMultiLineFirstBar() - { - var score = LoadLyricsTemplateFile(); - score.Tracks[0].ApplyLyrics(new FastList - { - new Lyrics { Text = "AAA BBB CCC DDD EEE", StartBar = 0 }, - new Lyrics { Text = "111 222 333 444 555", StartBar = 0 } - }); - Assert.AreEqual(2, score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Lyrics.Length); - Assert.AreEqual("AAA", score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Lyrics[0]); - Assert.AreEqual("111", score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Lyrics[1]); - Assert.AreEqual("BBB", score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Lyrics[0]); - Assert.AreEqual("222", score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[1].Lyrics[1]); - Assert.IsNull(score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[2].Lyrics); - Assert.AreEqual("CCC", score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[3].Lyrics[0]); - Assert.AreEqual("333", score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[3].Lyrics[1]); - - Assert.AreEqual(2, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Lyrics.Length); - Assert.AreEqual("DDD", score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Lyrics[0]); - Assert.AreEqual("444", score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Lyrics[1]); - Assert.AreEqual("EEE", score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[1].Lyrics[0]); - Assert.AreEqual("555", score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[1].Lyrics[1]); - } - - [TestMethod] - public void TestApplyMultiLineBarOffset() - { - var score = LoadLyricsTemplateFile(); - score.Tracks[0].ApplyLyrics(new FastList - { - new Lyrics { Text = "AAA BBB CCC DDD EEE", StartBar = 2 }, - new Lyrics { Text = "111 222 333 444 555", StartBar = 1 } - }); - - Assert.AreEqual(2, score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Lyrics.Length); - - Assert.IsNull(score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Lyrics[0]); - Assert.AreEqual("111", score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[0].Lyrics[1]); - Assert.IsNull(score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[1].Lyrics[0]); - Assert.AreEqual("222", score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[1].Lyrics[1]); - Assert.IsNull(score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[2].Lyrics); - Assert.IsNull(score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[3].Lyrics[0]); - Assert.AreEqual("333", score.Tracks[0].Staves[0].Bars[1].Voices[0].Beats[3].Lyrics[1]); - - Assert.AreEqual(2, score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[0].Lyrics.Length); - Assert.AreEqual("AAA", score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[0].Lyrics[0]); - Assert.AreEqual("444", score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[0].Lyrics[1]); - Assert.AreEqual("BBB", score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[1].Lyrics[0]); - Assert.AreEqual("555", score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[1].Lyrics[1]); - Assert.IsNull(score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[2].Lyrics); - Assert.AreEqual("CCC", score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[3].Lyrics[0]); - Assert.IsNull(score.Tracks[0].Staves[0].Bars[2].Voices[0].Beats[3].Lyrics[1]); - } - - [TestMethod] - public void TestSpaces() - { - TestLyrics("AAA BBB CCC DDD EEE", new[] - { - "AAA", - "BBB", - "CCC", - "DDD", - "EEE" - }); - TestLyrics("AAA BBB CCC", new[] - { - "AAA", - "", - "BBB", - "", - "", - "CCC" - }); - } - - [TestMethod] - public void TestNewLines() - { - TestLyrics("AAA\r\nBBB\rCCC\nDDD\r\nEEE", new[] - { - "AAA", - "BBB", - "CCC", - "DDD", - "EEE" - }); - } - - [TestMethod] - public void TestDash() - { - TestLyrics("AAA-BBB CCC- DDD EEE--FFF", new[] - { - "AAA-", - "BBB", - "CCC-", - "DDD", - "EEE--", - "FFF" - }); - } - - [TestMethod] - public void TestPlus() - { - TestLyrics("AAA+BBB CCC++DDD EEE+ FFF", new[] - { - "AAA BBB", - "CCC DDD", - "EEE ", - "FFF" - }); - } - - [TestMethod] - public void TestComments() - { - TestLyrics("[ABCD]AAA BBB", new[] - { - "AAA", - "BBB" - }); - - TestLyrics("[ABCD] AAA BBB", new[] - { - "", - "AAA", - "BBB" - }); - - TestLyrics("[AAA BBB\r\nCCC DDD]AAA BBB", new[] - { - "AAA", - "BBB" - }); - - TestLyrics("[AAA BBB\r\nCCC DDD] AAA BBB", new[] - { - "", - "AAA", - "BBB" - }); - - TestLyrics("[AAA] AAA [BBB] BBB [CCC] CCC [DDD] DDD", new[] - { - "", - "AAA", - "", - "BBB", - "", - "CCC", - "", - "DDD", - }); - } - - private void TestLyrics(string text, string[] chunks) - { - var lyrics = new Lyrics(); - lyrics.Text = text; - lyrics.Finish(); - Assert.AreEqual(string.Join(", ", chunks), string.Join(", ", lyrics.Chunks)); - } - } -} diff --git a/Source/AlphaTab.Test/Model/TuningParserTest.cs b/Source/AlphaTab.Test/Model/TuningParserTest.cs deleted file mode 100644 index 30fe0879b..000000000 --- a/Source/AlphaTab.Test/Model/TuningParserTest.cs +++ /dev/null @@ -1,27 +0,0 @@ -using AlphaTab.Model; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace AlphaTab.Test.Model -{ - [TestClass] - public class TuningParserTest - { - [TestMethod] - public void TestStandard() - { - var standard = Tuning.GetDefaultTuningFor(6); - var tuningText = new[] {"E4", "B3", "G3", "D3", "A2", "E2"}; - - var tuning = new int[tuningText.Length]; - var tuningText2 = new string[tuningText.Length]; - for (int i = 0; i < tuningText.Length; i++) - { - tuning[i] = TuningParser.GetTuningForText(tuningText[i]); - tuningText2[i] = Tuning.GetTextForTuning(tuning[i], true); - } - - Assert.AreEqual(string.Join(",", standard.Tunings), string.Join(",", tuning)); - Assert.AreEqual(string.Join(",", tuningText), string.Join(",", tuningText2)); - } - } -} diff --git a/Source/AlphaTab.Test/Rendering/AccidentalHelperTest.cs b/Source/AlphaTab.Test/Rendering/AccidentalHelperTest.cs deleted file mode 100644 index d5ed002ea..000000000 --- a/Source/AlphaTab.Test/Rendering/AccidentalHelperTest.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using AlphaTab.Importer; -using AlphaTab.IO; -using AlphaTab.Model; -using AlphaTab.Rendering.Utils; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace AlphaTab.Test.Rendering -{ - [TestClass] - public class AccidentalHelperTest - { - [TestMethod] - public void TestDisplayTranspositionPitchResultsInAccidental() - { - var import = new AlphaTexImporter(); - import.Init(ByteBuffer.FromBuffer(Encoding.UTF8.GetBytes("\\tuning none . e3.8"))); - var score = import.ReadScore(); - - var settings = Settings.Defaults; - - // no transposition - settings.TranspositionPitches = new [] {0}; - ModelUtils.ApplyPitchOffsets(settings, score); - var helper = new AccidentalHelper(); - var accidental = helper.ApplyAccidental(score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0]); - var line = helper.GetNoteLine(score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0]); - Assert.AreEqual(AccidentalType.None, accidental); - Assert.AreEqual(17, line); - - // one semitone up => one line up - settings.TranspositionPitches = new[] { 1 }; - ModelUtils.ApplyPitchOffsets(settings, score); - helper = new AccidentalHelper(); - accidental = helper.ApplyAccidental(score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0]); - line = helper.GetNoteLine(score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0]); - Assert.AreEqual(AccidentalType.None, accidental); - Assert.AreEqual(16, line); - - // two semitones up => one line up + sharp - settings.TranspositionPitches = new[] { 2 }; - ModelUtils.ApplyPitchOffsets(settings, score); - helper = new AccidentalHelper(); - accidental = helper.ApplyAccidental(score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0]); - line = helper.GetNoteLine(score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0]); - Assert.AreEqual(AccidentalType.Sharp, accidental); - Assert.AreEqual(16, line); - } - } -} diff --git a/Source/AlphaTab.Test/TestFiles/Audio/default.sf2 b/Source/AlphaTab.Test/TestFiles/Audio/default.sf2 deleted file mode 100644 index c83e38294..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/Audio/default.sf2 and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro5/Canon.gp5 b/Source/AlphaTab.Test/TestFiles/GuitarPro5/Canon.gp5 deleted file mode 100644 index ef6075811..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/GuitarPro5/Canon.gp5 and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXml/Effects.xml b/Source/AlphaTab.Test/TestFiles/MusicXml/Effects.xml deleted file mode 100644 index 909c98fb3..000000000 --- a/Source/AlphaTab.Test/TestFiles/MusicXml/Effects.xml +++ /dev/null @@ -1,2554 +0,0 @@ - - - - Effects - - - 2016-12-19 - Guitar Pro 6 - - - - - Track 1 - S-Gt - - 1 - 1 - 26 - 80 - 0 - - - - - - - 1 - - 0 - major - - - - TAB - 5 - - - 6 - - E - 2 - - - A - 2 - - - D - 3 - - - G - 3 - - - B - 3 - - - E - 4 - - - - 0 - 0 - 0 - - - - - - quarter - 120 - - - - - - - F - 2 - - 1 - 1 - quarter - up - - - 6 - 1 - - - - - - B - 2 - - 1 - 1 - quarter - up - - - 5 - 2 - - - - - - F - 3 - - 1 - 1 - quarter - up - - - 4 - 3 - - - - - - B - 3 - - 1 - 1 - quarter - down - - - 3 - 4 - - - - - - - 8 - - TAB - 5 - - - - - - A - 1 - 3 - - 1 - 32nd - sharp - up - - - - A - 3 - - 7 - 1 - quarter - natural - up - - - 3 - 2 - - - - - - - A - 3 - - 1 - 32nd - up - - - - A - 3 - - 7 - 1 - quarter - up - - - 3 - 2 - - - - - - - A - 3 - - 1 - 32nd - up - - - - A - 3 - - 7 - 1 - quarter - up - - - 3 - 2 - - - - - - - - A - 3 - - 1 - 32nd - up - - - - A - 3 - - 8 - 1 - quarter - up - - - 3 - 2 - - - - - - - 1 - - TAB - 5 - - - - - A - 3 - - 1 - 1 - quarter - up - - - 3 - 2 - - - - - - A - 3 - - 1 - 1 - quarter - up - - - - - - 3 - 2 - - - - - - A - 3 - - 1 - 1 - quarter - up - - - - - - 3 - 2 - - - - - - A - 3 - - 1 - 1 - quarter - up - - - 3 - 2 - - - - - - - - TAB - 5 - - - - - A - 3 - - 1 - 1 - quarter - down - - - - - - - 3 - 2 - - - - - - A - 3 - - 1 - 1 - quarter - up - - - - - - - 3 - 2 - - - - - - A - 3 - - 1 - 1 - quarter - up - - - - - - - 3 - 2 - - - - - - A - 3 - - 1 - 1 - quarter - up - - - - 3 - 2 - - - - - - A - 3 - - 1 - 1 - quarter - up - - - - 3 - 2 - - - - - - - - TAB - 5 - - - - - C - 1 - 4 - - 1 - 1 - quarter - sharp - up - - - 2 - 2 - - - - - - - - A - 3 - - 1 - 1 - quarter - up - - - H - 3 - 2 - - - - - - - - B - 2 - - 1 - 1 - quarter - up - - - H - 5 - 2 - - - - - - - - F - 1 - 2 - - 1 - 1 - quarter - sharp - up - - - H - 6 - 2 - - - - - - - C - 1 - 4 - - 1 - 1 - quarter - up - - - 2 - 2 - - - - - - - A - 3 - - 1 - 1 - quarter - up - - - 3 - 2 - - - - - - - B - 2 - - 1 - 1 - quarter - up - - - 5 - 2 - - - - - - - F - 1 - 2 - - 1 - 1 - quarter - up - - - 6 - 2 - - - - - - A - 3 - - 1 - 1 - quarter - up - - - H - 3 - 2 - - - - - - - - E - 3 - - 1 - 1 - quarter - up - - - H - 4 - 2 - - - - - - - A - 3 - - 1 - 1 - quarter - up - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - quarter - up - - - 4 - 2 - - - - - - - - TAB - 5 - - - - - A - 3 - - 1 - 1 - quarter - up - - - H - 3 - 2 - - - - - - - - F - 1 - 2 - - 1 - 1 - quarter - sharp - up - - - H - 6 - 2 - - - - - - - A - 1 - 3 - - 1 - 1 - quarter - sharp - up - - - H - - 3 - 3 - - - - - - - - G - 2 - - 1 - 1 - quarter - up - - - - 6 - 3 - - - - - - - A - 3 - - 1 - 1 - quarter - natural - up - - - - 3 - 2 - - - - - - - - F - 1 - 2 - - 1 - 1 - quarter - up - - - H - 6 - 2 - - - - - - - F - 1 - 5 - - 1 - 1 - quarter - sharp - down - - - H - 3 - 23 - - - - - - - F - 1 - 5 - - 1 - 1 - quarter - down - - - 3 - 23 - - - - - - - - TAB - 5 - - - - - A - 1 - 3 - - 2 - 1 - half - sharp - up - - - - 2 - - 3 - 3 - - - - - - A - 1 - 3 - - 2 - 1 - half - up - - - - 2 - - 3 - 3 - - - - - - - - TAB - 5 - - - - - F - 4 - - 4 - 1 - whole - down - - - - 6 - - - 3 - - - 3 - 10 - - - - - - - - TAB - 5 - - - - - F - 4 - - 4 - 1 - whole - down - - - 3 - 10 - - - - - - - - TAB - 5 - - - - - F - 4 - - 4 - 1 - whole - down - - - 3 - 10 - - - - - - - - TAB - 5 - - - - - F - 4 - - 4 - 1 - whole - down - - - 3 - 10 - - - - - - - - TAB - 5 - - - - - F - 4 - - 4 - 1 - whole - down - - - 3 - 10 - - - - - - - - TAB - 5 - - - - - 4 - 1 - whole - - - - - - TAB - 5 - - - - - C - 4 - - 1 - 1 - quarter - up - - - 2 - 1 - - - - - - - - A - 1 - 2 - - 1 - 1 - quarter - sharp - up - - - 5 - 1 - - - - - - - C - 1 - 4 - - 1 - 1 - quarter - sharp - up - - - 2 - 2 - - - - - - - - C - 3 - - 1 - 1 - quarter - up - - - 5 - 3 - - - - - - - A - 1 - 2 - - 1 - 1 - quarter - up - - - 5 - 1 - - - - - - - B - 2 - - 1 - 1 - quarter - up - - - 5 - 2 - - - - - - - - - TAB - 5 - - - - - C - 4 - - 1 - 1 - quarter - up - - - 2 - 1 - - - - - - C - 1 - 4 - - 1 - 1 - quarter - sharp - up - - - 2 - 2 - - - - - - D - 4 - - 1 - 1 - quarter - up - - - 2 - 3 - - - - - - D - 1 - 4 - - 1 - 1 - quarter - sharp - up - - - 2 - 4 - - - - - - - - TAB - 5 - - - - - D - 4 - - 1 - 1 - quarter - up - - - 2 - 3 - - - - - - D - 4 - - 1 - 1 - quarter - up - - - 2 - 3 - - - - - - D - 1 - 4 - - 1 - 1 - quarter - sharp - up - - - 2 - 4 - - - - - - D - 1 - 4 - - 1 - 1 - quarter - up - - - 2 - 4 - - - - - - - - TAB - 5 - - - - - C - 1 - 4 - - 1 - 1 - quarter - sharp - up - - - - - - 2 - 2 - - - - - - C - 1 - 4 - - 1 - 1 - quarter - up - - - 3 - - - 2 - 2 - - - - - - C - 1 - 4 - - 1 - 1 - quarter - up - - - 2 - - - 2 - 2 - - - - - - C - 1 - 4 - - 1 - 1 - quarter - up - - - 1 - - - 2 - 2 - - - - - - - - TAB - 5 - - - - - C - 1 - 4 - - 1 - 1 - quarter - sharp - up - - - 2 - 2 - - - - - - C - 1 - 4 - - 1 - 1 - quarter - up - - - - - - 2 - 2 - - - - - - C - 1 - 4 - - 1 - 1 - quarter - up - - - 2 - 2 - - - - - - C - 1 - 4 - - 1 - 1 - quarter - up - - - 2 - 2 - - - - - - - - TAB - 5 - - - - - C - 1 - 4 - - 1 - 1 - quarter - sharp - up - - - 2 - 2 - - - - - - C - 1 - 4 - - 1 - 1 - quarter - up - - - 2 - 2 - - - - - - C - 1 - 4 - - 1 - 1 - quarter - up - - - 2 - 2 - - - - - - C - 1 - 4 - - 1 - 1 - quarter - up - - - 2 - 2 - - - - - - - - TAB - 5 - - - - - C - 1 - 4 - - 1 - 1 - quarter - sharp - up - - - 2 - 2 - - - - - - 3 - 1 - half - - - - - - - TAB - 5 - - - - - C - 1 - 4 - - 1 - 1 - quarter - sharp - up - - - 2 - 2 - - - - - - C - 1 - 4 - - 1 - 1 - quarter - up - - - 2 - 2 - - - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - C - 1 - 4 - - 1 - 1 - quarter - sharp - up - - - 2 - 2 - - - - - - 3 - 1 - half - - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 3 - 1 - half - - - - - - - TAB - 5 - - - - - C - 1 - 4 - - 1 - 1 - quarter - sharp - up - - - - 2 - 2 - - - - - - C - 1 - 4 - - 1 - 1 - quarter - up - - - - 2 - 2 - - - - - - C - 1 - 4 - - 1 - 1 - quarter - up - - - - 2 - 2 - - - - - - C - 1 - 4 - - 1 - 1 - quarter - up - - - - 2 - 2 - - - - - - C - 1 - 4 - - 1 - 1 - quarter - up - - - - 2 - 2 - - - - - - C - 1 - 4 - - 1 - 1 - quarter - up - - - p - 2 - 2 - - - - - - C - 1 - 4 - - 1 - 1 - quarter - up - - - i - 2 - 2 - - - - - - C - 1 - 4 - - 1 - 1 - quarter - up - - - m - 2 - 2 - - - - - - C - 1 - 4 - - 1 - 1 - quarter - up - - - a - 2 - 2 - - - - - - C - 1 - 4 - - 1 - 1 - quarter - up - - - c - 2 - 2 - - - - - - - - TAB - 5 - - - - - C - 4 - - 1 - 1 - quarter - up - - - 2 - 1 - - - - - - - - G - 3 - - 1 - 1 - quarter - up - - - 3 - 0 - - - - - - - - E - 3 - - 1 - 1 - quarter - up - - - 4 - 2 - - - - - - - - C - 3 - - 1 - 1 - quarter - up - - - 5 - 3 - - - - - - - C - 4 - - 1 - 1 - quarter - up - - - 2 - 1 - - - - - - - - G - 3 - - 1 - 1 - quarter - up - - - 3 - 0 - - - - - - - - E - 3 - - 1 - 1 - quarter - up - - - 4 - 2 - - - - - - - - C - 3 - - 1 - 1 - quarter - up - - - 5 - 3 - - - - - - - B - 2 - - 1 - 1 - quarter - up - - - - 5 - 2 - - - - - - B - 2 - - 1 - 1 - quarter - up - - - - 5 - 2 - - - - - - - 3 - - TAB - 5 - - - - - C - 3 - - 2 - 1 - quarter - - 3 - 2 - - up - - - - 5 - 3 - - - - - - C - 3 - - 2 - 1 - quarter - - 3 - 2 - - up - - - 5 - 3 - - - - - - C - 3 - - 2 - 1 - quarter - - 3 - 2 - - up - - - - 5 - 3 - - - - - - 6 - 1 - half - - - - - 5 - - TAB - 5 - - - - - A - 1 - 2 - - 4 - 1 - quarter - sharp - - 5 - 4 - - up - - - - 5 - 1 - - - - - - A - 1 - 2 - - 4 - 1 - quarter - - 5 - 4 - - up - - - 5 - 1 - - - - - - A - 1 - 2 - - 4 - 1 - quarter - - 5 - 4 - - up - - - 5 - 1 - - - - - - A - 1 - 2 - - 4 - 1 - quarter - - 5 - 4 - - up - - - 5 - 1 - - - - - - A - 1 - 2 - - 4 - 1 - quarter - - 5 - 4 - - up - - - 5 - 1 - - - - - - - 1 - - TAB - 5 - - - - - B - 2 - - 1 - 1 - quarter - up - - - 5 - 2 - - - - - - B - 2 - - 1 - 1 - quarter - up - - - 5 - 2 - - - - - - B - 2 - - 1 - 1 - quarter - up - - - 5 - 2 - - - - - - B - 2 - - 1 - 1 - quarter - up - - - 5 - 2 - - - - - - - 5 - - TAB - 5 - - - - - B - 2 - - 4 - 1 - quarter - - 5 - 4 - - up - - - - 5 - 2 - - - - - - 1 - 1 - eighth - - 5 - 2 - - - - - - - - 15 - 1 - half - - - - - - 1 - - TAB - 5 - - - - - B - 2 - - 1 - 1 - quarter - up - - - 5 - 2 - - - - - - B - 2 - - 1 - 1 - quarter - up - - - 5 - 2 - - - - - - B - 2 - - 1 - 1 - quarter - up - - - 5 - 2 - - - - - - B - 2 - - 1 - 1 - quarter - up - - - 5 - 2 - - - - - - - - TAB - 5 - - - - - B - 2 - - 1 - 1 - quarter - up - - - 5 - 2 - - - - - - B - 2 - - 1 - 1 - quarter - up - - - 5 - 2 - - - - - - B - 2 - - 1 - 1 - quarter - up - - - 5 - 2 - - - - - - B - 2 - - 1 - 1 - quarter - up - - - 5 - 2 - - - - - - - - TAB - 5 - - - - - B - 2 - - 1 - 1 - quarter - up - - - 5 - 2 - - - - - - B - 3 - - 1 - 1 - quarter - up - - - 3 - 4 - - - - - - 2 - 1 - half - - - - diff --git a/Source/AlphaTab.Test/TestFiles/MusicXml/RepeatClose.xml b/Source/AlphaTab.Test/TestFiles/MusicXml/RepeatClose.xml deleted file mode 100644 index d80b2d96f..000000000 --- a/Source/AlphaTab.Test/TestFiles/MusicXml/RepeatClose.xml +++ /dev/null @@ -1,309 +0,0 @@ - - - - - - 2016-12-19 - Guitar Pro 6 - - - - - Steel Guitar - S-Gt - - 1 - 1 - 26 - 80 - 0 - - - - - - - 1 - - 0 - major - - - - TAB - 5 - - - 6 - - E - 2 - - - A - 2 - - - D - 3 - - - G - 3 - - - B - 3 - - - E - 4 - - - - 0 - 0 - 0 - - - - - - quarter - 120 - - - - - - - F - 2 - - 1 - 1 - quarter - up - - - 6 - 1 - - - - - - F - 1 - 2 - - 1 - 1 - quarter - sharp - up - - - 6 - 2 - - - - - - G - 2 - - 1 - 1 - quarter - up - - - 6 - 3 - - - - - - G - 1 - 2 - - 1 - 1 - quarter - sharp - up - - - 6 - 4 - - - - - - - - - - - TAB - 5 - - - - - F - 2 - - 1 - 1 - quarter - up - - - 6 - 1 - - - - - - F - 1 - 2 - - 1 - 1 - quarter - sharp - up - - - 6 - 2 - - - - - - G - 2 - - 1 - 1 - quarter - up - - - 6 - 3 - - - - - - G - 1 - 2 - - 1 - 1 - quarter - sharp - up - - - 6 - 4 - - - - - - - - - - - TAB - 5 - - - - - F - 2 - - 1 - 1 - quarter - up - - - 6 - 1 - - - - - - F - 1 - 2 - - 1 - 1 - quarter - sharp - up - - - 6 - 2 - - - - - - G - 2 - - 1 - 1 - quarter - up - - - 6 - 3 - - - - - - G - 1 - 2 - - 1 - 1 - quarter - sharp - up - - - 6 - 4 - - - - - - diff --git a/Source/AlphaTab.Test/TestFiles/MusicXml/RepeatCloseAlternateEndings.xml b/Source/AlphaTab.Test/TestFiles/MusicXml/RepeatCloseAlternateEndings.xml deleted file mode 100644 index dbb6ca6b4..000000000 --- a/Source/AlphaTab.Test/TestFiles/MusicXml/RepeatCloseAlternateEndings.xml +++ /dev/null @@ -1,252 +0,0 @@ - - - - - - 2016-12-19 - Guitar Pro 6 - - - - - Steel Guitar - S-Gt - - 1 - 1 - 26 - 80 - 0 - - - - - - - 1 - - 0 - major - - - - TAB - 5 - - - 6 - - E - 2 - - - A - 2 - - - D - 3 - - - G - 3 - - - B - 3 - - - E - 4 - - - - 0 - 0 - 0 - - - - - - quarter - 120 - - - - - - - F - 2 - - 1 - 1 - quarter - up - - - 6 - 1 - - - - - 1 - - - - 1 - 2 - quarter - - - - - - - - - TAB - 5 - - - - - F - 2 - - 1 - 1 - quarter - up - - - 6 - 1 - - - - - 1 - - - - 1 - 2 - quarter - - - - - - - - - TAB - 5 - - - - - F - 2 - - 1 - 1 - quarter - up - - - 6 - 1 - - - - - 1 - - - - 1 - 2 - quarter - - - - - - - - - TAB - 5 - - - - - F - 2 - - 1 - 1 - quarter - up - - - 6 - 1 - - - - - 1 - - - - 1 - 2 - quarter - - - - - - - - - TAB - 5 - - - - - F - 2 - - 1 - 1 - quarter - up - - - 6 - 1 - - - - - 1 - - - - 1 - 2 - quarter - - - - diff --git a/Source/AlphaTab.Test/TestFiles/MusicXml/RepeatCloseMulti.xml b/Source/AlphaTab.Test/TestFiles/MusicXml/RepeatCloseMulti.xml deleted file mode 100644 index 82b9ec3d1..000000000 --- a/Source/AlphaTab.Test/TestFiles/MusicXml/RepeatCloseMulti.xml +++ /dev/null @@ -1,309 +0,0 @@ - - - - - - 2016-12-19 - Guitar Pro 6 - - - - - Steel Guitar - S-Gt - - 1 - 1 - 26 - 80 - 0 - - - - - - - 1 - - 0 - major - - - - TAB - 5 - - - 6 - - E - 2 - - - A - 2 - - - D - 3 - - - G - 3 - - - B - 3 - - - E - 4 - - - - 0 - 0 - 0 - - - - - - quarter - 120 - - - - - - - F - 2 - - 1 - 1 - quarter - up - - - 6 - 1 - - - - - - F - 1 - 2 - - 1 - 1 - quarter - sharp - up - - - 6 - 2 - - - - - - G - 2 - - 1 - 1 - quarter - up - - - 6 - 3 - - - - - - G - 1 - 2 - - 1 - 1 - quarter - sharp - up - - - 6 - 4 - - - - - - - - - - - TAB - 5 - - - - - F - 2 - - 1 - 1 - quarter - up - - - 6 - 1 - - - - - - F - 1 - 2 - - 1 - 1 - quarter - sharp - up - - - 6 - 2 - - - - - - G - 2 - - 1 - 1 - quarter - up - - - 6 - 3 - - - - - - G - 1 - 2 - - 1 - 1 - quarter - sharp - up - - - 6 - 4 - - - - - - - - - - - TAB - 5 - - - - - F - 2 - - 1 - 1 - quarter - up - - - 6 - 1 - - - - - - F - 1 - 2 - - 1 - 1 - quarter - sharp - up - - - 6 - 2 - - - - - - G - 2 - - 1 - 1 - quarter - up - - - 6 - 3 - - - - - - G - 1 - 2 - - 1 - 1 - quarter - sharp - up - - - 6 - 4 - - - - - - diff --git a/Source/AlphaTab.Test/TestFiles/MusicXml/Serenade.xml b/Source/AlphaTab.Test/TestFiles/MusicXml/Serenade.xml deleted file mode 100644 index a940524d3..000000000 --- a/Source/AlphaTab.Test/TestFiles/MusicXml/Serenade.xml +++ /dev/null @@ -1,49314 +0,0 @@ - - - - Serenade to a Dream - - Suidakra - Chris Ninman - - 2016-12-19 - Guitar Pro 6 - - - - - Lead - S-Gt - - 1 - 1 - 26 - 80 - 0 - - - - Rhythm - S-Gt - - 3 - 1 - 26 - 80 - 0 - - - - Bass - A-Bass - - 5 - 1 - 33 - 80 - 0 - - - - Keyboard - S-Gt - - 7 - 1 - 11 - 80 - 0 - - - - - - - 12 - - 0 - major - - - - TAB - 5 - - - 6 - - D - 2 - - - G - 2 - - - C - 3 - - - F - 3 - - - A - 3 - - - D - 4 - - - - 0 - 0 - 0 - - - - - - quarter - 94 - - - - - - - D - 4 - - 6 - 1 - eighth - down - begin - - - 2 - 3 - - - - - - A - 3 - - 3 - 1 - 16th - down - continue - begin - - - 3 - 2 - - - - - - D - 4 - - 3 - 1 - 16th - down - continue - end - - - 2 - 3 - - - - - - E - 4 - - 6 - 1 - eighth - down - end - - - 1 - 0 - - - - - - F - 4 - - 6 - 1 - eighth - down - - - 1 - 1 - - - - - - E - 4 - - 2 - 1 - 16th - - 3 - 2 - - down - begin - begin - - - - H - 1 - 0 - - - - - - - F - 4 - - 2 - 1 - 16th - - 3 - 2 - - down - continue - continue - - - H - - 1 - 1 - - - - - - - E - 4 - - 2 - 1 - 16th - - 3 - 2 - - down - end - end - - - - - 1 - 0 - - - - - - - D - 4 - - 6 - 1 - eighth - down - - - 2 - 3 - - - - - - - 4 - - TAB - 5 - - - - - C - 4 - - 1 - 1 - 16th - down - begin - begin - - - H - 2 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - down - continue - end - - - - 2 - 3 - - - - - - - E - 4 - - 2 - 1 - eighth - down - continue - - - 1 - 0 - - - - - - C - 4 - - 2 - 1 - eighth - down - end - - - 2 - 1 - - - - - - D - 4 - - 2 - 1 - eighth - down - begin - - - 2 - 3 - - - - - - A - 3 - - 2 - 1 - eighth - down - continue - - - 3 - 2 - - - - - - D - 4 - - 2 - 1 - eighth - down - end - - - 2 - 3 - - - - - - - 6 - - TAB - 5 - - - - - C - 4 - - 3 - 1 - eighth - down - - - 2 - 1 - - - - - - B - 3 - - 1 - 1 - 16th - - 3 - 2 - - down - begin - begin - - - - H - 2 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - - 3 - 2 - - down - continue - continue - - - H - - 2 - 1 - - - - - - - B - 3 - - 1 - 1 - 16th - - 3 - 2 - - down - end - end - - - - - 2 - 0 - - - - - - - G - 3 - - 3 - 1 - eighth - up - - - 3 - 0 - - - - - - A - 3 - - 6 - 1 - quarter - up - - - 3 - 2 - - - - - - A - 3 - - 3 - 1 - eighth - up - - - 3 - 2 - - - - - - - - TAB - 5 - - - - - A - 3 - - 3 - 1 - eighth - up - - - 3 - 2 - - - - - - G - 3 - - 1 - 1 - 16th - - 3 - 2 - - up - begin - begin - - - - H - 3 - 0 - - - - - - - A - 3 - - 1 - 1 - 16th - - 3 - 2 - - up - continue - continue - - - H - - 3 - 2 - - - - - - - G - 3 - - 1 - 1 - 16th - - 3 - 2 - - up - end - end - - - - - 3 - 0 - - - - - - - E - 3 - - 3 - 1 - eighth - up - - - 4 - 2 - - - - - - D - 3 - - 9 - 1 - quarter - - up - - - 4 - 0 - - - - - - - 12 - - TAB - 5 - - - - - G - 4 - - 6 - 1 - eighth - up - begin - - - 1 - 3 - - - - - - - D - 4 - - 6 - 1 - eighth - up - begin - - - 2 - 3 - - - - - - - A - 3 - - 6 - 1 - eighth - up - begin - - - 3 - 2 - - - - - - - D - 3 - - 6 - 1 - eighth - up - begin - - - 4 - 0 - - - - - - D - 4 - - 3 - 1 - 16th - up - continue - begin - - - 2 - 3 - - - - - - A - 3 - - 3 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - G - 4 - - 2 - 1 - 16th - - 3 - 2 - - down - begin - begin - - - - H - 1 - 3 - - - - - - - F - 4 - - 2 - 1 - 16th - - 3 - 2 - - down - continue - continue - - - H - - 1 - 1 - - - - - - - E - 4 - - 2 - 1 - 16th - - 3 - 2 - - down - end - end - - - - - 1 - 0 - - - - - - - F - 4 - - 6 - 1 - eighth - down - begin - - - 1 - 1 - - - - - - D - 4 - - 3 - 1 - 16th - down - continue - begin - - - 2 - 3 - - - - - - A - 3 - - 3 - 1 - 16th - down - continue - continue - - - 3 - 2 - - - - - - E - 4 - - 2 - 1 - 16th - - 3 - 2 - - down - begin - begin - - - - H - 1 - 0 - - - - - - - F - 4 - - 2 - 1 - 16th - - 3 - 2 - - down - continue - continue - - - H - - 1 - 1 - - - - - - - E - 4 - - 2 - 1 - 16th - - 3 - 2 - - down - end - end - - - - 1 - 0 - - - - - - - - 4 - - TAB - 5 - - - - - C - 4 - - 1 - 1 - 16th - down - begin - begin - - - H - 2 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - down - continue - end - - - - 2 - 3 - - - - - - - E - 4 - - 2 - 1 - eighth - down - continue - - - 1 - 0 - - - - - - C - 4 - - 2 - 1 - eighth - down - end - - - 2 - 1 - - - - - - D - 4 - - 2 - 1 - eighth - down - begin - - - 2 - 3 - - - - - - A - 3 - - 2 - 1 - eighth - down - continue - - - 3 - 2 - - - - - - D - 4 - - 2 - 1 - eighth - down - end - - - 2 - 3 - - - - - - - 6 - - TAB - 5 - - - - - C - 4 - - 3 - 1 - eighth - down - - - 2 - 1 - - - - - - B - 3 - - 1 - 1 - 16th - - 3 - 2 - - down - begin - begin - - - - H - 2 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - - 3 - 2 - - down - continue - continue - - - H - - 2 - 1 - - - - - - - B - 3 - - 1 - 1 - 16th - - 3 - 2 - - down - end - end - - - - - 2 - 0 - - - - - - - G - 3 - - 3 - 1 - eighth - up - - - 3 - 0 - - - - - - A - 3 - - 6 - 1 - quarter - up - - - 3 - 2 - - - - - - A - 3 - - 3 - 1 - eighth - up - - - 3 - 2 - - - - - - - 4 - - TAB - 5 - - - - - A - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 2 - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - H - 3 - 2 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - end - - - - 3 - 0 - - - - - - - E - 3 - - 2 - 1 - eighth - up - end - - - 4 - 2 - - - - - - D - 3 - - 6 - 1 - quarter - - up - - - 4 - 0 - - - - - - - 12 - - TAB - 5 - - - - - D - 4 - - 6 - 1 - eighth - down - begin - - - 2 - 3 - - - - - - A - 3 - - 3 - 1 - 16th - down - continue - begin - - - 3 - 2 - - - - - - D - 4 - - 3 - 1 - 16th - down - continue - end - - - 2 - 3 - - - - - - E - 4 - - 6 - 1 - eighth - down - end - - - 1 - 0 - - - - - - F - 4 - - 6 - 1 - eighth - down - - - 1 - 1 - - - - - - E - 4 - - 2 - 1 - 16th - - 3 - 2 - - down - begin - begin - - - - H - 1 - 0 - - - - - - - F - 4 - - 2 - 1 - 16th - - 3 - 2 - - down - continue - continue - - - H - - 1 - 1 - - - - - - - E - 4 - - 2 - 1 - 16th - - 3 - 2 - - down - end - end - - - - - 1 - 0 - - - - - - - D - 4 - - 6 - 1 - eighth - down - - - 2 - 3 - - - - - - - 4 - - TAB - 5 - - - - - C - 4 - - 1 - 1 - 16th - down - begin - begin - - - H - 2 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - down - continue - end - - - - 2 - 3 - - - - - - - E - 4 - - 2 - 1 - eighth - down - continue - - - 1 - 0 - - - - - - C - 4 - - 2 - 1 - eighth - down - end - - - 2 - 1 - - - - - - D - 4 - - 2 - 1 - eighth - down - begin - - - 2 - 3 - - - - - - A - 3 - - 2 - 1 - eighth - down - continue - - - 3 - 2 - - - - - - D - 4 - - 2 - 1 - eighth - down - end - - - 2 - 3 - - - - - - - 6 - - TAB - 5 - - - - - C - 4 - - 3 - 1 - eighth - down - - - 2 - 1 - - - - - - B - 3 - - 1 - 1 - 16th - - 3 - 2 - - down - begin - begin - - - - H - 2 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - - 3 - 2 - - down - continue - continue - - - H - - 2 - 1 - - - - - - - B - 3 - - 1 - 1 - 16th - - 3 - 2 - - down - end - end - - - - - 2 - 0 - - - - - - - G - 3 - - 3 - 1 - eighth - up - - - 3 - 0 - - - - - - A - 3 - - 6 - 1 - quarter - up - - - 3 - 2 - - - - - - A - 3 - - 3 - 1 - eighth - up - - - 3 - 2 - - - - - - - - TAB - 5 - - - - - A - 3 - - 3 - 1 - eighth - up - - - 3 - 2 - - - - - - G - 3 - - 1 - 1 - 16th - - 3 - 2 - - up - begin - begin - - - - H - 3 - 0 - - - - - - - A - 3 - - 1 - 1 - 16th - - 3 - 2 - - up - continue - continue - - - H - - 3 - 2 - - - - - - - G - 3 - - 1 - 1 - 16th - - 3 - 2 - - up - end - end - - - - - 3 - 0 - - - - - - - E - 3 - - 3 - 1 - eighth - up - - - 4 - 2 - - - - - - D - 3 - - 9 - 1 - quarter - - up - - - 4 - 0 - - - - - - - 12 - - TAB - 5 - - - - - G - 4 - - 6 - 1 - eighth - up - begin - - - 1 - 3 - - - - - - - D - 4 - - 6 - 1 - eighth - up - begin - - - 2 - 3 - - - - - - - A - 3 - - 6 - 1 - eighth - up - begin - - - 3 - 2 - - - - - - - D - 3 - - 6 - 1 - eighth - up - begin - - - 4 - 0 - - - - - - D - 4 - - 3 - 1 - 16th - up - continue - begin - - - 2 - 3 - - - - - - A - 3 - - 3 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - G - 4 - - 2 - 1 - 16th - - 3 - 2 - - up - begin - begin - - - - H - 1 - 3 - - - - - - - F - 4 - - 2 - 1 - 16th - - 3 - 2 - - up - continue - continue - - - H - - 1 - 1 - - - - - - - E - 4 - - 2 - 1 - 16th - - 3 - 2 - - up - end - end - - - - - 1 - 0 - - - - - - - F - 4 - - 6 - 1 - eighth - up - begin - - - 1 - 1 - - - - - - D - 4 - - 3 - 1 - 16th - up - continue - begin - - - 2 - 3 - - - - - - A - 3 - - 3 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - E - 4 - - 2 - 1 - 16th - - 3 - 2 - - up - begin - begin - - - - H - 1 - 0 - - - - - - - F - 4 - - 2 - 1 - 16th - - 3 - 2 - - up - continue - continue - - - H - - 1 - 1 - - - - - - - E - 4 - - 2 - 1 - 16th - - 3 - 2 - - up - end - end - - - - 1 - 0 - - - - - - - - 4 - - TAB - 5 - - - - - C - 4 - - 1 - 1 - 16th - up - begin - begin - - - H - 2 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - end - - - - 2 - 3 - - - - - - - E - 4 - - 2 - 1 - eighth - up - continue - - - 1 - 0 - - - - - - C - 4 - - 2 - 1 - eighth - up - end - - - 2 - 1 - - - - - - D - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 3 - - - - - - A - 3 - - 2 - 1 - eighth - up - continue - - - 3 - 2 - - - - - - D - 4 - - 2 - 1 - eighth - up - end - - - 2 - 3 - - - - - - - 6 - - TAB - 5 - - - - - C - 4 - - 3 - 1 - eighth - up - - - 2 - 1 - - - - - - B - 3 - - 1 - 1 - 16th - - 3 - 2 - - up - begin - begin - - - - H - 2 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - - 3 - 2 - - up - continue - continue - - - H - - 2 - 1 - - - - - - - B - 3 - - 1 - 1 - 16th - - 3 - 2 - - up - end - end - - - - - 2 - 0 - - - - - - - G - 3 - - 3 - 1 - eighth - up - - - 3 - 0 - - - - - - A - 3 - - 6 - 1 - quarter - up - - - 3 - 2 - - - - - - A - 3 - - 3 - 1 - eighth - up - - - 3 - 2 - - - - - - - 4 - - TAB - 5 - - - - - A - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 2 - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - H - 3 - 2 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - end - - - - 3 - 0 - - - - - - - E - 3 - - 2 - 1 - eighth - up - end - - - 4 - 2 - - - - - - D - 3 - - 6 - - 1 - quarter - - up - - - 4 - 0 - - - - - - - - 2 - - - TAB - 5 - - - - - D - 3 - - 3 - - 1 - quarter - - up - - - 4 - 0 - - - - - - - - 12 - - - TAB - 5 - - - - - A - 4 - - 3 - 1 - 16th - up - begin - begin - - - 2 - 10 - - - - - - G - 4 - - 3 - 1 - 16th - up - continue - continue - - - 2 - 8 - - - - - - F - 4 - - 3 - 1 - 16th - up - continue - continue - - - 3 - 10 - - - - - - E - 4 - - 3 - 1 - 16th - up - continue - continue - - - 3 - 9 - - - - - - D - 4 - - 3 - 1 - 16th - up - continue - continue - - - 3 - 7 - - - - - - C - 4 - - 3 - 1 - 16th - up - end - end - - - 4 - 10 - - - - - - D - 4 - - 6 - 1 - eighth - up - - - 3 - 7 - - - - - - D - 4 - - 2 - 1 - 16th - - 3 - 2 - - up - begin - begin - - - - H - 3 - 7 - - - - - - - E - 4 - - 2 - 1 - 16th - - 3 - 2 - - up - continue - continue - - - H - - 3 - 9 - - - - - - - F - 4 - - 2 - 1 - 16th - - 3 - 2 - - up - end - end - - - - - 3 - 10 - - - - - - - G - 4 - - 6 - 1 - eighth - up - - - 2 - 8 - - - - - - - 4 - - TAB - 5 - - - - - A - 4 - - 1 - 1 - 16th - up - begin - begin - - - 2 - 10 - - - - - - G - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 8 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 10 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 9 - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 7 - - - - - - C - 4 - - 1 - 1 - 16th - up - end - end - - - 4 - 10 - - - - - - D - 4 - - 3 - 1 - eighth - - up - begin - - - 3 - 7 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - - - 3 - 9 - - - - - - C - 4 - - 2 - 1 - eighth - up - end - - - 4 - 10 - - - - - - - 12 - - TAB - 5 - - - - - A - 4 - - 3 - 1 - 16th - up - begin - begin - - - 2 - 10 - - - - - - G - 4 - - 3 - 1 - 16th - up - continue - continue - - - 2 - 8 - - - - - - F - 4 - - 3 - 1 - 16th - up - continue - continue - - - 3 - 10 - - - - - - E - 4 - - 3 - 1 - 16th - up - continue - continue - - - 3 - 9 - - - - - - D - 4 - - 3 - 1 - 16th - up - continue - continue - - - 3 - 7 - - - - - - C - 4 - - 3 - 1 - 16th - up - end - end - - - 4 - 10 - - - - - - D - 4 - - 6 - 1 - eighth - up - - - 3 - 7 - - - - - - D - 4 - - 2 - 1 - 16th - - 3 - 2 - - up - begin - begin - - - - H - 3 - 7 - - - - - - - E - 4 - - 2 - 1 - 16th - - 3 - 2 - - up - continue - continue - - - H - - 3 - 9 - - - - - - - F - 4 - - 2 - 1 - 16th - - 3 - 2 - - up - end - end - - - - - 3 - 10 - - - - - - - G - 4 - - 6 - 1 - eighth - up - - - 2 - 8 - - - - - - - 4 - - TAB - 5 - - - - - A - 4 - - 1 - 1 - 16th - up - begin - begin - - - 2 - 10 - - - - - - G - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 8 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 10 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 9 - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 7 - - - - - - C - 4 - - 1 - 1 - 16th - up - end - end - - - 4 - 10 - - - - - - D - 4 - - 3 - 1 - eighth - - up - begin - - - 3 - 7 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - - - 3 - 9 - - - - - - C - 4 - - 2 - 1 - eighth - up - end - - - 4 - 10 - - - - - - - - TAB - 5 - - - - - A - 4 - - 3 - 1 - eighth - - up - begin - - - 2 - 10 - - - - - - A - 4 - - 1 - 1 - 16th - up - continue - - - 2 - 10 - - - - - - A - 4 - - 2 - 1 - eighth - up - end - - - 2 - 10 - - - - - - A - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 10 - - - - - - G - 4 - - 2 - 1 - eighth - up - continue - - - 2 - 8 - - - - - - F - 4 - - 2 - 1 - eighth - up - end - - - 3 - 10 - - - - - - - - TAB - 5 - - - - - E - 4 - - 3 - 1 - eighth - - up - begin - - - 3 - 9 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - - - 3 - 10 - - - - - - G - 4 - - 2 - 1 - eighth - up - end - - - 2 - 8 - - - - - - G - 4 - - 3 - 1 - eighth - - up - begin - - - 2 - 8 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - - - 3 - 10 - - - - - - E - 4 - - 2 - 1 - eighth - up - end - - - 3 - 9 - - - - - - - - TAB - 5 - - - - - D - 4 - - 3 - 1 - eighth - - up - begin - - - 3 - 7 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - - - 3 - 9 - - - - - - F - 4 - - 2 - 1 - eighth - up - end - - - 3 - 10 - - - - - - F - 4 - - 2 - 1 - eighth - up - begin - - - 3 - 10 - - - - - - E - 4 - - 2 - 1 - eighth - up - continue - - - 3 - 9 - - - - - - D - 4 - - 2 - 1 - eighth - up - end - - - 3 - 7 - - - - - - - - TAB - 5 - - - - - C - 4 - - 3 - 1 - eighth - - up - begin - - - 4 - 10 - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - - - 3 - 7 - - - - - - E - 4 - - 2 - 1 - eighth - up - end - - - 3 - 9 - - - - - - D - 4 - - 4 - 1 - quarter - up - - - 3 - 7 - - - - - - D - 4 - - 2 - 1 - eighth - up - - - 3 - 7 - - - - - - - - TAB - 5 - - - - - A - 4 - - 3 - 1 - eighth - - up - begin - - - 2 - 10 - - - - - - A - 4 - - 1 - 1 - 16th - up - continue - - - 2 - 10 - - - - - - A - 4 - - 2 - 1 - eighth - up - end - - - 2 - 10 - - - - - - A - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 10 - - - - - - G - 4 - - 2 - 1 - eighth - up - continue - - - 2 - 8 - - - - - - F - 4 - - 2 - 1 - eighth - up - end - - - 3 - 10 - - - - - - - - TAB - 5 - - - - - E - 4 - - 3 - 1 - eighth - - up - begin - - - 3 - 9 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - - - 3 - 10 - - - - - - G - 4 - - 2 - 1 - eighth - up - end - - - 2 - 8 - - - - - - G - 4 - - 3 - 1 - eighth - - up - begin - - - 2 - 8 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - - - 3 - 10 - - - - - - E - 4 - - 2 - 1 - eighth - up - end - - - 3 - 9 - - - - - - - - TAB - 5 - - - - - D - 4 - - 3 - 1 - eighth - - up - begin - - - 3 - 7 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - - - 3 - 9 - - - - - - F - 4 - - 2 - 1 - eighth - up - end - - - 3 - 10 - - - - - - F - 4 - - 3 - 1 - eighth - - up - begin - - - 3 - 10 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - - - 3 - 9 - - - - - - D - 4 - - 2 - 1 - eighth - up - end - - - 3 - 7 - - - - - - - - TAB - 5 - - - - - C - 4 - - 3 - 1 - eighth - - up - begin - - - 4 - 10 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - - - 3 - 9 - - - - - - F - 4 - - 2 - 1 - eighth - up - end - - - 3 - 10 - - - - - - E - 4 - - 6 - 1 - quarter - - up - - - 3 - 9 - - - - - - - - TAB - 5 - - - - - 4 - 1 - quarter - - - - A - 4 - - 1 - 1 - 16th - up - begin - begin - - - 2 - 10 - - - - - - G - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 8 - - - - - - A - 4 - - 6 - - 1 - quarter - - up - - - 2 - 10 - - - - - - - - - TAB - 5 - - - - - A - 4 - - 4 - - 1 - quarter - up - - - 2 - 10 - - - - - - - A - 4 - - 1 - 1 - 16th - up - begin - begin - - - 2 - 10 - - - - - - G - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 8 - - - - - - E - 4 - - 6 - 1 - quarter - - up - - - 3 - 9 - - - - - - - - TAB - 5 - - - - - 4 - 1 - quarter - - - - A - 4 - - 1 - 1 - 16th - up - begin - begin - - - 2 - 10 - - - - - - G - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 8 - - - - - - A - 4 - - 6 - 1 - quarter - - up - - - 2 - 10 - - - - - - - - TAB - 5 - - - - - A - 4 - - 3 - 1 - eighth - - up - begin - - - 2 - 10 - - - - - - G - 4 - - 1 - 1 - 16th - up - continue - - - 2 - 8 - - - - - - E - 4 - - 2 - 1 - eighth - up - end - - - 3 - 9 - - - - - - D - 4 - - 6 - 1 - quarter - - up - - - 3 - 7 - - - - - - - - TAB - 5 - - - - - 4 - 1 - quarter - - - - A - 4 - - 1 - 1 - 16th - up - begin - begin - - - 2 - 10 - - - - - - G - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 8 - - - - - - A - 4 - - 6 - - 1 - quarter - - up - - - 2 - 10 - - - - - - - - - TAB - 5 - - - - - A - 4 - - 4 - - 1 - quarter - up - - - 2 - 10 - - - - - - - A - 4 - - 1 - 1 - 16th - up - begin - begin - - - 2 - 10 - - - - - - G - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 8 - - - - - - E - 4 - - 6 - 1 - quarter - - up - - - 3 - 9 - - - - - - - - TAB - 5 - - - - - 4 - 1 - quarter - - - - A - 4 - - 1 - 1 - 16th - up - begin - begin - - - 2 - 10 - - - - - - G - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 8 - - - - - - A - 4 - - 6 - 1 - quarter - - up - - - 2 - 10 - - - - - - - - TAB - 5 - - - - - A - 4 - - 3 - 1 - eighth - - up - begin - - - 2 - 10 - - - - - - G - 4 - - 1 - 1 - 16th - up - continue - - - 2 - 8 - - - - - - E - 4 - - 2 - 1 - eighth - up - end - - - 3 - 9 - - - - - - D - 4 - - 6 - 1 - quarter - - up - - - 3 - 7 - - - - - - - - TAB - 5 - - - - - A - 4 - - 3 - 1 - eighth - - up - begin - - - 2 - 10 - - - - - - A - 4 - - 1 - 1 - 16th - up - continue - - - 2 - 10 - - - - - - A - 4 - - 2 - 1 - eighth - up - end - - - 2 - 10 - - - - - - A - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 10 - - - - - - G - 4 - - 2 - 1 - eighth - up - continue - - - 2 - 8 - - - - - - F - 4 - - 2 - 1 - eighth - up - end - - - 3 - 10 - - - - - - - - TAB - 5 - - - - - E - 4 - - 3 - 1 - eighth - - up - begin - - - 3 - 9 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - - - 3 - 10 - - - - - - G - 4 - - 2 - 1 - eighth - up - end - - - 2 - 8 - - - - - - G - 4 - - 3 - 1 - eighth - - up - begin - - - 2 - 8 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - - - 3 - 10 - - - - - - E - 4 - - 2 - 1 - eighth - up - end - - - 3 - 9 - - - - - - - - TAB - 5 - - - - - D - 4 - - 3 - 1 - eighth - - up - begin - - - 3 - 7 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - - - 3 - 9 - - - - - - F - 4 - - 2 - 1 - eighth - up - end - - - 3 - 10 - - - - - - F - 4 - - 3 - 1 - eighth - - up - begin - - - 3 - 10 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - - - 3 - 9 - - - - - - D - 4 - - 2 - 1 - eighth - up - end - - - 3 - 7 - - - - - - - - TAB - 5 - - - - - C - 4 - - 3 - 1 - eighth - - up - begin - - - 4 - 10 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - - - 3 - 9 - - - - - - F - 4 - - 2 - 1 - eighth - up - end - - - 3 - 10 - - - - - - D - 4 - - 3 - 1 - eighth - - up - begin - - - 3 - 7 - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - - - 3 - 7 - - - - - - D - 4 - - 2 - 1 - eighth - up - end - - - 3 - 7 - - - - - - - - TAB - 5 - - - - - A - 4 - - 3 - 1 - eighth - - up - begin - - - 2 - 10 - - - - - - A - 4 - - 1 - 1 - 16th - up - continue - - - 2 - 10 - - - - - - A - 4 - - 2 - 1 - eighth - up - end - - - 2 - 10 - - - - - - A - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 10 - - - - - - G - 4 - - 2 - 1 - eighth - up - continue - - - 2 - 8 - - - - - - F - 4 - - 2 - 1 - eighth - up - end - - - 3 - 10 - - - - - - - - TAB - 5 - - - - - E - 4 - - 3 - 1 - eighth - - up - begin - - - 3 - 9 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - - - 3 - 10 - - - - - - G - 4 - - 2 - 1 - eighth - up - end - - - 2 - 8 - - - - - - G - 4 - - 3 - 1 - eighth - - up - begin - - - 2 - 8 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - - - 3 - 10 - - - - - - E - 4 - - 2 - 1 - eighth - up - end - - - 3 - 9 - - - - - - - - TAB - 5 - - - - - D - 4 - - 3 - 1 - eighth - - up - begin - - - 3 - 7 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - - - 3 - 9 - - - - - - F - 4 - - 2 - 1 - eighth - up - end - - - 3 - 10 - - - - - - F - 4 - - 3 - 1 - eighth - - up - begin - - - 3 - 10 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - - - 3 - 9 - - - - - - D - 4 - - 2 - 1 - eighth - up - end - - - 3 - 7 - - - - - - - - TAB - 5 - - - - - C - 4 - - 3 - 1 - eighth - - up - begin - - - 4 - 10 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - - - 3 - 9 - - - - - - F - 4 - - 2 - 1 - eighth - up - end - - - 3 - 10 - - - - - - E - 4 - - 3 - 1 - eighth - - up - begin - - - 3 - 9 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - - - 3 - 9 - - - - - - E - 4 - - 2 - 1 - eighth - up - end - - - 3 - 9 - - - - - - - - TAB - 5 - - - - - D - 4 - - 1 - 1 - 16th - up - begin - begin - - - 3 - 7 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 9 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 10 - - - - - - G - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 8 - - - - - - A - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 10 - - - - - - A - 1 - 4 - - 1 - 1 - 16th - sharp - up - end - end - - - 2 - 11 - - - - - - A - 4 - - 1 - 1 - 16th - natural - up - begin - begin - - - 2 - 10 - - - - - - G - 4 - - 1 - 1 - 16th - up - continue - end - - - 2 - 8 - - - - - - F - 4 - - 2 - 1 - eighth - up - continue - - - 3 - 10 - - - - - - C - 4 - - 2 - 1 - eighth - up - end - - - 4 - 10 - - - - - - - - TAB - 5 - - - - - D - 4 - - 2 - 1 - eighth - up - begin - - - 3 - 7 - - - - - - A - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 10 - - - - - - G - 4 - - 1 - 1 - 16th - up - continue - end - - - 2 - 8 - - - - - - F - 4 - - 2 - 1 - eighth - up - end - - - 3 - 10 - - - - - - A - 4 - - 3 - 1 - eighth - - up - begin - - - 2 - 10 - - - - - - A - 1 - 4 - - 1 - 1 - 16th - sharp - up - continue - - - 2 - 11 - - - - - - G - 4 - - 2 - 1 - eighth - up - end - - - 2 - 8 - - - - - - - - TAB - 5 - - - - - D - 4 - - 1 - 1 - 16th - up - begin - begin - - - 3 - 7 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 9 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 10 - - - - - - G - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 8 - - - - - - A - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 10 - - - - - - A - 1 - 4 - - 1 - 1 - 16th - sharp - up - end - end - - - 2 - 11 - - - - - - A - 4 - - 1 - 1 - 16th - natural - up - begin - begin - - - 2 - 10 - - - - - - G - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 8 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 10 - - - - - - G - 4 - - 1 - 1 - 16th - up - continue - end - - - 2 - 8 - - - - - - E - 4 - - 2 - 1 - eighth - up - end - - - 3 - 9 - - - - - - - - TAB - 5 - - - - - A - 1 - 4 - - 3 - 1 - eighth - - sharp - up - begin - - - 2 - 11 - - - - - - C - 5 - - 1 - 1 - 16th - up - continue - - - 1 - 8 - - - - - - A - 1 - 4 - - 2 - 1 - eighth - up - end - - - 2 - 11 - - - - - - A - 4 - - 6 - 1 - quarter - - natural - up - - - 2 - 10 - - - - - - - - TAB - 5 - - - - - D - 4 - - 1 - 1 - 16th - up - begin - begin - - - 3 - 7 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 9 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 10 - - - - - - G - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 8 - - - - - - A - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 10 - - - - - - A - 1 - 4 - - 1 - 1 - 16th - sharp - up - end - end - - - 2 - 11 - - - - - - A - 4 - - 1 - 1 - 16th - natural - up - begin - begin - - - 2 - 10 - - - - - - G - 4 - - 1 - 1 - 16th - up - continue - end - - - 2 - 8 - - - - - - F - 4 - - 2 - 1 - eighth - up - continue - - - 3 - 10 - - - - - - C - 4 - - 2 - 1 - eighth - up - end - - - 4 - 10 - - - - - - - - TAB - 5 - - - - - D - 4 - - 2 - 1 - eighth - up - begin - - - 3 - 7 - - - - - - A - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 10 - - - - - - G - 4 - - 1 - 1 - 16th - up - continue - end - - - 2 - 8 - - - - - - F - 4 - - 2 - 1 - eighth - up - end - - - 3 - 10 - - - - - - A - 4 - - 3 - 1 - eighth - - up - begin - - - 2 - 10 - - - - - - A - 1 - 4 - - 1 - 1 - 16th - sharp - up - continue - - - 2 - 11 - - - - - - G - 4 - - 2 - 1 - eighth - up - end - - - 2 - 8 - - - - - - - - TAB - 5 - - - - - D - 4 - - 1 - 1 - 16th - up - begin - begin - - - 3 - 7 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 9 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 10 - - - - - - G - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 8 - - - - - - A - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 10 - - - - - - A - 1 - 4 - - 1 - 1 - 16th - sharp - up - end - end - - - 2 - 11 - - - - - - A - 4 - - 1 - 1 - 16th - natural - up - begin - begin - - - 2 - 10 - - - - - - G - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 8 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 10 - - - - - - G - 4 - - 1 - 1 - 16th - up - continue - end - - - 2 - 8 - - - - - - E - 4 - - 2 - 1 - eighth - up - end - - - 3 - 9 - - - - - - - - TAB - 5 - - - - - A - 1 - 4 - - 3 - 1 - eighth - - sharp - up - begin - - - 2 - 11 - - - - - - C - 5 - - 1 - 1 - 16th - up - continue - - - 1 - 8 - - - - - - A - 1 - 4 - - 2 - 1 - eighth - up - end - - - 2 - 11 - - - - - - A - 4 - - 6 - 1 - quarter - - natural - up - - - 2 - 10 - - - - - - - - TAB - 5 - - - - - A - 4 - - 3 - 1 - eighth - - up - begin - - - 2 - 10 - - - - - - A - 4 - - 1 - 1 - 16th - up - continue - - - 2 - 10 - - - - - - A - 4 - - 2 - 1 - eighth - up - end - - - 2 - 10 - - - - - - A - 4 - - 3 - 1 - eighth - - up - begin - - - 2 - 10 - - - - - - G - 4 - - 1 - 1 - 16th - up - continue - - - 2 - 8 - - - - - - F - 4 - - 2 - 1 - eighth - up - end - - - 3 - 10 - - - - - - - - TAB - 5 - - - - - E - 4 - - 3 - 1 - eighth - - up - begin - - - 3 - 9 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - - - 3 - 10 - - - - - - G - 4 - - 2 - 1 - eighth - up - end - - - 2 - 8 - - - - - - G - 4 - - 3 - 1 - eighth - - up - begin - - - 2 - 8 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - - - 3 - 10 - - - - - - E - 4 - - 2 - 1 - eighth - up - end - - - 3 - 9 - - - - - - - - TAB - 5 - - - - - D - 4 - - 3 - 1 - eighth - - up - begin - - - 3 - 7 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - - - 3 - 9 - - - - - - F - 4 - - 2 - 1 - eighth - up - end - - - 3 - 10 - - - - - - F - 4 - - 3 - 1 - eighth - - up - begin - - - 3 - 10 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - - - 3 - 9 - - - - - - D - 4 - - 2 - 1 - eighth - up - end - - - 3 - 7 - - - - - - - - TAB - 5 - - - - - C - 4 - - 3 - 1 - eighth - - up - begin - - - 4 - 10 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - - - 3 - 9 - - - - - - F - 4 - - 2 - 1 - eighth - up - end - - - 3 - 10 - - - - - - D - 4 - - 3 - 1 - eighth - - up - begin - - - 3 - 7 - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - - - 3 - 7 - - - - - - D - 4 - - 2 - 1 - eighth - up - end - - - 3 - 7 - - - - - - - - TAB - 5 - - - - - A - 4 - - 3 - 1 - eighth - - up - begin - - - 2 - 10 - - - - - - A - 4 - - 1 - 1 - 16th - up - continue - - - 2 - 10 - - - - - - A - 4 - - 2 - 1 - eighth - up - end - - - 2 - 10 - - - - - - A - 4 - - 3 - 1 - eighth - - up - begin - - - 2 - 10 - - - - - - G - 4 - - 1 - 1 - 16th - up - continue - - - 2 - 8 - - - - - - F - 4 - - 2 - 1 - eighth - up - end - - - 3 - 10 - - - - - - - - TAB - 5 - - - - - E - 4 - - 3 - 1 - eighth - - up - begin - - - 3 - 9 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - - - 3 - 10 - - - - - - G - 4 - - 2 - 1 - eighth - up - end - - - 2 - 8 - - - - - - G - 4 - - 3 - 1 - eighth - - up - begin - - - 2 - 8 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - - - 3 - 10 - - - - - - E - 4 - - 2 - 1 - eighth - up - end - - - 3 - 9 - - - - - - - - TAB - 5 - - - - - D - 4 - - 3 - 1 - eighth - - up - begin - - - 3 - 7 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - - - 3 - 9 - - - - - - F - 4 - - 2 - 1 - eighth - up - end - - - 3 - 10 - - - - - - F - 4 - - 3 - 1 - eighth - - up - begin - - - 3 - 10 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - - - 3 - 9 - - - - - - D - 4 - - 2 - 1 - eighth - up - end - - - 3 - 7 - - - - - - - - TAB - 5 - - - - - C - 4 - - 3 - 1 - eighth - - up - begin - - - 4 - 10 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - - - 3 - 9 - - - - - - F - 4 - - 2 - 1 - eighth - up - end - - - 3 - 10 - - - - - - E - 4 - - 3 - 1 - eighth - - up - begin - - - 3 - 9 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - - - 3 - 9 - - - - - - E - 4 - - 2 - 1 - eighth - up - end - - - 3 - 9 - - - - - - - - TAB - 5 - - - - - 4 - 1 - quarter - - - - A - 4 - - 1 - 1 - 16th - up - begin - begin - - - 2 - 10 - - - - - - G - 4 - - 1 - 1 - 16th - down - end - end - - - 2 - 8 - - - - - - A - 4 - - 6 - - 1 - quarter - - down - - - 2 - 10 - - - - - - - - - TAB - 5 - - - - - A - 4 - - 4 - - 1 - quarter - down - - - 2 - 10 - - - - - - - A - 4 - - 1 - 1 - 16th - down - begin - begin - - - 2 - 10 - - - - - - G - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 8 - - - - - - E - 4 - - 6 - 1 - quarter - - down - - - 3 - 9 - - - - - - - - TAB - 5 - - - - - 4 - 1 - quarter - - - - A - 4 - - 1 - 1 - 16th - up - begin - begin - - - 2 - 10 - - - - - - G - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 8 - - - - - - A - 4 - - 6 - 1 - quarter - - up - - - 2 - 10 - - - - - - - - TAB - 5 - - - - - A - 4 - - 3 - 1 - eighth - - up - begin - - - 2 - 10 - - - - - - G - 4 - - 1 - 1 - 16th - up - continue - - - 2 - 8 - - - - - - E - 4 - - 2 - 1 - eighth - up - end - - - 3 - 9 - - - - - - D - 4 - - 6 - 1 - quarter - - up - - - 3 - 7 - - - - - - - - TAB - 5 - - - - - 4 - 1 - quarter - - - - A - 4 - - 1 - 1 - 16th - up - begin - begin - - - 2 - 10 - - - - - - G - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 8 - - - - - - A - 4 - - 6 - - 1 - quarter - - up - - - 2 - 10 - - - - - - - - - TAB - 5 - - - - - A - 4 - - 4 - - 1 - quarter - up - - - 2 - 10 - - - - - - - A - 4 - - 1 - 1 - 16th - up - begin - begin - - - 2 - 10 - - - - - - G - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 8 - - - - - - E - 4 - - 6 - 1 - quarter - - up - - - 3 - 9 - - - - - - - - TAB - 5 - - - - - 4 - 1 - quarter - - - - A - 4 - - 1 - 1 - 16th - up - begin - begin - - - 2 - 10 - - - - - - G - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 8 - - - - - - A - 4 - - 6 - 1 - quarter - - up - - - 2 - 10 - - - - - - - - TAB - 5 - - - - - A - 4 - - 3 - 1 - eighth - - down - begin - - - 2 - 10 - - - - - - G - 4 - - 1 - 1 - 16th - down - continue - - - 2 - 8 - - - - - - E - 4 - - 2 - 1 - eighth - up - end - - - 3 - 9 - - - - - - D - 4 - - 6 - 1 - quarter - - up - - - 3 - 7 - - - - - - - - TAB - 5 - - - - - A - 4 - - 3 - 1 - eighth - - down - begin - - - 2 - 10 - - - - - - A - 4 - - 1 - 1 - 16th - down - continue - - - 2 - 10 - - - - - - A - 4 - - 2 - 1 - eighth - down - end - - - 2 - 10 - - - - - - A - 4 - - 3 - 1 - eighth - - down - begin - - - 2 - 10 - - - - - - G - 4 - - 1 - 1 - 16th - up - continue - - - 2 - 8 - - - - - - F - 4 - - 2 - 1 - eighth - up - end - - - 3 - 10 - - - - - - - - TAB - 5 - - - - - E - 4 - - 3 - 1 - eighth - - down - begin - - - 3 - 9 - - - - - - F - 4 - - 1 - 1 - 16th - down - continue - - - 3 - 10 - - - - - - G - 4 - - 2 - 1 - eighth - down - end - - - 2 - 8 - - - - - - G - 4 - - 3 - 1 - eighth - - down - begin - - - 2 - 8 - - - - - - F - 4 - - 1 - 1 - 16th - down - continue - - - 3 - 10 - - - - - - E - 4 - - 2 - 1 - eighth - up - end - - - 3 - 9 - - - - - - - - TAB - 5 - - - - - D - 4 - - 3 - 1 - eighth - - down - begin - - - 3 - 7 - - - - - - E - 4 - - 1 - 1 - 16th - down - continue - - - 3 - 9 - - - - - - F - 4 - - 2 - 1 - eighth - down - end - - - 3 - 10 - - - - - - F - 4 - - 3 - 1 - eighth - - down - begin - - - 3 - 10 - - - - - - E - 4 - - 1 - 1 - 16th - down - continue - - - 3 - 9 - - - - - - D - 4 - - 2 - 1 - eighth - down - end - - - 3 - 7 - - - - - - - - TAB - 5 - - - - - C - 4 - - 3 - 1 - eighth - - up - begin - - - 4 - 10 - - - - - - E - 4 - - 1 - 1 - 16th - down - continue - - - 3 - 9 - - - - - - F - 4 - - 2 - 1 - eighth - down - end - - - 3 - 10 - - - - - - D - 4 - - 3 - 1 - eighth - - down - begin - - - 3 - 7 - - - - - - D - 4 - - 1 - 1 - 16th - down - continue - - - 3 - 7 - - - - - - D - 4 - - 2 - 1 - eighth - down - end - - - 3 - 7 - - - - - - - - TAB - 5 - - - - - A - 4 - - 3 - 1 - eighth - - down - begin - - - 2 - 10 - - - - - - A - 4 - - 1 - 1 - 16th - down - continue - - - 2 - 10 - - - - - - A - 4 - - 2 - 1 - eighth - down - end - - - 2 - 10 - - - - - - A - 4 - - 3 - 1 - eighth - - down - begin - - - 2 - 10 - - - - - - G - 4 - - 1 - 1 - 16th - down - continue - - - 2 - 8 - - - - - - F - 4 - - 2 - 1 - eighth - down - end - - - 3 - 10 - - - - - - - - TAB - 5 - - - - - E - 4 - - 3 - 1 - eighth - - down - begin - - - 3 - 9 - - - - - - F - 4 - - 1 - 1 - 16th - down - continue - - - 3 - 10 - - - - - - G - 4 - - 2 - 1 - eighth - down - end - - - 2 - 8 - - - - - - G - 4 - - 3 - 1 - eighth - - down - begin - - - 2 - 8 - - - - - - F - 4 - - 1 - 1 - 16th - down - continue - - - 3 - 10 - - - - - - E - 4 - - 2 - 1 - eighth - down - end - - - 3 - 9 - - - - - - - - TAB - 5 - - - - - D - 4 - - 3 - 1 - eighth - - down - begin - - - 3 - 7 - - - - - - E - 4 - - 1 - 1 - 16th - down - continue - - - 3 - 9 - - - - - - F - 4 - - 2 - 1 - eighth - down - end - - - 3 - 10 - - - - - - F - 4 - - 3 - 1 - eighth - - down - begin - - - 3 - 10 - - - - - - E - 4 - - 1 - 1 - 16th - down - continue - - - 3 - 9 - - - - - - D - 4 - - 2 - 1 - eighth - down - end - - - 3 - 7 - - - - - - - - TAB - 5 - - - - - C - 4 - - 3 - 1 - eighth - - down - begin - - - 4 - 10 - - - - - - E - 4 - - 1 - 1 - 16th - down - continue - - - 3 - 9 - - - - - - F - 4 - - 2 - 1 - eighth - down - end - - - 3 - 10 - - - - - - E - 4 - - 3 - 1 - eighth - - down - begin - - - 3 - 9 - - - - - - E - 4 - - 1 - 1 - 16th - down - continue - - - 3 - 9 - - - - - - E - 4 - - 2 - 1 - eighth - down - end - - - 3 - 9 - - - - - - - 1 - - TAB - 5 - - - - - D - 4 - - 3 - 1 - half - - down - - - 3 - 7 - - - - - - - - - 1 - - 0 - major - - - - TAB - 5 - - - 6 - - D - 2 - - - G - 2 - - - C - 3 - - - F - 3 - - - A - 3 - - - D - 4 - - - - 0 - 0 - 0 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - 2 - - - TAB - 5 - - - - - 2 - 1 - quarter - - - - 1 - 1 - eighth - - - - - - - TAB - 5 - - - - - F - 4 - - 1 - 1 - eighth - up - - - 2 - 6 - - - - - - - D - 4 - - 1 - 1 - eighth - up - - - 3 - 7 - - - - - - - A - 3 - - 1 - 1 - eighth - up - - - 4 - 7 - - - - - - - D - 3 - - 1 - 1 - eighth - up - - - 5 - 5 - - - - - - 2 - 1 - quarter - - - - 2 - 1 - quarter - - - - E - 4 - - 1 - 1 - eighth - up - - - 2 - 5 - - - - - - - C - 4 - - 1 - 1 - eighth - up - - - 3 - 5 - - - - - - - G - 3 - - 1 - 1 - eighth - up - - - 4 - 5 - - - - - - - C - 3 - - 1 - 1 - eighth - up - - - 5 - 3 - - - - - - - - TAB - 5 - - - - - F - 4 - - 1 - 1 - eighth - up - - - 2 - 6 - - - - - - - D - 4 - - 1 - 1 - eighth - up - - - 3 - 7 - - - - - - - A - 3 - - 1 - 1 - eighth - up - - - 4 - 7 - - - - - - - D - 3 - - 1 - 1 - eighth - up - - - 5 - 5 - - - - - - 2 - 1 - quarter - - - - 2 - 1 - quarter - - - - 1 - 1 - eighth - - - - - - TAB - 5 - - - - - F - 4 - - 1 - 1 - eighth - up - - - 2 - 6 - - - - - - - D - 4 - - 1 - 1 - eighth - up - - - 3 - 7 - - - - - - - A - 3 - - 1 - 1 - eighth - up - - - 4 - 7 - - - - - - - D - 3 - - 1 - 1 - eighth - up - - - 5 - 5 - - - - - - 2 - 1 - quarter - - - - 2 - 1 - quarter - - - - E - 4 - - 1 - 1 - eighth - up - - - 2 - 5 - - - - - - - C - 4 - - 1 - 1 - eighth - up - - - 3 - 5 - - - - - - - G - 3 - - 1 - 1 - eighth - up - - - 4 - 5 - - - - - - - C - 3 - - 1 - 1 - eighth - up - - - 5 - 3 - - - - - - - 4 - - TAB - 5 - - - - - D - 4 - - 3 - 1 - eighth - - up - begin - - - 3 - 7 - - - - - - - A - 3 - - 3 - 1 - eighth - - up - begin - - - 4 - 7 - - - - - - - D - 3 - - 3 - 1 - eighth - - up - begin - - - 5 - 5 - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 7 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 7 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - begin - - - 5 - 5 - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 7 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 7 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 5 - - - - - - D - 4 - - 1 - 1 - 16th - up - end - end - - - 3 - 7 - - - - - - - A - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 7 - - - - - - - D - 3 - - 1 - 1 - 16th - up - end - end - - - 5 - 5 - - - - - - C - 4 - - 3 - 1 - eighth - - up - begin - - - 3 - 5 - - - - - - - G - 3 - - 3 - 1 - eighth - - up - begin - - - 4 - 5 - - - - - - - C - 3 - - 3 - 1 - eighth - - up - begin - - - 5 - 3 - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 5 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 5 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - begin - - - 5 - 3 - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 5 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 5 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 3 - - - - - - C - 4 - - 1 - 1 - 16th - up - end - end - - - 3 - 5 - - - - - - - G - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 5 - - - - - - - C - 3 - - 1 - 1 - 16th - up - end - end - - - 5 - 3 - - - - - - - - TAB - 5 - - - - - F - 4 - - 3 - 1 - eighth - - up - begin - - - 1 - 1 - - - - - - - D - 4 - - 3 - 1 - eighth - - up - begin - - - 2 - 3 - - - - - - - A - 3 - - 3 - 1 - eighth - - up - begin - - - 3 - 2 - - - - - - - D - 3 - - 3 - 1 - eighth - - up - begin - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - begin - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - end - end - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 0 - - - - - - F - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 1 - - - - - - - D - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 3 - - - - - - - A - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 2 - - - - - - - D - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 0 - - - - - - F - 4 - - 2 - 1 - eighth - up - continue - - - 1 - 1 - - - - - - - D - 4 - - 2 - 1 - eighth - up - continue - - - 2 - 3 - - - - - - - A - 3 - - 2 - 1 - eighth - up - continue - - - 3 - 2 - - - - - - - D - 3 - - 2 - 1 - eighth - up - continue - - - 4 - 0 - - - - - - F - 4 - - 2 - 1 - eighth - up - end - - - 1 - 1 - - - - - - - D - 4 - - 2 - 1 - eighth - up - end - - - 2 - 3 - - - - - - - A - 3 - - 2 - 1 - eighth - up - end - - - 3 - 2 - - - - - - - D - 3 - - 2 - 1 - eighth - up - end - - - 4 - 0 - - - - - - - - TAB - 5 - - - - - E - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 0 - - - - - - - C - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 1 - - - - - - - G - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 0 - - - - - - - E - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 2 - - - - - - - C - 3 - - 2 - 1 - eighth - up - begin - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - begin - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - begin - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - end - end - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - end - end - - - 5 - 3 - - - - - - E - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 0 - - - - - - - C - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 1 - - - - - - - G - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 0 - - - - - - - E - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 2 - - - - - - - C - 3 - - 2 - 1 - eighth - up - begin - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - begin - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - begin - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - end - end - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - end - end - - - 5 - 3 - - - - - - - - TAB - 5 - - - - - E - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 0 - - - - - - - C - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 1 - - - - - - - A - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 2 - - - - - - - E - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 2 - - - - - - - A - 2 - - 2 - 1 - eighth - up - begin - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - begin - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - continue - begin - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - end - end - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - end - end - - - 5 - 0 - - - - - - E - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 0 - - - - - - - C - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 1 - - - - - - - A - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 2 - - - - - - - E - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 2 - - - - - - - A - 2 - - 2 - 1 - eighth - up - begin - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - begin - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - continue - begin - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - end - end - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - end - end - - - 5 - 0 - - - - - - - - TAB - 5 - - - - - C - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 1 - - - - - - - A - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 2 - - - - - - - F - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 3 - - - - - - - C - 3 - - 2 - 1 - eighth - up - begin - - - 5 - 3 - - - - - - - F - 2 - - 2 - 1 - eighth - up - begin - - - 6 - 1 - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 2 - - - - - - - F - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 3 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - begin - - - 5 - 3 - - - - - - - F - 2 - - 1 - 1 - 16th - up - continue - begin - - - 6 - 1 - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - F - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 3 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 3 - - - - - - - F - 2 - - 1 - 1 - 16th - up - continue - continue - - - 6 - 1 - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - F - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 3 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 3 - - - - - - - F - 2 - - 1 - 1 - 16th - up - continue - continue - - - 6 - 1 - - - - - - C - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 2 - - - - - - - F - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 3 - - - - - - - C - 3 - - 1 - 1 - 16th - up - end - end - - - 5 - 3 - - - - - - - F - 2 - - 1 - 1 - 16th - up - end - end - - - 6 - 1 - - - - - - D - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 3 - - - - - - - B - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 4 - - - - - - - G - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 5 - - - - - - - D - 3 - - 2 - 1 - eighth - up - begin - - - 5 - 5 - - - - - - - G - 2 - - 2 - 1 - eighth - up - begin - - - 6 - 3 - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 3 - - - - - - - B - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 4 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 5 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - begin - - - 5 - 5 - - - - - - - G - 2 - - 1 - 1 - 16th - up - continue - begin - - - 6 - 3 - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 3 - - - - - - - B - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 4 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 5 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 5 - - - - - - - G - 2 - - 1 - 1 - 16th - up - continue - continue - - - 6 - 3 - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 3 - - - - - - - B - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 4 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 5 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 5 - - - - - - - G - 2 - - 1 - 1 - 16th - up - continue - continue - - - 6 - 3 - - - - - - D - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 3 - - - - - - - B - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 4 - - - - - - - G - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 5 - - - - - - - D - 3 - - 1 - 1 - 16th - up - end - end - - - 5 - 5 - - - - - - - G - 2 - - 1 - 1 - 16th - up - end - end - - - 6 - 3 - - - - - - - - TAB - 5 - - - - - F - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 1 - - - - - - - D - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 3 - - - - - - - A - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 2 - - - - - - - D - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - begin - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - end - end - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 0 - - - - - - F - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 1 - - - - - - - D - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 3 - - - - - - - A - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 2 - - - - - - - D - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - begin - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - end - end - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 0 - - - - - - - - TAB - 5 - - - - - E - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 0 - - - - - - - C - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 1 - - - - - - - G - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 0 - - - - - - - E - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 2 - - - - - - - C - 3 - - 2 - 1 - eighth - up - begin - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - begin - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - begin - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - end - end - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - end - end - - - 5 - 3 - - - - - - E - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 0 - - - - - - - C - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 1 - - - - - - - G - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 0 - - - - - - - E - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 2 - - - - - - - C - 3 - - 2 - 1 - eighth - up - begin - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - begin - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - begin - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - end - end - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - end - end - - - 5 - 3 - - - - - - - - TAB - 5 - - - - - E - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 0 - - - - - - - C - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 1 - - - - - - - A - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 2 - - - - - - - E - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 2 - - - - - - - A - 2 - - 2 - 1 - eighth - up - begin - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - begin - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - continue - begin - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - end - end - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - end - end - - - 5 - 0 - - - - - - E - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 0 - - - - - - - C - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 1 - - - - - - - A - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 2 - - - - - - - E - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 2 - - - - - - - A - 2 - - 2 - 1 - eighth - up - begin - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - begin - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - continue - begin - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - end - end - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - end - end - - - 5 - 0 - - - - - - - - TAB - 5 - - - - - C - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 1 - - - - - - - A - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 2 - - - - - - - F - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 3 - - - - - - - C - 3 - - 2 - 1 - eighth - up - begin - - - 5 - 3 - - - - - - - F - 2 - - 2 - 1 - eighth - up - begin - - - 6 - 1 - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 2 - - - - - - - F - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 3 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - begin - - - 5 - 3 - - - - - - - F - 2 - - 1 - 1 - 16th - up - continue - begin - - - 6 - 1 - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - F - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 3 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 3 - - - - - - - F - 2 - - 1 - 1 - 16th - up - continue - continue - - - 6 - 1 - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - F - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 3 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 3 - - - - - - - F - 2 - - 1 - 1 - 16th - up - continue - continue - - - 6 - 1 - - - - - - C - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 2 - - - - - - - F - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 3 - - - - - - - C - 3 - - 1 - 1 - 16th - up - end - end - - - 5 - 3 - - - - - - - F - 2 - - 1 - 1 - 16th - up - end - end - - - 6 - 1 - - - - - - E - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 0 - - - - - - - C - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 1 - - - - - - - G - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 0 - - - - - - - E - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 2 - - - - - - - C - 3 - - 2 - 1 - eighth - up - begin - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - begin - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - begin - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - end - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - end - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - end - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - end - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - end - - - 5 - 3 - - - - - - E - 4 - - 2 - 1 - eighth - up - end - - - 1 - 0 - - - - - - - C - 4 - - 2 - 1 - eighth - up - end - - - 2 - 1 - - - - - - - G - 3 - - 2 - 1 - eighth - up - end - - - 3 - 0 - - - - - - - E - 3 - - 2 - 1 - eighth - up - end - - - 4 - 2 - - - - - - - C - 3 - - 2 - 1 - eighth - up - end - - - 5 - 3 - - - - - - - - TAB - 5 - - - - - D - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 0 - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 2 - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 3 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - continue - - - H - 1 - 1 - - - - - - - E - 4 - - 1 - 1 - 16th - up - end - end - - - - 1 - 0 - - - - - - - F - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 1 - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 2 - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 3 - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - D - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 3 - - - - - - - - TAB - 5 - - - - - D - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 0 - - - - - - A - 3 - - 2 - 1 - eighth - up - continue - - - 3 - 2 - - - - - - F - 4 - - 2 - 1 - eighth - up - end - - - 1 - 1 - - - - - - C - 4 - - 3 - 1 - eighth - - up - begin - - - 3 - 5 - - - - - - - G - 3 - - 3 - 1 - eighth - - up - begin - - - 4 - 5 - - - - - - - C - 3 - - 3 - 1 - eighth - - up - begin - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 5 - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 5 - - - - - - E - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 5 - - - - - - - - TAB - 5 - - - - - D - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 0 - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 2 - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 3 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - continue - - - H - 1 - 1 - - - - - - - E - 4 - - 1 - 1 - 16th - up - end - end - - - - 1 - 0 - - - - - - - F - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 1 - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 2 - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 3 - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - D - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 3 - - - - - - - 2 - - TAB - 5 - - - - - C - 4 - - 1 - 1 - eighth - up - begin - - - 3 - 5 - - - - - - - G - 3 - - 1 - 1 - eighth - up - begin - - - 4 - 5 - - - - - - - C - 3 - - 1 - 1 - eighth - up - begin - - - 5 - 3 - - - - - - C - 4 - - 1 - 1 - eighth - up - continue - - - 3 - 5 - - - - - - E - 4 - - 1 - 1 - eighth - up - end - - - 2 - 5 - - - - - - D - 4 - - 2 - 1 - quarter - up - - - 2 - 3 - - - - - - - A - 3 - - 2 - 1 - quarter - up - - - 3 - 2 - - - - - - - D - 3 - - 2 - 1 - quarter - up - - - 4 - 0 - - - - - - A - 3 - - 1 - 1 - eighth - up - - - 3 - 2 - - - - - - - 4 - - TAB - 5 - - - - - D - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 0 - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 2 - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 3 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - continue - - - H - 1 - 1 - - - - - - - E - 4 - - 1 - 1 - 16th - up - end - end - - - - 1 - 0 - - - - - - - F - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 1 - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 2 - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 3 - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - D - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 3 - - - - - - - - TAB - 5 - - - - - D - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 0 - - - - - - A - 3 - - 2 - 1 - eighth - up - continue - - - 3 - 2 - - - - - - F - 4 - - 2 - 1 - eighth - up - end - - - 1 - 1 - - - - - - C - 4 - - 2 - 1 - eighth - up - begin - - - 3 - 5 - - - - - - - G - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 5 - - - - - - - C - 3 - - 2 - 1 - eighth - up - begin - - - 5 - 3 - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 5 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 5 - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 5 - - - - - - E - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 5 - - - - - - - - TAB - 5 - - - - - D - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 0 - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 2 - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 3 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - continue - - - H - 1 - 1 - - - - - - - E - 4 - - 1 - 1 - 16th - up - end - end - - - - 1 - 0 - - - - - - - F - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 1 - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 2 - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 3 - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - D - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 3 - - - - - - - - TAB - 5 - - - - - C - 4 - - 2 - 1 - eighth - up - begin - - - 3 - 5 - - - - - - - G - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 5 - - - - - - - C - 3 - - 2 - 1 - eighth - up - begin - - - 5 - 3 - - - - - - C - 4 - - 2 - 1 - eighth - up - continue - - - 3 - 5 - - - - - - E - 4 - - 2 - 1 - eighth - up - end - - - 2 - 5 - - - - - - D - 4 - - 3 - 1 - eighth - - up - begin - - - 2 - 3 - - - - - - - A - 3 - - 3 - 1 - eighth - - up - begin - - - 3 - 2 - - - - - - - D - 3 - - 3 - 1 - eighth - - up - begin - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - begin - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 2 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - F - 4 - - 1 - 1 - 16th - up - end - end - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 2 - - - - - - - - TAB - 5 - - - - - F - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 1 - - - - - - - D - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 3 - - - - - - - A - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 2 - - - - - - - D - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - begin - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - end - end - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 0 - - - - - - F - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 1 - - - - - - - D - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 3 - - - - - - - A - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 2 - - - - - - - D - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - begin - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - end - end - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 0 - - - - - - - - TAB - 5 - - - - - E - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 0 - - - - - - - C - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 1 - - - - - - - G - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 0 - - - - - - - E - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 2 - - - - - - - C - 3 - - 2 - 1 - eighth - up - begin - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - begin - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - begin - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - end - end - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - end - end - - - 5 - 3 - - - - - - E - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 0 - - - - - - - C - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 1 - - - - - - - G - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 0 - - - - - - - E - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 2 - - - - - - - C - 3 - - 2 - 1 - eighth - up - begin - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - begin - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - begin - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - end - end - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - end - end - - - 5 - 3 - - - - - - - - TAB - 5 - - - - - E - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 0 - - - - - - - C - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 1 - - - - - - - A - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 2 - - - - - - - E - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 2 - - - - - - - A - 2 - - 2 - 1 - eighth - up - begin - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - begin - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - continue - begin - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - end - end - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - end - end - - - 5 - 0 - - - - - - E - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 0 - - - - - - - C - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 1 - - - - - - - A - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 2 - - - - - - - E - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 2 - - - - - - - A - 2 - - 2 - 1 - eighth - up - begin - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - begin - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - continue - begin - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - end - end - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - end - end - - - 5 - 0 - - - - - - - - TAB - 5 - - - - - C - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 1 - - - - - - - A - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 2 - - - - - - - F - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 3 - - - - - - - C - 3 - - 2 - 1 - eighth - up - begin - - - 5 - 3 - - - - - - - F - 2 - - 2 - 1 - eighth - up - begin - - - 6 - 1 - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 2 - - - - - - - F - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 3 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - begin - - - 5 - 3 - - - - - - - F - 2 - - 1 - 1 - 16th - up - continue - begin - - - 6 - 1 - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - F - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 3 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 3 - - - - - - - F - 2 - - 1 - 1 - 16th - up - continue - continue - - - 6 - 1 - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - F - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 3 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 3 - - - - - - - F - 2 - - 1 - 1 - 16th - up - continue - continue - - - 6 - 1 - - - - - - C - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 2 - - - - - - - F - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 3 - - - - - - - C - 3 - - 1 - 1 - 16th - up - end - end - - - 5 - 3 - - - - - - - F - 2 - - 1 - 1 - 16th - up - end - end - - - 6 - 1 - - - - - - D - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 3 - - - - - - - B - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 4 - - - - - - - G - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 5 - - - - - - - D - 3 - - 2 - 1 - eighth - up - begin - - - 5 - 5 - - - - - - - G - 2 - - 2 - 1 - eighth - up - begin - - - 6 - 3 - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 3 - - - - - - - B - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 4 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 5 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - begin - - - 5 - 5 - - - - - - - G - 2 - - 1 - 1 - 16th - up - continue - begin - - - 6 - 3 - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 3 - - - - - - - B - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 4 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 5 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 5 - - - - - - - G - 2 - - 1 - 1 - 16th - up - continue - continue - - - 6 - 3 - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 3 - - - - - - - B - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 4 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 5 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 5 - - - - - - - G - 2 - - 1 - 1 - 16th - up - continue - continue - - - 6 - 3 - - - - - - D - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 3 - - - - - - - B - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 4 - - - - - - - G - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 5 - - - - - - - D - 3 - - 1 - 1 - 16th - up - end - end - - - 5 - 5 - - - - - - - G - 2 - - 1 - 1 - 16th - up - end - end - - - 6 - 3 - - - - - - - - TAB - 5 - - - - - F - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 1 - - - - - - - D - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 3 - - - - - - - A - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 2 - - - - - - - D - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - begin - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - end - end - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 0 - - - - - - F - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 1 - - - - - - - D - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 3 - - - - - - - A - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 2 - - - - - - - D - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - begin - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - end - end - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 0 - - - - - - - - TAB - 5 - - - - - E - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 0 - - - - - - - C - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 1 - - - - - - - G - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 0 - - - - - - - E - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 2 - - - - - - - C - 3 - - 2 - 1 - eighth - up - begin - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - begin - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - begin - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - end - end - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - end - end - - - 5 - 3 - - - - - - E - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 0 - - - - - - - C - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 1 - - - - - - - G - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 0 - - - - - - - E - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 2 - - - - - - - C - 3 - - 2 - 1 - eighth - up - begin - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - begin - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - begin - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - end - end - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - end - end - - - 5 - 3 - - - - - - - - TAB - 5 - - - - - E - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 0 - - - - - - - C - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 1 - - - - - - - A - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 2 - - - - - - - E - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 2 - - - - - - - A - 2 - - 2 - 1 - eighth - up - begin - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - begin - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - continue - begin - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - end - end - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - end - end - - - 5 - 0 - - - - - - E - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 0 - - - - - - - C - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 1 - - - - - - - A - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 2 - - - - - - - E - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 2 - - - - - - - A - 2 - - 2 - 1 - eighth - up - begin - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - begin - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - continue - begin - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - end - end - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - end - end - - - 5 - 0 - - - - - - - - TAB - 5 - - - - - C - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 1 - - - - - - - A - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 2 - - - - - - - F - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 3 - - - - - - - C - 3 - - 2 - 1 - eighth - up - begin - - - 5 - 3 - - - - - - - F - 2 - - 2 - 1 - eighth - up - begin - - - 6 - 1 - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 2 - - - - - - - F - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 3 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - begin - - - 5 - 3 - - - - - - - F - 2 - - 1 - 1 - 16th - up - continue - begin - - - 6 - 1 - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - F - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 3 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 3 - - - - - - - F - 2 - - 1 - 1 - 16th - up - continue - continue - - - 6 - 1 - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - F - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 3 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 3 - - - - - - - F - 2 - - 1 - 1 - 16th - up - continue - continue - - - 6 - 1 - - - - - - C - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 2 - - - - - - - F - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 3 - - - - - - - C - 3 - - 1 - 1 - 16th - up - end - end - - - 5 - 3 - - - - - - - F - 2 - - 1 - 1 - 16th - up - end - end - - - 6 - 1 - - - - - - E - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 0 - - - - - - - C - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 1 - - - - - - - G - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 0 - - - - - - - E - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 2 - - - - - - - C - 3 - - 2 - 1 - eighth - up - begin - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - begin - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - begin - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - end - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - end - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - end - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - end - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - end - - - 5 - 3 - - - - - - E - 4 - - 2 - 1 - eighth - up - end - - - 1 - 0 - - - - - - - C - 4 - - 2 - 1 - eighth - up - end - - - 2 - 1 - - - - - - - G - 3 - - 2 - 1 - eighth - up - end - - - 3 - 0 - - - - - - - E - 3 - - 2 - 1 - eighth - up - end - - - 4 - 2 - - - - - - - C - 3 - - 2 - 1 - eighth - up - end - - - 5 - 3 - - - - - - - - TAB - 5 - - - - - D - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 0 - - - - - - A - 3 - - 2 - 1 - eighth - up - continue - - - 3 - 2 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - begin - - - H - 1 - 1 - - - - - - - E - 4 - - 1 - 1 - 16th - up - end - end - - - - 1 - 0 - - - - - - - F - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 1 - - - - - - D - 4 - - 2 - 1 - eighth - up - continue - - - 2 - 3 - - - - - - A - 3 - - 2 - 1 - eighth - up - end - - - 3 - 2 - - - - - - - - TAB - 5 - - - - - D - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 0 - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - begin - - - H - 2 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - end - - - - 2 - 3 - - - - - - - F - 4 - - 2 - 1 - eighth - up - end - - - 1 - 1 - - - - - - C - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 1 - - - - - - - G - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 0 - - - - - - - E - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 2 - - - - - - - C - 3 - - 2 - 1 - eighth - up - begin - - - 5 - 3 - - - - - - E - 4 - - 2 - 1 - eighth - up - continue - - - 1 - 0 - - - - - - C - 4 - - 2 - 1 - eighth - up - end - - - 2 - 1 - - - - - - - - TAB - 5 - - - - - D - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 0 - - - - - - A - 3 - - 2 - 1 - eighth - up - continue - - - 3 - 2 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - begin - - - H - 1 - 1 - - - - - - - E - 4 - - 1 - 1 - 16th - up - end - end - - - - 1 - 0 - - - - - - - F - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 1 - - - - - - D - 4 - - 2 - 1 - eighth - up - continue - - - 2 - 3 - - - - - - A - 3 - - 2 - 1 - eighth - up - end - - - 3 - 2 - - - - - - - - TAB - 5 - - - - - E - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 5 - - - - - - - C - 4 - - 2 - 1 - eighth - up - begin - - - 3 - 5 - - - - - - - G - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 5 - - - - - - - C - 3 - - 2 - 1 - eighth - up - begin - - - 5 - 3 - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 5 - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - end - - - 3 - 5 - - - - - - E - 4 - - 2 - 1 - eighth - up - end - - - 2 - 5 - - - - - - D - 4 - - 4 - 1 - quarter - up - - - 2 - 3 - - - - - - - A - 3 - - 4 - 1 - quarter - up - - - 3 - 2 - - - - - - - D - 3 - - 4 - 1 - quarter - up - - - 4 - 0 - - - - - - A - 3 - - 2 - 1 - eighth - up - - - 3 - 2 - - - - - - - - TAB - 5 - - - - - D - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 0 - - - - - - A - 3 - - 2 - 1 - eighth - up - continue - - - 3 - 2 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - begin - - - H - 1 - 1 - - - - - - - E - 4 - - 1 - 1 - 16th - up - end - end - - - - 1 - 0 - - - - - - - F - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 1 - - - - - - D - 4 - - 2 - 1 - eighth - up - continue - - - 2 - 3 - - - - - - A - 3 - - 2 - 1 - eighth - up - end - - - 3 - 2 - - - - - - - - TAB - 5 - - - - - D - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 0 - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - begin - - - H - 2 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - end - - - - 2 - 3 - - - - - - - F - 4 - - 2 - 1 - eighth - up - end - - - 1 - 1 - - - - - - C - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 1 - - - - - - - G - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 0 - - - - - - - E - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 2 - - - - - - - C - 3 - - 2 - 1 - eighth - up - begin - - - 5 - 3 - - - - - - E - 4 - - 2 - 1 - eighth - up - continue - - - 1 - 0 - - - - - - C - 4 - - 2 - 1 - eighth - up - end - - - 2 - 1 - - - - - - - - TAB - 5 - - - - - D - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 0 - - - - - - A - 3 - - 2 - 1 - eighth - up - continue - - - 3 - 2 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - begin - - - H - 1 - 1 - - - - - - - E - 4 - - 1 - 1 - 16th - up - end - end - - - - 1 - 0 - - - - - - - F - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 1 - - - - - - D - 4 - - 2 - 1 - eighth - up - continue - - - 2 - 3 - - - - - - A - 3 - - 2 - 1 - eighth - up - end - - - 3 - 2 - - - - - - - - TAB - 5 - - - - - C - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 1 - - - - - - - G - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 0 - - - - - - - E - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 2 - - - - - - - C - 3 - - 2 - 1 - eighth - up - begin - - - 5 - 3 - - - - - - C - 4 - - 2 - 1 - eighth - up - continue - - - 2 - 1 - - - - - - - G - 3 - - 2 - 1 - eighth - up - continue - - - 3 - 0 - - - - - - - D - 3 - - 2 - 1 - eighth - up - continue - - - 4 - 0 - - - - - - C - 4 - - 2 - 1 - eighth - up - end - - - 2 - 1 - - - - - - - G - 3 - - 2 - 1 - eighth - up - end - - - 3 - 0 - - - - - - - E - 3 - - 2 - 1 - eighth - up - end - - - 4 - 2 - - - - - - F - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 1 - - - - - - - D - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 3 - - - - - - - A - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 2 - - - - - - - D - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - begin - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - end - end - - - 1 - 0 - - - - - - - D - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 0 - - - - - - - - TAB - 5 - - - - - F - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 1 - - - - - - - D - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 3 - - - - - - - A - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 2 - - - - - - - D - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - begin - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - end - end - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 0 - - - - - - F - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 1 - - - - - - - D - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 3 - - - - - - - A - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 2 - - - - - - - D - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - begin - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - end - end - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 0 - - - - - - - - TAB - 5 - - - - - E - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 0 - - - - - - - C - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 1 - - - - - - - G - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 0 - - - - - - - E - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 2 - - - - - - - C - 3 - - 2 - 1 - eighth - up - begin - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - begin - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - begin - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - end - end - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - end - end - - - 5 - 3 - - - - - - E - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 0 - - - - - - - C - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 1 - - - - - - - G - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 0 - - - - - - - E - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 2 - - - - - - - C - 3 - - 2 - 1 - eighth - up - begin - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - begin - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - begin - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - end - end - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - end - end - - - 5 - 3 - - - - - - - - TAB - 5 - - - - - E - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 0 - - - - - - - C - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 1 - - - - - - - A - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 2 - - - - - - - E - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 2 - - - - - - - A - 2 - - 2 - 1 - eighth - up - begin - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - begin - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - continue - begin - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - end - end - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - end - end - - - 5 - 0 - - - - - - E - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 0 - - - - - - - C - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 1 - - - - - - - A - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 2 - - - - - - - E - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 2 - - - - - - - A - 2 - - 2 - 1 - eighth - up - begin - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - begin - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - continue - begin - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - end - end - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - end - end - - - 5 - 0 - - - - - - - - TAB - 5 - - - - - C - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 1 - - - - - - - A - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 2 - - - - - - - F - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 3 - - - - - - - C - 3 - - 2 - 1 - eighth - up - begin - - - 5 - 3 - - - - - - - F - 2 - - 2 - 1 - eighth - up - begin - - - 6 - 1 - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 2 - - - - - - - F - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 3 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - begin - - - 5 - 3 - - - - - - - F - 2 - - 1 - 1 - 16th - up - continue - begin - - - 6 - 1 - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - F - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 3 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 3 - - - - - - - F - 2 - - 1 - 1 - 16th - up - continue - continue - - - 6 - 1 - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - F - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 3 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 3 - - - - - - - F - 2 - - 1 - 1 - 16th - up - continue - continue - - - 6 - 1 - - - - - - C - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 2 - - - - - - - F - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 3 - - - - - - - C - 3 - - 1 - 1 - 16th - up - end - end - - - 5 - 3 - - - - - - - F - 2 - - 1 - 1 - 16th - up - end - end - - - 6 - 1 - - - - - - D - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 3 - - - - - - - B - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 4 - - - - - - - G - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 5 - - - - - - - D - 3 - - 2 - 1 - eighth - up - begin - - - 5 - 5 - - - - - - - G - 2 - - 2 - 1 - eighth - up - begin - - - 6 - 3 - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 3 - - - - - - - B - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 4 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 5 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - begin - - - 5 - 5 - - - - - - - G - 2 - - 1 - 1 - 16th - up - continue - begin - - - 6 - 3 - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 3 - - - - - - - B - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 4 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 5 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 5 - - - - - - - G - 2 - - 1 - 1 - 16th - up - continue - continue - - - 6 - 3 - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 3 - - - - - - - B - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 4 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 5 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 5 - - - - - - - G - 2 - - 1 - 1 - 16th - up - continue - continue - - - 6 - 3 - - - - - - D - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 3 - - - - - - - B - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 4 - - - - - - - G - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 5 - - - - - - - D - 3 - - 1 - 1 - 16th - up - end - end - - - 5 - 5 - - - - - - - G - 2 - - 1 - 1 - 16th - up - end - end - - - 6 - 3 - - - - - - - - TAB - 5 - - - - - F - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 1 - - - - - - - D - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 3 - - - - - - - A - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 2 - - - - - - - D - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - begin - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - end - end - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 0 - - - - - - F - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 1 - - - - - - - D - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 3 - - - - - - - A - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 2 - - - - - - - D - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - begin - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - end - end - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 0 - - - - - - - - TAB - 5 - - - - - E - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 0 - - - - - - - C - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 1 - - - - - - - G - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 0 - - - - - - - E - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 2 - - - - - - - C - 3 - - 2 - 1 - eighth - up - begin - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - begin - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - begin - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - end - end - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - end - end - - - 5 - 3 - - - - - - E - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 0 - - - - - - - C - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 1 - - - - - - - G - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 0 - - - - - - - E - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 2 - - - - - - - C - 3 - - 2 - 1 - eighth - up - begin - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - begin - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - begin - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - end - end - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - end - end - - - 5 - 3 - - - - - - - - TAB - 5 - - - - - E - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 0 - - - - - - - C - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 1 - - - - - - - A - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 2 - - - - - - - E - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 2 - - - - - - - A - 2 - - 2 - 1 - eighth - up - begin - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - begin - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - continue - begin - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - end - end - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - end - end - - - 5 - 0 - - - - - - E - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 0 - - - - - - - C - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 1 - - - - - - - A - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 2 - - - - - - - E - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 2 - - - - - - - A - 2 - - 2 - 1 - eighth - up - begin - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - begin - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - continue - begin - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - up - end - end - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - up - end - end - - - 5 - 0 - - - - - - - - TAB - 5 - - - - - C - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 1 - - - - - - - A - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 2 - - - - - - - F - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 3 - - - - - - - C - 3 - - 2 - 1 - eighth - up - begin - - - 5 - 3 - - - - - - - F - 2 - - 2 - 1 - eighth - up - begin - - - 6 - 1 - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 2 - - - - - - - F - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 3 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - begin - - - 5 - 3 - - - - - - - F - 2 - - 1 - 1 - 16th - up - continue - begin - - - 6 - 1 - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - F - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 3 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 3 - - - - - - - F - 2 - - 1 - 1 - 16th - up - continue - continue - - - 6 - 1 - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - F - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 3 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - continue - - - 5 - 3 - - - - - - - F - 2 - - 1 - 1 - 16th - up - continue - continue - - - 6 - 1 - - - - - - C - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 2 - - - - - - - F - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 3 - - - - - - - C - 3 - - 1 - 1 - 16th - up - end - end - - - 5 - 3 - - - - - - - F - 2 - - 1 - 1 - 16th - up - end - end - - - 6 - 1 - - - - - - E - 4 - - 2 - 1 - eighth - up - begin - - - 1 - 0 - - - - - - - C - 4 - - 2 - 1 - eighth - up - begin - - - 2 - 1 - - - - - - - G - 3 - - 2 - 1 - eighth - up - begin - - - 3 - 0 - - - - - - - E - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 2 - - - - - - - C - 3 - - 2 - 1 - eighth - up - begin - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - begin - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - begin - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - end - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - end - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - end - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - continue - end - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - continue - end - - - 5 - 3 - - - - - - E - 4 - - 2 - 1 - eighth - up - end - - - 1 - 0 - - - - - - - C - 4 - - 2 - 1 - eighth - up - end - - - 2 - 1 - - - - - - - G - 3 - - 2 - 1 - eighth - up - end - - - 3 - 0 - - - - - - - E - 3 - - 2 - 1 - eighth - up - end - - - 4 - 2 - - - - - - - C - 3 - - 2 - 1 - eighth - up - end - - - 5 - 3 - - - - - - - - TAB - 5 - - - - - D - 3 - - 2 - 1 - eighth - down - begin - - - 4 - 0 - - - - - - A - 3 - - 2 - 1 - eighth - down - continue - - - 3 - 2 - - - - - - F - 4 - - 1 - 1 - 16th - down - continue - begin - - - H - 1 - 1 - - - - - - - E - 4 - - 1 - 1 - 16th - down - end - end - - - - 1 - 0 - - - - - - - F - 4 - - 2 - 1 - eighth - down - begin - - - 1 - 1 - - - - - - D - 4 - - 2 - 1 - eighth - down - continue - - - 2 - 3 - - - - - - A - 3 - - 2 - 1 - eighth - down - end - - - 3 - 2 - - - - - - - - TAB - 5 - - - - - D - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 0 - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 2 - - - - - - D - 4 - - 1 - 1 - 16th - down - continue - end - - - 2 - 3 - - - - - - F - 4 - - 2 - 1 - eighth - up - end - - - 1 - 1 - - - - - - C - 4 - - 2 - 1 - eighth - up - begin - - - 3 - 5 - - - - - - - G - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 5 - - - - - - - C - 3 - - 2 - 1 - eighth - up - begin - - - 5 - 3 - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 5 - - - - - - E - 4 - - 1 - 1 - 16th - down - continue - end - - - 2 - 5 - - - - - - C - 4 - - 2 - 1 - eighth - up - end - - - 3 - 5 - - - - - - - - TAB - 5 - - - - - D - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 0 - - - - - - A - 3 - - 2 - 1 - eighth - down - continue - - - 3 - 2 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - begin - - - H - 1 - 1 - - - - - - - E - 4 - - 1 - 1 - 16th - up - end - end - - - - 1 - 0 - - - - - - - F - 4 - - 3 - 1 - eighth - - down - begin - - - 1 - 1 - - - - - - D - 4 - - 1 - 1 - 16th - down - continue - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - down - continue - - - 3 - 2 - - - - - - D - 4 - - 2 - 1 - eighth - down - end - - - 2 - 3 - - - - - - - A - 3 - - 2 - 1 - eighth - down - end - - - 3 - 2 - - - - - - - 2 - - TAB - 5 - - - - - C - 4 - - 1 - 1 - eighth - up - begin - - - 3 - 5 - - - - - - - G - 3 - - 1 - 1 - eighth - up - begin - - - 4 - 5 - - - - - - - C - 3 - - 1 - 1 - eighth - up - begin - - - 5 - 3 - - - - - - C - 4 - - 1 - 1 - eighth - up - continue - - - 3 - 5 - - - - - - E - 4 - - 1 - 1 - eighth - up - end - - - 2 - 5 - - - - - - D - 4 - - 2 - 1 - quarter - up - - - 2 - 3 - - - - - - - A - 3 - - 2 - 1 - quarter - up - - - 3 - 2 - - - - - - - D - 3 - - 2 - 1 - quarter - up - - - 4 - 0 - - - - - - A - 3 - - 1 - 1 - eighth - up - - - 3 - 2 - - - - - - - 4 - - TAB - 5 - - - - - D - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 0 - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 2 - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 3 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - continue - - - H - 1 - 1 - - - - - - - E - 4 - - 1 - 1 - 16th - up - end - end - - - - 1 - 0 - - - - - - - F - 4 - - 3 - 1 - eighth - - up - begin - - - 1 - 1 - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 3 - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - D - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 3 - - - - - - - - TAB - 5 - - - - - D - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 0 - - - - - - A - 3 - - 2 - 1 - eighth - up - continue - - - 3 - 2 - - - - - - F - 4 - - 2 - 1 - eighth - up - end - - - 1 - 1 - - - - - - C - 4 - - 2 - 1 - eighth - up - begin - - - 3 - 5 - - - - - - - G - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 5 - - - - - - - C - 3 - - 2 - 1 - eighth - up - begin - - - 5 - 3 - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 5 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - begin - - - 4 - 5 - - - - - - E - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 5 - - - - - - C - 4 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 5 - - - - - - - G - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 5 - - - - - - E - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 5 - - - - - - - - TAB - 5 - - - - - D - 3 - - 2 - 1 - eighth - up - begin - - - 4 - 0 - - - - - - A - 3 - - 2 - 1 - eighth - up - continue - - - 3 - 2 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - begin - - - H - 1 - 1 - - - - - - - E - 4 - - 1 - 1 - 16th - up - end - end - - - - 1 - 0 - - - - - - - F - 4 - - 3 - 1 - eighth - - up - begin - - - 1 - 1 - - - - - - G - 4 - - 1 - 1 - 16th - up - continue - - - 1 - 3 - - - - - - D - 4 - - 2 - 1 - eighth - up - end - - - 2 - 3 - - - - - - - - TAB - 5 - - - - - G - 3 - - 2 - 1 - eighth - down - begin - - - 3 - 0 - - - - - - - E - 3 - - 2 - 1 - eighth - down - begin - - - 4 - 2 - - - - - - - C - 3 - - 2 - 1 - eighth - down - begin - - - 5 - 3 - - - - - - C - 4 - - 2 - 1 - eighth - up - continue - - - 2 - 1 - - - - - - E - 4 - - 2 - 1 - eighth - up - end - - - 1 - 0 - - - - - - D - 4 - - 2 - 1 - eighth - down - begin - - - 2 - 3 - - - - - - - A - 3 - - 2 - 1 - eighth - down - begin - - - 3 - 2 - - - - - - - D - 3 - - 2 - 1 - eighth - down - begin - - - 4 - 0 - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - begin - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - begin - - - 3 - 2 - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - D - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 2 - - - - - - - - TAB - 5 - - - - - F - 4 - - 2 - 1 - eighth - down - begin - - - 1 - 1 - - - - - - - D - 4 - - 2 - 1 - eighth - down - begin - - - 2 - 3 - - - - - - - A - 3 - - 2 - 1 - eighth - down - begin - - - 3 - 2 - - - - - - - D - 3 - - 2 - 1 - eighth - down - begin - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - down - continue - begin - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - down - continue - begin - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - down - continue - begin - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - down - continue - begin - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - down - continue - continue - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - down - continue - continue - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - down - continue - continue - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - down - continue - continue - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - continue - continue - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - continue - continue - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - continue - continue - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - continue - continue - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - up - end - end - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 0 - - - - - - F - 4 - - 2 - 1 - eighth - down - begin - - - 1 - 1 - - - - - - - D - 4 - - 2 - 1 - eighth - down - begin - - - 2 - 3 - - - - - - - A - 3 - - 2 - 1 - eighth - down - begin - - - 3 - 2 - - - - - - - D - 3 - - 2 - 1 - eighth - down - begin - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - down - continue - begin - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - down - continue - begin - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - down - continue - begin - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - down - continue - begin - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - down - continue - continue - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - down - continue - continue - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - down - continue - continue - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - down - continue - continue - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - down - continue - continue - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - down - continue - continue - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - down - continue - continue - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - down - continue - continue - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - down - end - end - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - down - end - end - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - down - end - end - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - down - end - end - - - 4 - 0 - - - - - - - - TAB - 5 - - - - - E - 4 - - 2 - 1 - eighth - down - begin - - - 1 - 0 - - - - - - - C - 4 - - 2 - 1 - eighth - down - begin - - - 2 - 1 - - - - - - - G - 3 - - 2 - 1 - eighth - down - begin - - - 3 - 0 - - - - - - - E - 3 - - 2 - 1 - eighth - down - begin - - - 4 - 2 - - - - - - - C - 3 - - 2 - 1 - eighth - down - begin - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - down - continue - begin - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - down - continue - begin - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - down - continue - begin - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - down - continue - begin - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - down - continue - begin - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - down - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - down - continue - continue - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - down - continue - continue - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - down - continue - continue - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - down - continue - continue - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - down - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - down - continue - continue - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - down - continue - continue - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - down - continue - continue - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - down - continue - continue - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - up - end - end - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - up - end - end - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - up - end - end - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - up - end - end - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - up - end - end - - - 5 - 3 - - - - - - E - 4 - - 2 - 1 - eighth - down - begin - - - 1 - 0 - - - - - - - C - 4 - - 2 - 1 - eighth - down - begin - - - 2 - 1 - - - - - - - G - 3 - - 2 - 1 - eighth - down - begin - - - 3 - 0 - - - - - - - E - 3 - - 2 - 1 - eighth - down - begin - - - 4 - 2 - - - - - - - C - 3 - - 2 - 1 - eighth - down - begin - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - down - continue - begin - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - down - continue - begin - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - down - continue - begin - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - down - continue - begin - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - down - continue - begin - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - down - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - down - continue - continue - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - down - continue - continue - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - down - continue - continue - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - down - continue - continue - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - down - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - down - continue - continue - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - down - continue - continue - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - down - continue - continue - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - down - continue - continue - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - down - end - end - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - down - end - end - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - down - end - end - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - down - end - end - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - down - end - end - - - 5 - 3 - - - - - - - - TAB - 5 - - - - - E - 4 - - 2 - 1 - eighth - down - begin - - - 1 - 0 - - - - - - - C - 4 - - 2 - 1 - eighth - down - begin - - - 2 - 1 - - - - - - - A - 3 - - 2 - 1 - eighth - down - begin - - - 3 - 2 - - - - - - - E - 3 - - 2 - 1 - eighth - down - begin - - - 4 - 2 - - - - - - - A - 2 - - 2 - 1 - eighth - down - begin - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - down - continue - begin - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - down - continue - begin - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - down - continue - begin - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - down - continue - begin - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - down - continue - begin - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - down - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - down - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - down - continue - continue - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - down - continue - continue - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - down - continue - continue - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - down - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - down - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - down - continue - continue - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - down - continue - continue - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - down - continue - continue - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - down - end - end - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - down - end - end - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - down - end - end - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - down - end - end - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - down - end - end - - - 5 - 0 - - - - - - E - 4 - - 2 - 1 - eighth - down - begin - - - 1 - 0 - - - - - - - C - 4 - - 2 - 1 - eighth - down - begin - - - 2 - 1 - - - - - - - A - 3 - - 2 - 1 - eighth - down - begin - - - 3 - 2 - - - - - - - E - 3 - - 2 - 1 - eighth - down - begin - - - 4 - 2 - - - - - - - A - 2 - - 2 - 1 - eighth - down - begin - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - down - continue - begin - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - down - continue - begin - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - down - continue - begin - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - down - continue - begin - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - down - continue - begin - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - down - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - down - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - down - continue - continue - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - down - continue - continue - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - down - continue - continue - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - down - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - down - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - down - continue - continue - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - down - continue - continue - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - down - continue - continue - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - down - end - end - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - down - end - end - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - down - end - end - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - down - end - end - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - down - end - end - - - 5 - 0 - - - - - - - - TAB - 5 - - - - - C - 4 - - 2 - 1 - eighth - down - begin - - - 2 - 1 - - - - - - - A - 3 - - 2 - 1 - eighth - down - begin - - - 3 - 2 - - - - - - - F - 3 - - 2 - 1 - eighth - down - begin - - - 4 - 3 - - - - - - - C - 3 - - 2 - 1 - eighth - down - begin - - - 5 - 3 - - - - - - - F - 2 - - 2 - 1 - eighth - down - begin - - - 6 - 1 - - - - - - C - 4 - - 1 - 1 - 16th - down - continue - begin - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - down - continue - begin - - - 3 - 2 - - - - - - - F - 3 - - 1 - 1 - 16th - down - continue - begin - - - 4 - 3 - - - - - - - C - 3 - - 1 - 1 - 16th - down - continue - begin - - - 5 - 3 - - - - - - - F - 2 - - 1 - 1 - 16th - down - continue - begin - - - 6 - 1 - - - - - - C - 4 - - 1 - 1 - 16th - down - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - down - continue - continue - - - 3 - 2 - - - - - - - F - 3 - - 1 - 1 - 16th - down - continue - continue - - - 4 - 3 - - - - - - - C - 3 - - 1 - 1 - 16th - down - continue - continue - - - 5 - 3 - - - - - - - F - 2 - - 1 - 1 - 16th - down - continue - continue - - - 6 - 1 - - - - - - C - 4 - - 1 - 1 - 16th - down - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - down - continue - continue - - - 3 - 2 - - - - - - - F - 3 - - 1 - 1 - 16th - down - continue - continue - - - 4 - 3 - - - - - - - C - 3 - - 1 - 1 - 16th - down - continue - continue - - - 5 - 3 - - - - - - - F - 2 - - 1 - 1 - 16th - down - continue - continue - - - 6 - 1 - - - - - - C - 4 - - 1 - 1 - 16th - down - end - end - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - down - end - end - - - 3 - 2 - - - - - - - F - 3 - - 1 - 1 - 16th - down - end - end - - - 4 - 3 - - - - - - - C - 3 - - 1 - 1 - 16th - down - end - end - - - 5 - 3 - - - - - - - F - 2 - - 1 - 1 - 16th - down - end - end - - - 6 - 1 - - - - - - D - 4 - - 2 - 1 - eighth - down - begin - - - 2 - 3 - - - - - - - B - 3 - - 2 - 1 - eighth - down - begin - - - 3 - 4 - - - - - - - G - 3 - - 2 - 1 - eighth - down - begin - - - 4 - 5 - - - - - - - D - 3 - - 2 - 1 - eighth - down - begin - - - 5 - 5 - - - - - - - G - 2 - - 2 - 1 - eighth - down - begin - - - 6 - 3 - - - - - - D - 4 - - 1 - 1 - 16th - down - continue - begin - - - 2 - 3 - - - - - - - B - 3 - - 1 - 1 - 16th - down - continue - begin - - - 3 - 4 - - - - - - - G - 3 - - 1 - 1 - 16th - down - continue - begin - - - 4 - 5 - - - - - - - D - 3 - - 1 - 1 - 16th - down - continue - begin - - - 5 - 5 - - - - - - - G - 2 - - 1 - 1 - 16th - down - continue - begin - - - 6 - 3 - - - - - - D - 4 - - 1 - 1 - 16th - down - continue - continue - - - 2 - 3 - - - - - - - B - 3 - - 1 - 1 - 16th - down - continue - continue - - - 3 - 4 - - - - - - - G - 3 - - 1 - 1 - 16th - down - continue - continue - - - 4 - 5 - - - - - - - D - 3 - - 1 - 1 - 16th - down - continue - continue - - - 5 - 5 - - - - - - - G - 2 - - 1 - 1 - 16th - down - continue - continue - - - 6 - 3 - - - - - - D - 4 - - 1 - 1 - 16th - down - continue - continue - - - 2 - 3 - - - - - - - B - 3 - - 1 - 1 - 16th - down - continue - continue - - - 3 - 4 - - - - - - - G - 3 - - 1 - 1 - 16th - down - continue - continue - - - 4 - 5 - - - - - - - D - 3 - - 1 - 1 - 16th - down - continue - continue - - - 5 - 5 - - - - - - - G - 2 - - 1 - 1 - 16th - down - continue - continue - - - 6 - 3 - - - - - - D - 4 - - 1 - 1 - 16th - down - end - end - - - 2 - 3 - - - - - - - B - 3 - - 1 - 1 - 16th - down - end - end - - - 3 - 4 - - - - - - - G - 3 - - 1 - 1 - 16th - down - end - end - - - 4 - 5 - - - - - - - D - 3 - - 1 - 1 - 16th - down - end - end - - - 5 - 5 - - - - - - - G - 2 - - 1 - 1 - 16th - down - end - end - - - 6 - 3 - - - - - - - - TAB - 5 - - - - - F - 4 - - 2 - 1 - eighth - down - begin - - - 1 - 1 - - - - - - - D - 4 - - 2 - 1 - eighth - down - begin - - - 2 - 3 - - - - - - - A - 3 - - 2 - 1 - eighth - down - begin - - - 3 - 2 - - - - - - - D - 3 - - 2 - 1 - eighth - down - begin - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - down - continue - begin - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - down - continue - begin - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - down - continue - begin - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - down - continue - begin - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - down - continue - continue - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - down - continue - continue - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - down - continue - continue - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - down - continue - continue - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - down - continue - continue - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - down - continue - continue - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - down - continue - continue - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - down - continue - continue - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - down - end - end - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - down - end - end - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - down - end - end - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - down - end - end - - - 4 - 0 - - - - - - F - 4 - - 2 - 1 - eighth - down - begin - - - 1 - 1 - - - - - - - D - 4 - - 2 - 1 - eighth - down - begin - - - 2 - 3 - - - - - - - A - 3 - - 2 - 1 - eighth - down - begin - - - 3 - 2 - - - - - - - D - 3 - - 2 - 1 - eighth - down - begin - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - down - continue - begin - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - down - continue - begin - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - down - continue - begin - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - down - continue - begin - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - down - continue - continue - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - down - continue - continue - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - down - continue - continue - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - down - continue - continue - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - down - continue - continue - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - down - continue - continue - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - down - continue - continue - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - down - continue - continue - - - 4 - 0 - - - - - - F - 4 - - 1 - 1 - 16th - down - end - end - - - 1 - 1 - - - - - - - D - 4 - - 1 - 1 - 16th - down - end - end - - - 2 - 3 - - - - - - - A - 3 - - 1 - 1 - 16th - down - end - end - - - 3 - 2 - - - - - - - D - 3 - - 1 - 1 - 16th - down - end - end - - - 4 - 0 - - - - - - - - TAB - 5 - - - - - E - 4 - - 2 - 1 - eighth - down - begin - - - 1 - 0 - - - - - - - C - 4 - - 2 - 1 - eighth - down - begin - - - 2 - 1 - - - - - - - G - 3 - - 2 - 1 - eighth - down - begin - - - 3 - 0 - - - - - - - E - 3 - - 2 - 1 - eighth - down - begin - - - 4 - 2 - - - - - - - C - 3 - - 2 - 1 - eighth - down - begin - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - down - continue - begin - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - down - continue - begin - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - down - continue - begin - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - down - continue - begin - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - down - continue - begin - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - down - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - down - continue - continue - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - down - continue - continue - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - down - continue - continue - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - down - continue - continue - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - down - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - down - continue - continue - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - down - continue - continue - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - down - continue - continue - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - down - continue - continue - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - down - end - end - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - down - end - end - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - down - end - end - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - down - end - end - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - down - end - end - - - 5 - 3 - - - - - - E - 4 - - 2 - 1 - eighth - down - begin - - - 1 - 0 - - - - - - - C - 4 - - 2 - 1 - eighth - down - begin - - - 2 - 1 - - - - - - - G - 3 - - 2 - 1 - eighth - down - begin - - - 3 - 0 - - - - - - - E - 3 - - 2 - 1 - eighth - down - begin - - - 4 - 2 - - - - - - - C - 3 - - 2 - 1 - eighth - down - begin - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - down - continue - begin - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - down - continue - begin - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - down - continue - begin - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - down - continue - begin - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - down - continue - begin - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - down - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - down - continue - continue - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - down - continue - continue - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - down - continue - continue - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - down - continue - continue - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - down - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - down - continue - continue - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - down - continue - continue - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - down - continue - continue - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - down - continue - continue - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - down - end - end - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - down - end - end - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - down - end - end - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - down - end - end - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - down - end - end - - - 5 - 3 - - - - - - - - TAB - 5 - - - - - E - 4 - - 2 - 1 - eighth - down - begin - - - 1 - 0 - - - - - - - C - 4 - - 2 - 1 - eighth - down - begin - - - 2 - 1 - - - - - - - A - 3 - - 2 - 1 - eighth - down - begin - - - 3 - 2 - - - - - - - E - 3 - - 2 - 1 - eighth - down - begin - - - 4 - 2 - - - - - - - A - 2 - - 2 - 1 - eighth - down - begin - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - down - continue - begin - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - down - continue - begin - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - down - continue - begin - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - down - continue - begin - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - down - continue - begin - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - down - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - down - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - down - continue - continue - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - down - continue - continue - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - down - continue - continue - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - down - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - down - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - down - continue - continue - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - down - continue - continue - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - down - continue - continue - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - down - end - end - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - down - end - end - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - down - end - end - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - down - end - end - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - down - end - end - - - 5 - 0 - - - - - - E - 4 - - 2 - 1 - eighth - down - begin - - - 1 - 0 - - - - - - - C - 4 - - 2 - 1 - eighth - down - begin - - - 2 - 1 - - - - - - - A - 3 - - 2 - 1 - eighth - down - begin - - - 3 - 2 - - - - - - - E - 3 - - 2 - 1 - eighth - down - begin - - - 4 - 2 - - - - - - - A - 2 - - 2 - 1 - eighth - down - begin - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - down - continue - begin - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - down - continue - begin - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - down - continue - begin - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - down - continue - begin - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - down - continue - begin - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - down - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - down - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - down - continue - continue - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - down - continue - continue - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - down - continue - continue - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - down - continue - continue - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - down - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - down - continue - continue - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - down - continue - continue - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - down - continue - continue - - - 5 - 0 - - - - - - E - 4 - - 1 - 1 - 16th - down - end - end - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - down - end - end - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - down - end - end - - - 3 - 2 - - - - - - - E - 3 - - 1 - 1 - 16th - down - end - end - - - 4 - 2 - - - - - - - A - 2 - - 1 - 1 - 16th - down - end - end - - - 5 - 0 - - - - - - - - TAB - 5 - - - - - C - 4 - - 2 - 1 - eighth - down - begin - - - 2 - 1 - - - - - - - A - 3 - - 2 - 1 - eighth - down - begin - - - 3 - 2 - - - - - - - F - 3 - - 2 - 1 - eighth - down - begin - - - 4 - 3 - - - - - - - C - 3 - - 2 - 1 - eighth - down - begin - - - 5 - 3 - - - - - - - F - 2 - - 2 - 1 - eighth - down - begin - - - 6 - 1 - - - - - - C - 4 - - 1 - 1 - 16th - down - continue - begin - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - down - continue - begin - - - 3 - 2 - - - - - - - F - 3 - - 1 - 1 - 16th - down - continue - begin - - - 4 - 3 - - - - - - - C - 3 - - 1 - 1 - 16th - down - continue - begin - - - 5 - 3 - - - - - - - F - 2 - - 1 - 1 - 16th - down - continue - begin - - - 6 - 1 - - - - - - C - 4 - - 1 - 1 - 16th - down - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - down - continue - continue - - - 3 - 2 - - - - - - - F - 3 - - 1 - 1 - 16th - down - continue - continue - - - 4 - 3 - - - - - - - C - 3 - - 1 - 1 - 16th - down - continue - continue - - - 5 - 3 - - - - - - - F - 2 - - 1 - 1 - 16th - down - continue - continue - - - 6 - 1 - - - - - - C - 4 - - 1 - 1 - 16th - down - continue - continue - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - down - continue - continue - - - 3 - 2 - - - - - - - F - 3 - - 1 - 1 - 16th - down - continue - continue - - - 4 - 3 - - - - - - - C - 3 - - 1 - 1 - 16th - down - continue - continue - - - 5 - 3 - - - - - - - F - 2 - - 1 - 1 - 16th - down - continue - continue - - - 6 - 1 - - - - - - C - 4 - - 1 - 1 - 16th - down - end - end - - - 2 - 1 - - - - - - - A - 3 - - 1 - 1 - 16th - down - end - end - - - 3 - 2 - - - - - - - F - 3 - - 1 - 1 - 16th - down - end - end - - - 4 - 3 - - - - - - - C - 3 - - 1 - 1 - 16th - down - end - end - - - 5 - 3 - - - - - - - F - 2 - - 1 - 1 - 16th - down - end - end - - - 6 - 1 - - - - - - E - 4 - - 2 - 1 - eighth - down - begin - - - 1 - 0 - - - - - - - C - 4 - - 2 - 1 - eighth - down - begin - - - 2 - 1 - - - - - - - G - 3 - - 2 - 1 - eighth - down - begin - - - 3 - 0 - - - - - - - E - 3 - - 2 - 1 - eighth - down - begin - - - 4 - 2 - - - - - - - C - 3 - - 2 - 1 - eighth - down - begin - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - down - continue - begin - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - down - continue - begin - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - down - continue - begin - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - down - continue - begin - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - down - continue - begin - - - 5 - 3 - - - - - - E - 4 - - 1 - 1 - 16th - down - continue - end - - - 1 - 0 - - - - - - - C - 4 - - 1 - 1 - 16th - down - continue - end - - - 2 - 1 - - - - - - - G - 3 - - 1 - 1 - 16th - down - continue - end - - - 3 - 0 - - - - - - - E - 3 - - 1 - 1 - 16th - down - continue - end - - - 4 - 2 - - - - - - - C - 3 - - 1 - 1 - 16th - down - continue - end - - - 5 - 3 - - - - - - E - 4 - - 2 - 1 - eighth - down - end - - - 1 - 0 - - - - - - - C - 4 - - 2 - 1 - eighth - down - end - - - 2 - 1 - - - - - - - G - 3 - - 2 - 1 - eighth - down - end - - - 3 - 0 - - - - - - - E - 3 - - 2 - 1 - eighth - down - end - - - 4 - 2 - - - - - - - C - 3 - - 2 - 1 - eighth - down - end - - - 5 - 3 - - - - - - - 1 - - TAB - 5 - - - - - D - 4 - - 3 - 1 - half - - down - - - 2 - 3 - - - - - - - A - 3 - - 3 - 1 - half - - down - - - 3 - 2 - - - - - - - D - 3 - - 3 - 1 - half - - down - - - 4 - 0 - - - - - - - - - 2 - - 0 - major - - - - TAB - 5 - - - 4 - - D - 1 - - - G - 1 - - - C - 2 - - - F - 2 - - - - 0 - 0 - 0 - - - - - D - 2 - - 3 - - 1 - quarter - - down - - - 2 - 0 - - - - - - - D - 2 - - 3 - - 1 - quarter - - down - - - 2 - 0 - - - - - - - - - TAB - 5 - - - - - D - 2 - - 3 - - 1 - quarter - - down - - - 2 - 0 - - - - - - - D - 2 - - 2 - - 1 - quarter - up - - - 2 - 0 - - - - - - - D - 2 - - 1 - 1 - eighth - down - - - 2 - 0 - - - - - - - - TAB - 5 - - - - - D - 2 - - 3 - - 1 - quarter - - up - - - 2 - 0 - - - - - - - D - 2 - - 2 - - 1 - quarter - up - - - 2 - 0 - - - - - - - D - 2 - - 1 - 1 - eighth - up - - - 2 - 0 - - - - - - - - TAB - 5 - - - - - C - 2 - - 3 - 1 - quarter - - up - - - 3 - 3 - - - - - - G - 1 - - 3 - 1 - quarter - - up - - - 4 - 3 - - - - - - - - TAB - 5 - - - - - D - 2 - - 3 - - 1 - quarter - - down - - - 2 - 0 - - - - - - - D - 2 - - 3 - - 1 - quarter - - up - - - 2 - 0 - - - - - - - - - TAB - 5 - - - - - D - 2 - - 3 - - 1 - quarter - - up - - - 2 - 0 - - - - - - - D - 2 - - 2 - - 1 - quarter - up - - - 2 - 0 - - - - - - - D - 2 - - 1 - 1 - eighth - up - - - 2 - 0 - - - - - - - - TAB - 5 - - - - - D - 2 - - 3 - - 1 - quarter - - up - - - 2 - 0 - - - - - - - D - 2 - - 2 - - 1 - quarter - up - - - 2 - 0 - - - - - - - D - 2 - - 1 - 1 - eighth - up - - - 2 - 0 - - - - - - - - TAB - 5 - - - - - C - 2 - - 3 - 1 - quarter - - up - - - 3 - 3 - - - - - - G - 1 - - 3 - 1 - quarter - - up - - - 4 - 3 - - - - - - - - TAB - 5 - - - - - D - 2 - - 3 - - 1 - quarter - - up - - - 2 - 0 - - - - - - - D - 2 - - 3 - - 1 - quarter - - up - - - 2 - 0 - - - - - - - - - TAB - 5 - - - - - D - 2 - - 3 - - 1 - quarter - - up - - - 2 - 0 - - - - - - - D - 2 - - 2 - - 1 - quarter - up - - - 2 - 0 - - - - - - - D - 2 - - 1 - 1 - eighth - up - - - 2 - 0 - - - - - - - - TAB - 5 - - - - - D - 2 - - 3 - - 1 - quarter - - up - - - 2 - 0 - - - - - - - D - 2 - - 2 - - 1 - quarter - up - - - 2 - 0 - - - - - - - D - 2 - - 1 - 1 - eighth - up - - - 2 - 0 - - - - - - - - TAB - 5 - - - - - C - 2 - - 3 - 1 - quarter - - up - - - 3 - 3 - - - - - - G - 1 - - 3 - 1 - quarter - - up - - - 4 - 3 - - - - - - - - TAB - 5 - - - - - D - 2 - - 3 - - 1 - quarter - - up - - - 2 - 0 - - - - - - - D - 2 - - 3 - - 1 - quarter - - up - - - 2 - 0 - - - - - - - - - TAB - 5 - - - - - D - 2 - - 3 - - 1 - quarter - - up - - - 2 - 0 - - - - - - - D - 2 - - 2 - - 1 - quarter - up - - - 2 - 0 - - - - - - - D - 2 - - 1 - 1 - eighth - up - - - 2 - 0 - - - - - - - - TAB - 5 - - - - - D - 2 - - 3 - - 1 - quarter - - up - - - 2 - 0 - - - - - - - D - 2 - - 2 - - 1 - quarter - up - - - 2 - 0 - - - - - - - D - 2 - - 1 - 1 - eighth - up - - - 2 - 0 - - - - - - - - TAB - 5 - - - - - C - 2 - - 3 - 1 - quarter - - up - - - 3 - 3 - - - - - - G - 1 - - 3 - - 1 - quarter - - up - - - 4 - 3 - - - - - - - - - - TAB - 5 - - - - - G - 1 - - 3 - - 1 - quarter - - up - - - 4 - 3 - - - - - - - - - - TAB - 5 - - - - - D - 2 - - 1 - 1 - eighth - up - - - 2 - 0 - - - - - - 2 - 1 - quarter - - - - 2 - 1 - quarter - - - - C - 2 - - 1 - 1 - eighth - up - - - 3 - 3 - - - - - - - - TAB - 5 - - - - - D - 2 - - 1 - 1 - eighth - up - - - 2 - 0 - - - - - - 2 - 1 - quarter - - - - 2 - 1 - quarter - - - - 1 - 1 - eighth - - - - - - TAB - 5 - - - - - D - 2 - - 1 - 1 - eighth - up - - - 2 - 0 - - - - - - 2 - 1 - quarter - - - - 2 - 1 - quarter - - - - C - 2 - - 1 - 1 - eighth - up - - - 3 - 3 - - - - - - - - TAB - 5 - - - - - D - 2 - - 3 - 1 - quarter - - up - - - 2 - 0 - - - - - - C - 2 - - 3 - 1 - quarter - - up - - - 3 - 3 - - - - - - - 1 - - TAB - 5 - - - - - D - 2 - - 3 - 1 - half - - up - - - 2 - 0 - - - - - - - - TAB - 5 - - - - - C - 2 - - 3 - 1 - half - - up - - - 3 - 3 - - - - - - - - TAB - 5 - - - - - A - 1 - - 3 - 1 - half - - up - - - 3 - 0 - - - - - - - 2 - - TAB - 5 - - - - - F - 1 - - 3 - 1 - quarter - - up - - - 4 - 1 - - - - - - G - 1 - - 3 - 1 - quarter - - up - - - 4 - 3 - - - - - - - 1 - - TAB - 5 - - - - - D - 2 - - 3 - 1 - half - - up - - - 2 - 0 - - - - - - - - TAB - 5 - - - - - C - 2 - - 3 - 1 - half - - up - - - 3 - 3 - - - - - - - - TAB - 5 - - - - - A - 1 - - 3 - 1 - half - - up - - - 3 - 0 - - - - - - - 2 - - TAB - 5 - - - - - F - 1 - - 3 - 1 - quarter - - up - - - 4 - 1 - - - - - - C - 2 - - 3 - 1 - quarter - - up - - - 3 - 3 - - - - - - - 1 - - TAB - 5 - - - - - D - 2 - - 3 - 1 - half - - up - - - 2 - 0 - - - - - - - 2 - - TAB - 5 - - - - - D - 2 - - 3 - 1 - quarter - - up - - - 2 - 0 - - - - - - C - 2 - - 3 - 1 - quarter - - up - - - 3 - 3 - - - - - - - 1 - - TAB - 5 - - - - - D - 2 - - 3 - 1 - half - - up - - - 2 - 0 - - - - - - - 2 - - TAB - 5 - - - - - C - 2 - - 3 - 1 - quarter - - up - - - 3 - 3 - - - - - - D - 2 - - 3 - 1 - quarter - - up - - - 2 - 0 - - - - - - - 1 - - TAB - 5 - - - - - D - 2 - - 3 - 1 - half - - up - - - 2 - 0 - - - - - - - 2 - - TAB - 5 - - - - - D - 2 - - 3 - 1 - quarter - - up - - - 2 - 0 - - - - - - C - 2 - - 3 - 1 - quarter - - up - - - 3 - 3 - - - - - - - 1 - - TAB - 5 - - - - - D - 2 - - 3 - 1 - half - - up - - - 2 - 0 - - - - - - - 2 - - TAB - 5 - - - - - C - 2 - - 3 - 1 - quarter - - up - - - 3 - 3 - - - - - - D - 2 - - 3 - 1 - quarter - - up - - - 2 - 0 - - - - - - - 1 - - TAB - 5 - - - - - D - 2 - - 3 - 1 - half - - up - - - 2 - 0 - - - - - - - - TAB - 5 - - - - - C - 2 - - 3 - 1 - half - - up - - - 3 - 3 - - - - - - - - TAB - 5 - - - - - A - 1 - - 3 - 1 - half - - up - - - 3 - 0 - - - - - - - 2 - - TAB - 5 - - - - - F - 1 - - 3 - 1 - quarter - - up - - - 4 - 1 - - - - - - G - 1 - - 3 - 1 - quarter - - up - - - 4 - 3 - - - - - - - 1 - - TAB - 5 - - - - - D - 2 - - 3 - 1 - half - - up - - - 2 - 0 - - - - - - - - TAB - 5 - - - - - C - 2 - - 3 - 1 - half - - up - - - 3 - 3 - - - - - - - - TAB - 5 - - - - - A - 1 - - 3 - 1 - half - - up - - - 3 - 0 - - - - - - - 2 - - TAB - 5 - - - - - F - 1 - - 3 - 1 - quarter - - up - - - 4 - 1 - - - - - - C - 2 - - 3 - 1 - quarter - - up - - - 3 - 3 - - - - - - - 1 - - TAB - 5 - - - - - D - 2 - - 3 - 1 - half - - up - - - 2 - 0 - - - - - - - 2 - - TAB - 5 - - - - - D - 2 - - 3 - 1 - quarter - - up - - - 2 - 0 - - - - - - C - 2 - - 3 - 1 - quarter - - up - - - 3 - 3 - - - - - - - 1 - - TAB - 5 - - - - - D - 2 - - 3 - 1 - half - - up - - - 2 - 0 - - - - - - - 2 - - TAB - 5 - - - - - C - 2 - - 3 - 1 - quarter - - up - - - 3 - 3 - - - - - - D - 2 - - 3 - 1 - quarter - - up - - - 2 - 0 - - - - - - - 1 - - TAB - 5 - - - - - D - 2 - - 3 - 1 - half - - up - - - 2 - 0 - - - - - - - 2 - - TAB - 5 - - - - - D - 2 - - 3 - 1 - quarter - - up - - - 2 - 0 - - - - - - C - 2 - - 3 - 1 - quarter - - up - - - 3 - 3 - - - - - - - 1 - - TAB - 5 - - - - - D - 2 - - 3 - 1 - half - - up - - - 2 - 0 - - - - - - - 2 - - TAB - 5 - - - - - C - 2 - - 3 - 1 - quarter - - up - - - 3 - 3 - - - - - - D - 2 - - 3 - 1 - quarter - - up - - - 2 - 0 - - - - - - - 1 - - TAB - 5 - - - - - D - 2 - - 3 - 1 - half - - up - - - 2 - 0 - - - - - - - - TAB - 5 - - - - - C - 2 - - 3 - 1 - half - - up - - - 3 - 3 - - - - - - - - TAB - 5 - - - - - A - 1 - - 3 - 1 - half - - up - - - 3 - 0 - - - - - - - 2 - - TAB - 5 - - - - - F - 1 - - 3 - 1 - quarter - - up - - - 4 - 1 - - - - - - G - 1 - - 3 - 1 - quarter - - up - - - 4 - 3 - - - - - - - 1 - - TAB - 5 - - - - - D - 2 - - 3 - 1 - half - - up - - - 2 - 0 - - - - - - - - TAB - 5 - - - - - C - 2 - - 3 - 1 - half - - up - - - 3 - 3 - - - - - - - - TAB - 5 - - - - - A - 1 - - 3 - 1 - half - - up - - - 3 - 0 - - - - - - - 2 - - TAB - 5 - - - - - F - 1 - - 3 - 1 - quarter - - up - - - 4 - 1 - - - - - - C - 2 - - 3 - 1 - quarter - - up - - - 3 - 3 - - - - - - - 1 - - TAB - 5 - - - - - D - 2 - - 3 - 1 - half - - up - - - 2 - 0 - - - - - - - 2 - - TAB - 5 - - - - - D - 2 - - 3 - 1 - quarter - - up - - - 2 - 0 - - - - - - C - 2 - - 3 - 1 - quarter - - up - - - 3 - 3 - - - - - - - 1 - - TAB - 5 - - - - - D - 2 - - 3 - 1 - half - - up - - - 2 - 0 - - - - - - - 2 - - TAB - 5 - - - - - C - 2 - - 3 - 1 - quarter - - up - - - 3 - 3 - - - - - - D - 2 - - 3 - 1 - quarter - - up - - - 2 - 0 - - - - - - - 1 - - TAB - 5 - - - - - D - 2 - - 3 - 1 - half - - up - - - 2 - 0 - - - - - - - 2 - - TAB - 5 - - - - - D - 2 - - 3 - 1 - quarter - - up - - - 2 - 0 - - - - - - C - 2 - - 3 - 1 - quarter - - up - - - 3 - 3 - - - - - - - 1 - - TAB - 5 - - - - - D - 2 - - 3 - 1 - half - - up - - - 2 - 0 - - - - - - - 2 - - TAB - 5 - - - - - C - 2 - - 3 - 1 - quarter - - down - - - 3 - 3 - - - - - - D - 2 - - 3 - 1 - quarter - - up - - - 2 - 0 - - - - - - - 1 - - TAB - 5 - - - - - D - 2 - - 3 - 1 - half - - down - - - 2 - 0 - - - - - - - - TAB - 5 - - - - - C - 2 - - 3 - 1 - half - - down - - - 3 - 3 - - - - - - - - TAB - 5 - - - - - A - 1 - - 3 - 1 - half - - down - - - 3 - 0 - - - - - - - 2 - - TAB - 5 - - - - - F - 1 - - 3 - 1 - quarter - - down - - - 4 - 1 - - - - - - G - 1 - - 3 - 1 - quarter - - down - - - 4 - 3 - - - - - - - 1 - - TAB - 5 - - - - - D - 2 - - 3 - 1 - half - - down - - - 2 - 0 - - - - - - - - TAB - 5 - - - - - C - 2 - - 3 - 1 - half - - down - - - 3 - 3 - - - - - - - - TAB - 5 - - - - - A - 1 - - 3 - 1 - half - - down - - - 3 - 0 - - - - - - - 2 - - TAB - 5 - - - - - F - 1 - - 3 - 1 - quarter - - down - - - 4 - 1 - - - - - - C - 2 - - 3 - 1 - quarter - - down - - - 3 - 3 - - - - - - - 1 - - TAB - 5 - - - - - D - 2 - - 3 - 1 - half - - down - - - 2 - 0 - - - - - - - - - 1 - - 0 - major - - - - TAB - 5 - - - 6 - - D - 2 - - - G - 2 - - - C - 3 - - - F - 3 - - - A - 3 - - - D - 4 - - - - 0 - 0 - 0 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - 2 - - - TAB - 5 - - - - - 2 - 1 - quarter - - - - 1 - 1 - eighth - - - - - 1 - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - 4 - - TAB - 5 - - - - - 4 - 1 - quarter - - - - A - 5 - - 1 - 1 - 16th - up - begin - begin - - - 1 - 17 - - - - - - G - 5 - - 1 - 1 - 16th - up - end - end - - - 1 - 15 - - - - - - A - 5 - - 6 - - 1 - quarter - - up - - - 1 - 17 - - - - - - - - - TAB - 5 - - - - - A - 5 - - 4 - - 1 - quarter - up - - - 1 - 17 - - - - - - - A - 5 - - 1 - 1 - 16th - up - begin - begin - - - 1 - 17 - - - - - - G - 5 - - 1 - 1 - 16th - up - end - end - - - 1 - 15 - - - - - - E - 5 - - 6 - 1 - quarter - - up - - - 2 - 17 - - - - - - - - TAB - 5 - - - - - 4 - 1 - quarter - - - - A - 5 - - 1 - 1 - 16th - up - begin - begin - - - 1 - 17 - - - - - - G - 5 - - 1 - 1 - 16th - up - end - end - - - 1 - 15 - - - - - - A - 5 - - 6 - 1 - quarter - - up - - - 1 - 17 - - - - - - - - TAB - 5 - - - - - A - 5 - - 3 - 1 - eighth - - up - begin - - - 1 - 17 - - - - - - G - 5 - - 1 - 1 - 16th - up - continue - - - 1 - 15 - - - - - - E - 5 - - 2 - 1 - eighth - up - end - - - 2 - 17 - - - - - - D - 5 - - 6 - 1 - quarter - - up - - - 2 - 15 - - - - - - - - TAB - 5 - - - - - 4 - 1 - quarter - - - - A - 5 - - 1 - 1 - 16th - up - begin - begin - - - 1 - 17 - - - - - - - F - 5 - - 1 - 1 - 16th - up - begin - begin - - - 2 - 18 - - - - - - G - 5 - - 1 - 1 - 16th - up - end - end - - - 1 - 15 - - - - - - - E - 5 - - 1 - 1 - 16th - up - end - end - - - 2 - 17 - - - - - - A - 5 - - 6 - - 1 - quarter - - up - - - 1 - 17 - - - - - - - - F - 5 - - 6 - - 1 - quarter - - up - - - 2 - 18 - - - - - - - - - TAB - 5 - - - - - A - 5 - - 4 - - 1 - quarter - up - - - 1 - 17 - - - - - - - - F - 5 - - 4 - - 1 - quarter - up - - - 2 - 18 - - - - - - - A - 5 - - 1 - 1 - 16th - up - begin - begin - - - 1 - 17 - - - - - - - F - 5 - - 1 - 1 - 16th - up - begin - begin - - - 2 - 18 - - - - - - G - 5 - - 1 - 1 - 16th - up - end - end - - - 1 - 15 - - - - - - - E - 5 - - 1 - 1 - 16th - up - end - end - - - 2 - 17 - - - - - - E - 5 - - 6 - 1 - quarter - - up - - - 2 - 17 - - - - - - - C - 5 - - 6 - 1 - quarter - - up - - - 3 - 17 - - - - - - - - TAB - 5 - - - - - 4 - 1 - quarter - - - - A - 5 - - 1 - 1 - 16th - up - begin - begin - - - 1 - 17 - - - - - - - F - 5 - - 1 - 1 - 16th - up - begin - begin - - - 2 - 18 - - - - - - G - 5 - - 1 - 1 - 16th - up - end - end - - - 1 - 15 - - - - - - - E - 5 - - 1 - 1 - 16th - up - end - end - - - 2 - 17 - - - - - - A - 5 - - 6 - 1 - quarter - - up - - - 1 - 17 - - - - - - - F - 5 - - 6 - 1 - quarter - - up - - - 2 - 18 - - - - - - - - TAB - 5 - - - - - A - 5 - - 3 - 1 - eighth - - up - begin - - - 1 - 17 - - - - - - - F - 5 - - 3 - 1 - eighth - - up - begin - - - 2 - 18 - - - - - - G - 5 - - 1 - 1 - 16th - up - continue - - - 1 - 15 - - - - - - - E - 5 - - 1 - 1 - 16th - up - continue - - - 2 - 17 - - - - - - E - 5 - - 2 - 1 - eighth - up - end - - - 2 - 17 - - - - - - - C - 5 - - 2 - 1 - eighth - up - end - - - 3 - 17 - - - - - - D - 5 - - 6 - 1 - quarter - - up - - - 2 - 15 - - - - - - - A - 4 - - 6 - 1 - quarter - - up - - - 3 - 14 - - - - - - - 1 - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - 4 - - TAB - 5 - - - - - 4 - 1 - quarter - - - - A - 5 - - 1 - 1 - 16th - down - begin - begin - - - 1 - 17 - - - - - - G - 5 - - 1 - 1 - 16th - down - end - end - - - 1 - 15 - - - - - - A - 5 - - 6 - - 1 - quarter - - down - - - 1 - 17 - - - - - - - - - TAB - 5 - - - - - A - 5 - - 4 - - 1 - quarter - up - - - 1 - 17 - - - - - - - A - 5 - - 1 - 1 - 16th - up - begin - begin - - - 1 - 17 - - - - - - G - 5 - - 1 - 1 - 16th - up - end - end - - - 1 - 15 - - - - - - E - 5 - - 6 - 1 - quarter - - up - - - 2 - 17 - - - - - - - - TAB - 5 - - - - - 4 - 1 - quarter - - - - A - 5 - - 1 - 1 - 16th - up - begin - begin - - - 1 - 17 - - - - - - G - 5 - - 1 - 1 - 16th - up - end - end - - - 1 - 15 - - - - - - A - 5 - - 6 - 1 - quarter - - up - - - 1 - 17 - - - - - - - - TAB - 5 - - - - - A - 5 - - 3 - 1 - eighth - - up - begin - - - 1 - 17 - - - - - - G - 5 - - 1 - 1 - 16th - up - continue - - - 1 - 15 - - - - - - E - 5 - - 2 - 1 - eighth - up - end - - - 2 - 17 - - - - - - D - 5 - - 6 - 1 - quarter - - up - - - 2 - 15 - - - - - - - - TAB - 5 - - - - - 4 - 1 - quarter - - - - A - 5 - - 1 - 1 - 16th - up - begin - begin - - - 1 - 17 - - - - - - - F - 5 - - 1 - 1 - 16th - up - begin - begin - - - 2 - 18 - - - - - - G - 5 - - 1 - 1 - 16th - up - end - end - - - 1 - 15 - - - - - - - E - 5 - - 1 - 1 - 16th - up - end - end - - - 2 - 17 - - - - - - A - 5 - - 6 - - 1 - quarter - - up - - - 1 - 17 - - - - - - - - F - 5 - - 6 - - 1 - quarter - - up - - - 2 - 18 - - - - - - - - - TAB - 5 - - - - - A - 5 - - 4 - - 1 - quarter - up - - - 1 - 17 - - - - - - - - F - 5 - - 4 - - 1 - quarter - up - - - 2 - 18 - - - - - - - A - 5 - - 1 - 1 - 16th - up - begin - begin - - - 1 - 17 - - - - - - - F - 5 - - 1 - 1 - 16th - up - begin - begin - - - 2 - 18 - - - - - - G - 5 - - 1 - 1 - 16th - up - end - end - - - 1 - 15 - - - - - - - E - 5 - - 1 - 1 - 16th - up - end - end - - - 2 - 17 - - - - - - E - 5 - - 6 - 1 - quarter - - up - - - 2 - 17 - - - - - - - C - 5 - - 6 - 1 - quarter - - up - - - 3 - 17 - - - - - - - - TAB - 5 - - - - - 4 - 1 - quarter - - - - A - 5 - - 1 - 1 - 16th - up - begin - begin - - - 1 - 17 - - - - - - - F - 5 - - 1 - 1 - 16th - up - begin - begin - - - 2 - 18 - - - - - - G - 5 - - 1 - 1 - 16th - down - end - end - - - 1 - 15 - - - - - - - E - 5 - - 1 - 1 - 16th - down - end - end - - - 2 - 17 - - - - - - A - 5 - - 6 - 1 - quarter - - up - - - 1 - 17 - - - - - - - F - 5 - - 6 - 1 - quarter - - up - - - 2 - 18 - - - - - - - - TAB - 5 - - - - - A - 5 - - 3 - 1 - eighth - - up - begin - - - 1 - 17 - - - - - - - F - 5 - - 3 - 1 - eighth - - up - begin - - - 2 - 18 - - - - - - G - 5 - - 1 - 1 - 16th - up - continue - - - 1 - 15 - - - - - - - E - 5 - - 1 - 1 - 16th - up - continue - - - 2 - 17 - - - - - - E - 5 - - 2 - 1 - eighth - down - end - - - 2 - 17 - - - - - - - C - 5 - - 2 - 1 - eighth - down - end - - - 3 - 17 - - - - - - D - 5 - - 6 - 1 - quarter - - down - - - 2 - 15 - - - - - - - A - 4 - - 6 - 1 - quarter - - down - - - 3 - 14 - - - - - - - 2 - - TAB - 5 - - - - - 3 - 1 - quarter - - - - - 3 - 1 - quarter - - - - - - - TAB - 5 - - - - - 3 - 1 - quarter - - - - - 3 - 1 - quarter - - - - - - - TAB - 5 - - - - - 3 - 1 - quarter - - - - - 3 - 1 - quarter - - - - - - - TAB - 5 - - - - - 3 - 1 - quarter - - - - - 3 - 1 - quarter - - - - - - - TAB - 5 - - - - - 3 - 1 - quarter - - - - - 3 - 1 - quarter - - - - - - - TAB - 5 - - - - - 3 - 1 - quarter - - - - - 3 - 1 - quarter - - - - - - - TAB - 5 - - - - - 3 - 1 - quarter - - - - - 3 - 1 - quarter - - - - - - - TAB - 5 - - - - - 3 - 1 - quarter - - - - - 3 - 1 - quarter - - - - - - - TAB - 5 - - - - - 3 - 1 - quarter - - - - - 3 - 1 - quarter - - - - - diff --git a/Source/AlphaTab.Test/TestFiles/MusicXml/Test01.xml b/Source/AlphaTab.Test/TestFiles/MusicXml/Test01.xml deleted file mode 100644 index 8a7450da2..000000000 --- a/Source/AlphaTab.Test/TestFiles/MusicXml/Test01.xml +++ /dev/null @@ -1,345 +0,0 @@ - - - - Title - - Music - Artist - Words - Copyright - Tab - - 2016-12-19 - Guitar Pro 6 - - - - - Track 1 - S-Gt - - 1 - 1 - 26 - 80 - 0 - - - - Track 2 - S-Gt - - 3 - 1 - 26 - 80 - 0 - - - - - - - 1 - - 0 - major - - - - TAB - 5 - - - 6 - - E - 2 - - - A - 2 - - - D - 3 - - - G - 3 - - - B - 3 - - - E - 4 - - - - 0 - 0 - 0 - - - - - - quarter - 120 - - - - - - - 1 - 1 - quarter - - - - 3 - 1 - half - - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 3 - 1 - half - - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 3 - 1 - half - - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 3 - 1 - half - - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 3 - 1 - half - - - - - - - - 1 - - 0 - major - - - - TAB - 5 - - - 6 - - E - 2 - - - A - 2 - - - D - 3 - - - G - 3 - - - B - 3 - - - E - 4 - - - - 0 - 0 - 0 - - - - - 1 - 1 - quarter - - - - 3 - 1 - half - - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 3 - 1 - half - - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 3 - 1 - half - - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 3 - 1 - half - - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 3 - 1 - half - - - - - diff --git a/Source/AlphaTab.Test/TestFiles/MusicXml/Test02.xml b/Source/AlphaTab.Test/TestFiles/MusicXml/Test02.xml deleted file mode 100644 index 9492a37a4..000000000 --- a/Source/AlphaTab.Test/TestFiles/MusicXml/Test02.xml +++ /dev/null @@ -1,653 +0,0 @@ - - - - - - 2016-12-19 - Guitar Pro 6 - - - - - Track 1 - S-Gt - - 1 - 1 - 26 - 80 - 0 - - - - - - - 16 - - 0 - major - - - - TAB - 5 - - - 6 - - E - 2 - - - A - 2 - - - D - 3 - - - G - 3 - - - B - 3 - - - E - 4 - - - - 0 - 0 - 0 - - - - - - quarter - 120 - - - - - - - F - 2 - - 64 - 1 - whole - up - - - 6 - 1 - - - - - - F - 1 - 2 - - 64 - 1 - whole - sharp - up - - - 6 - 2 - - - - - - G - 2 - - 64 - 1 - whole - up - - - 6 - 3 - - - - - - G - 1 - 2 - - 64 - 1 - whole - sharp - up - - - 6 - 4 - - - - - - 64 - 1 - whole - - - - F - 2 - - 32 - 1 - half - natural - up - - - 6 - 1 - - - - - - F - 1 - 2 - - 32 - 1 - half - sharp - up - - - 6 - 2 - - - - - - G - 2 - - 32 - 1 - half - natural - up - - - 6 - 3 - - - - - - G - 1 - 2 - - 32 - 1 - half - sharp - up - - - 6 - 4 - - - - - - 32 - 1 - half - - - - F - 2 - - 16 - 1 - quarter - natural - up - - - 6 - 1 - - - - - - F - 1 - 2 - - 16 - 1 - quarter - sharp - up - - - 6 - 2 - - - - - - G - 2 - - 16 - 1 - quarter - natural - up - - - 6 - 3 - - - - - - G - 1 - 2 - - 16 - 1 - quarter - sharp - up - - - 6 - 4 - - - - - - 16 - 1 - quarter - - - - F - 2 - - 8 - 1 - eighth - natural - up - begin - - - 6 - 1 - - - - - - F - 1 - 2 - - 8 - 1 - eighth - sharp - up - end - - - 6 - 2 - - - - - - G - 2 - - 8 - 1 - eighth - natural - up - begin - - - 6 - 3 - - - - - - G - 1 - 2 - - 8 - 1 - eighth - sharp - up - end - - - 6 - 4 - - - - - - 8 - 1 - eighth - - - - F - 2 - - 4 - 1 - 16th - natural - up - begin - begin - - - 6 - 1 - - - - - - F - 1 - 2 - - 4 - 1 - 16th - sharp - up - end - end - - - 6 - 2 - - - - - - G - 2 - - 4 - 1 - 16th - natural - up - begin - begin - - - 6 - 3 - - - - - - G - 1 - 2 - - 4 - 1 - 16th - sharp - up - end - end - - - 6 - 4 - - - - - - 4 - 1 - 16th - - - - F - 2 - - 2 - 1 - 32nd - natural - up - begin - begin - begin - - - 6 - 1 - - - - - - F - 1 - 2 - - 2 - 1 - 32nd - sharp - up - end - end - end - - - 6 - 2 - - - - - - G - 2 - - 2 - 1 - 32nd - natural - up - begin - begin - begin - - - 6 - 3 - - - - - - G - 1 - 2 - - 2 - 1 - 32nd - sharp - up - end - end - end - - - 6 - 4 - - - - - - 2 - 1 - 32nd - - - - F - 2 - - 1 - 1 - 64th - natural - up - begin - begin - begin - begin - - - 6 - 1 - - - - - - F - 1 - 2 - - 1 - 1 - 64th - sharp - up - continue - continue - continue - continue - - - 6 - 2 - - - - - - G - 2 - - 1 - 1 - 64th - natural - up - continue - continue - continue - continue - - - 6 - 3 - - - - - - G - 1 - 2 - - 1 - 1 - 64th - sharp - up - end - end - end - end - - - 6 - 4 - - - - - - 1 - 1 - 64th - - - - diff --git a/Source/AlphaTab.Test/TestFiles/MusicXml/Test03.xml b/Source/AlphaTab.Test/TestFiles/MusicXml/Test03.xml deleted file mode 100644 index c96e8bc2f..000000000 --- a/Source/AlphaTab.Test/TestFiles/MusicXml/Test03.xml +++ /dev/null @@ -1,218 +0,0 @@ - - - - - - 2016-12-19 - Guitar Pro 6 - - - - - Track 1 - S-Gt - - 1 - 1 - 26 - 80 - 0 - - - - - - - 1 - - 0 - major - - - - TAB - 5 - - - 6 - - E - 2 - - - A - 2 - - - D - 3 - - - G - 3 - - - B - 3 - - - E - 4 - - - - 0 - 0 - 0 - - - - - - quarter - 120 - - - - - - - 1 - 1 - quarter - - - - 3 - 1 - half - - - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 2 - 1 - half - - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 1 - 1 - quarter - - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - 1 - - - - 1 - 2 - quarter - - - - - 2 - - - TAB - 5 - - - - - 2 - 1 - quarter - - - - 3 - 1 - quarter - - - - - - - TAB - 5 - - - - - 2 - 1 - quarter - - - - 3 - 1 - quarter - - - - - diff --git a/Source/AlphaTab.Test/TestFiles/MusicXml/TestAccentuations.xml b/Source/AlphaTab.Test/TestFiles/MusicXml/TestAccentuations.xml deleted file mode 100644 index c511ddddd..000000000 --- a/Source/AlphaTab.Test/TestFiles/MusicXml/TestAccentuations.xml +++ /dev/null @@ -1,153 +0,0 @@ - - - - - - 2016-12-19 - Guitar Pro 6 - - - - - Track 1 - S-Gt - - 1 - 1 - 26 - 80 - 0 - - - - - - - 1 - - 0 - major - - - - TAB - 5 - - - 6 - - E - 2 - - - A - 2 - - - D - 3 - - - G - 3 - - - B - 3 - - - E - 4 - - - - 0 - 0 - 0 - - - - - - quarter - 120 - - - - - - - A - 3 - - 1 - 1 - quarter - up - - - 3 - 2 - - - - - - A - 3 - - 1 - 1 - quarter - up - - - - - - 3 - 2 - - - - - - A - 3 - - 1 - 1 - quarter - up - - - - - - 3 - 2 - - - - - - A - 3 - - 1 - 1 - quarter - up - - - 3 - 2 - - - - - - diff --git a/Source/AlphaTab.Test/TestFiles/MusicXml/TestBends.xml b/Source/AlphaTab.Test/TestFiles/MusicXml/TestBends.xml deleted file mode 100644 index 2826067ab..000000000 --- a/Source/AlphaTab.Test/TestFiles/MusicXml/TestBends.xml +++ /dev/null @@ -1,155 +0,0 @@ - - - - - - 2016-12-19 - Guitar Pro 6 - - - - - Track 1 - S-Gt - - 1 - 1 - 26 - 80 - 0 - - - - - - - 1 - - 0 - major - - - - TAB - 5 - - - 6 - - E - 2 - - - A - 2 - - - D - 3 - - - G - 3 - - - B - 3 - - - E - 4 - - - - 0 - 0 - 0 - - - - - - quarter - 120 - - - - - - - A - 1 - 3 - - 2 - 1 - half - sharp - up - - - - 2 - - 3 - 3 - - - - - - A - 1 - 3 - - 2 - 1 - half - up - - - - 2 - - 3 - 3 - - - - - - - - TAB - 5 - - - - - F - 4 - - 4 - 1 - whole - down - - - - 6 - - - 3 - - - 3 - 10 - - - - - - diff --git a/Source/AlphaTab.Test/TestFiles/MusicXml/TestDead.xml b/Source/AlphaTab.Test/TestFiles/MusicXml/TestDead.xml deleted file mode 100644 index 0ec955f56..000000000 --- a/Source/AlphaTab.Test/TestFiles/MusicXml/TestDead.xml +++ /dev/null @@ -1,147 +0,0 @@ - - - - - - 2016-12-19 - Guitar Pro 6 - - - - - Track 1 - S-Gt - - 1 - 1 - 26 - 80 - 0 - - - - - - - 1 - - 0 - major - - - - TAB - 5 - - - 6 - - E - 2 - - - A - 2 - - - D - 3 - - - G - 3 - - - B - 3 - - - E - 4 - - - - 0 - 0 - 0 - - - - - - quarter - 120 - - - - - - - F - 2 - - 1 - 1 - quarter - up - - - 6 - 1 - - - - - - B - 2 - - 1 - 1 - quarter - up - - - 5 - 2 - - - - - - F - 3 - - 1 - 1 - quarter - up - - - 4 - 3 - - - - - - B - 3 - - 1 - 1 - quarter - down - - - 3 - 4 - - - - - - diff --git a/Source/AlphaTab.Test/TestFiles/MusicXml/TestFingering.xml b/Source/AlphaTab.Test/TestFiles/MusicXml/TestFingering.xml deleted file mode 100644 index 77a269f75..000000000 --- a/Source/AlphaTab.Test/TestFiles/MusicXml/TestFingering.xml +++ /dev/null @@ -1,264 +0,0 @@ - - - - - - 2016-12-19 - Guitar Pro 6 - - - - - Track 1 - S-Gt - - 1 - 1 - 26 - 80 - 0 - - - - - - - 1 - - 0 - major - - - - TAB - 5 - - - 6 - - E - 2 - - - A - 2 - - - D - 3 - - - G - 3 - - - B - 3 - - - E - 4 - - - - 0 - 0 - 0 - - - - - - quarter - 120 - - - - - - - C - 1 - 4 - - 1 - 1 - quarter - sharp - down - - - - 2 - 2 - - - - - - C - 1 - 4 - - 1 - 1 - quarter - down - - - - 2 - 2 - - - - - - C - 1 - 4 - - 1 - 1 - quarter - down - - - - 2 - 2 - - - - - - C - 1 - 4 - - 1 - 1 - quarter - down - - - - 2 - 2 - - - - - - C - 1 - 4 - - 1 - 1 - quarter - down - - - - 2 - 2 - - - - - - C - 1 - 4 - - 1 - 1 - quarter - down - - - p - 2 - 2 - - - - - - C - 1 - 4 - - 1 - 1 - quarter - down - - - i - 2 - 2 - - - - - - C - 1 - 4 - - 1 - 1 - quarter - down - - - m - 2 - 2 - - - - - - C - 1 - 4 - - 1 - 1 - quarter - down - - - a - 2 - 2 - - - - - - C - 1 - 4 - - 1 - 1 - quarter - down - - - c - 2 - 2 - - - - - - diff --git a/Source/AlphaTab.Test/TestFiles/MusicXml/TestGrace.xml b/Source/AlphaTab.Test/TestFiles/MusicXml/TestGrace.xml deleted file mode 100644 index 5761344ac..000000000 --- a/Source/AlphaTab.Test/TestFiles/MusicXml/TestGrace.xml +++ /dev/null @@ -1,145 +0,0 @@ - - - - - - 2016-12-19 - Guitar Pro 6 - - - - - Track 1 - S-Gt - - 1 - 1 - 26 - 80 - 0 - - - - - - - 8 - - 0 - major - - - - TAB - 5 - - - 6 - - E - 2 - - - A - 2 - - - D - 3 - - - G - 3 - - - B - 3 - - - E - 4 - - - - 0 - 0 - 0 - - - - - - quarter - 120 - - - - - - - - A - 1 - 3 - - 1 - 32nd - sharp - up - - - - A - 3 - - 7 - 1 - quarter - natural - up - - - 3 - 2 - - - - - - - A - 3 - - 1 - 32nd - up - - - - A - 3 - - 8 - 1 - quarter - up - - - 3 - 2 - - - - - - - 16 - 1 - half - - - - diff --git a/Source/AlphaTab.Test/TestFiles/MusicXml/TestHammer.xml b/Source/AlphaTab.Test/TestFiles/MusicXml/TestHammer.xml deleted file mode 100644 index 0e95d65eb..000000000 --- a/Source/AlphaTab.Test/TestFiles/MusicXml/TestHammer.xml +++ /dev/null @@ -1,478 +0,0 @@ - - - - - - 2016-12-19 - Guitar Pro 6 - - - - - Track 1 - S-Gt - - 1 - 1 - 26 - 80 - 0 - - - - - - - 1 - - 0 - major - - - - TAB - 5 - - - 6 - - E - 2 - - - A - 2 - - - D - 3 - - - G - 3 - - - B - 3 - - - E - 4 - - - - 0 - 0 - 0 - - - - - - quarter - 120 - - - - - - - C - 4 - - 1 - 1 - quarter - up - - - 2 - 1 - - - - - - - - A - 1 - 3 - - 1 - 1 - quarter - sharp - up - - - H - 3 - 3 - - - - - - - - D - 3 - - 1 - 1 - quarter - up - - - H - 5 - 5 - - - - - - - - B - 2 - - 1 - 1 - quarter - up - - - H - 6 - 7 - - - - - - - C - 1 - 4 - - 1 - 1 - quarter - sharp - up - - - 2 - 2 - - - - - - - - B - 3 - - 1 - 1 - quarter - up - - - - 3 - 4 - - - - - - - - D - 1 - 3 - - 1 - 1 - quarter - sharp - up - - - - 5 - 6 - - - - - - - - C - 3 - - 1 - 1 - quarter - up - - - - 6 - 8 - - - - - - - E - 4 - - 1 - 1 - quarter - down - - - H - 3 - 9 - - - - - - - - C - 1 - 4 - - 1 - 1 - quarter - down - - - H - 4 - 11 - - - - - - - F - 4 - - 1 - 1 - quarter - down - - - - 3 - 10 - - - - - - - - D - 4 - - 1 - 1 - quarter - down - - - - 4 - 12 - - - - - - - - - TAB - 5 - - - - - G - 1 - 4 - - 1 - 1 - quarter - sharp - up - - - H - 3 - 13 - - - - - - - - G - 1 - 3 - - 1 - 1 - quarter - sharp - up - - - H - 6 - 16 - - - - - - - A - 4 - - 1 - 1 - quarter - up - - - H - - 3 - 14 - - - - - - - - A - 3 - - 1 - 1 - quarter - up - - - - 6 - 17 - - - - - - - A - 1 - 4 - - 1 - 1 - quarter - sharp - up - - - - 3 - 15 - - - - - - - - A - 1 - 3 - - 1 - 1 - quarter - sharp - up - - - H - 6 - 18 - - - - - - - D - 5 - - 1 - 1 - quarter - down - - - H - 3 - 19 - - - - - - - D - 1 - 5 - - 1 - 1 - quarter - sharp - down - - - - 3 - 20 - - - - - - - diff --git a/Source/AlphaTab.Test/TestFiles/MusicXml/TestHarmonics.xml b/Source/AlphaTab.Test/TestFiles/MusicXml/TestHarmonics.xml deleted file mode 100644 index 5437b9c78..000000000 --- a/Source/AlphaTab.Test/TestFiles/MusicXml/TestHarmonics.xml +++ /dev/null @@ -1,195 +0,0 @@ - - - - - - 2016-12-19 - Guitar Pro 6 - - - - - Track 1 - S-Gt - - 1 - 1 - 26 - 80 - 0 - - - - - - - 1 - - 0 - major - - - - TAB - 5 - - - 6 - - E - 2 - - - A - 2 - - - D - 3 - - - G - 3 - - - B - 3 - - - E - 4 - - - - 0 - 0 - 0 - - - - - - quarter - 120 - - - - - - - A - 3 - - 1 - 1 - quarter - down - - - - - - - 3 - 2 - - - - - - A - 3 - - 1 - 1 - quarter - up - - - - - - - 3 - 2 - - - - - - A - 3 - - 1 - 1 - quarter - up - - - - 3 - 2 - - - - - - A - 3 - - 1 - 1 - quarter - up - - - - 3 - 2 - - - - - - A - 3 - - 1 - 1 - quarter - up - - - - 3 - 2 - - - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 3 - 1 - half - - - - - diff --git a/Source/AlphaTab.Test/TestFiles/MusicXml/TestKeySignatures.xml b/Source/AlphaTab.Test/TestFiles/MusicXml/TestKeySignatures.xml deleted file mode 100644 index 27baa08a8..000000000 --- a/Source/AlphaTab.Test/TestFiles/MusicXml/TestKeySignatures.xml +++ /dev/null @@ -1,467 +0,0 @@ - - - - - - 2016-12-19 - Guitar Pro 6 - - - - - Steel Guitar - S-Gt - - 1 - 1 - 26 - 80 - 0 - - - - - - - 1 - - 0 - major - - - - TAB - 5 - - - 6 - - E - 2 - - - A - 2 - - - D - 3 - - - G - 3 - - - B - 3 - - - E - 4 - - - - 0 - 0 - 0 - - - - - - quarter - 120 - - - - - - - - - -1 - major - - - TAB - 5 - - - - - - - -2 - major - - - TAB - 5 - - - - - - - -3 - major - - - TAB - 5 - - - - - - - -4 - major - - - TAB - 5 - - - - - - - -5 - major - - - TAB - 5 - - - - - - - -6 - major - - - TAB - 5 - - - - - - - -7 - major - - - TAB - 5 - - - - - - - 0 - major - - - TAB - 5 - - - - - - - 1 - major - - - TAB - 5 - - - - - - - 2 - major - - - TAB - 5 - - - - - - - 3 - major - - - TAB - 5 - - - - - - - 4 - major - - - TAB - 5 - - - - - - - 5 - major - - - TAB - 5 - - - - - - - 6 - major - - - TAB - 5 - - - - - - - 7 - major - - - TAB - 5 - - - - - - - 0 - minor - - - TAB - 5 - - - - - - - -1 - minor - - - TAB - 5 - - - - - - - -2 - minor - - - TAB - 5 - - - - - - - -3 - minor - - - TAB - 5 - - - - - - - -4 - minor - - - TAB - 5 - - - - - - - -5 - minor - - - TAB - 5 - - - - - - - -6 - minor - - - TAB - 5 - - - - - - - -7 - minor - - - TAB - 5 - - - - - - - 0 - minor - - - TAB - 5 - - - - - - - 1 - minor - - - TAB - 5 - - - - - - - 2 - minor - - - TAB - 5 - - - - - - - 3 - minor - - - TAB - 5 - - - - - - - 4 - minor - - - TAB - 5 - - - - - - - 5 - minor - - - TAB - 5 - - - - - - - 6 - minor - - - TAB - 5 - - - - - - - 7 - minor - - - TAB - 5 - - - - - - - 0 - minor - - - TAB - 5 - - - - - diff --git a/Source/AlphaTab.Test/TestFiles/MusicXml/TestOtherEffects.xml b/Source/AlphaTab.Test/TestFiles/MusicXml/TestOtherEffects.xml deleted file mode 100644 index 9cef119ea..000000000 --- a/Source/AlphaTab.Test/TestFiles/MusicXml/TestOtherEffects.xml +++ /dev/null @@ -1,368 +0,0 @@ - - - - - - 2016-12-19 - Guitar Pro 6 - - - - - Track 1 - S-Gt - - 1 - 1 - 26 - 80 - 0 - - - - - - - 1 - - 0 - major - - - - TAB - 5 - - - 6 - - E - 2 - - - A - 2 - - - D - 3 - - - G - 3 - - - B - 3 - - - E - 4 - - - - 0 - 0 - 0 - - - - - - quarter - 120 - - - - - - - C - 1 - 4 - - 1 - 1 - quarter - sharp - down - - - 2 - 2 - - - - - - C - 1 - 4 - - 1 - 1 - quarter - down - - - - - - 2 - 2 - - - - - - C - 1 - 4 - - 1 - 1 - quarter - down - - - 2 - 2 - - - - - - C - 1 - 4 - - 1 - 1 - quarter - down - - - 2 - 2 - - - - - - - - TAB - 5 - - - - - C - 1 - 4 - - 1 - 1 - quarter - sharp - down - - - 2 - 2 - - - - - - C - 1 - 4 - - 1 - 1 - quarter - down - - - 2 - 2 - - - - - - C - 1 - 4 - - 1 - 1 - quarter - down - - - 2 - 2 - - - - - - C - 1 - 4 - - 1 - 1 - quarter - down - - - 2 - 2 - - - - - - - - TAB - 5 - - - - - C - 1 - 4 - - 1 - 1 - quarter - sharp - down - - - 2 - 2 - - - - - - 3 - 1 - half - - - - - - - TAB - 5 - - - - - C - 1 - 4 - - 1 - 1 - quarter - sharp - down - - - 2 - 2 - - - - - - C - 1 - 4 - - 1 - 1 - quarter - down - - - 2 - 2 - - - - - - 2 - 1 - half - - - - - - TAB - 5 - - - - - C - 1 - 4 - - 1 - 1 - quarter - sharp - down - - - 2 - 2 - - - - - - 3 - 1 - half - - - - - - - TAB - 5 - - - - - 1 - 1 - quarter - - - - 3 - 1 - half - - - - - diff --git a/Source/AlphaTab.Test/TestFiles/MusicXml/TestRanges.xml b/Source/AlphaTab.Test/TestFiles/MusicXml/TestRanges.xml deleted file mode 100644 index d9ce8e11a..000000000 --- a/Source/AlphaTab.Test/TestFiles/MusicXml/TestRanges.xml +++ /dev/null @@ -1,265 +0,0 @@ - - - - - - 2016-12-19 - Guitar Pro 6 - - - - - Track 1 - S-Gt - - 1 - 1 - 26 - 80 - 0 - - - - - - - 1 - - 0 - major - - - - TAB - 5 - - - 6 - - E - 2 - - - A - 2 - - - D - 3 - - - G - 3 - - - B - 3 - - - E - 4 - - - - 0 - 0 - 0 - - - - - - quarter - 120 - - - - - - - B - 2 - - 1 - 1 - quarter - up - - - 5 - 2 - - - - - - B - 2 - - 1 - 1 - quarter - up - - - 5 - 2 - - - - - - B - 2 - - 1 - 1 - quarter - up - - - 5 - 2 - - - - - - B - 2 - - 1 - 1 - quarter - up - - - 5 - 2 - - - - - - - - TAB - 5 - - - - - B - 2 - - 1 - 1 - quarter - up - - - 5 - 2 - - - - - - B - 2 - - 1 - 1 - quarter - up - - - 5 - 2 - - - - - - B - 2 - - 1 - 1 - quarter - up - - - 5 - 2 - - - - - - B - 2 - - 1 - 1 - quarter - up - - - 5 - 2 - - - - - - - - TAB - 5 - - - - - B - 2 - - 1 - 1 - quarter - up - - - 5 - 2 - - - - - - B - 3 - - 1 - 1 - quarter - down - - - 3 - 4 - - - - - - 2 - 1 - half - - - - diff --git a/Source/AlphaTab.Test/TestFiles/MusicXml/TestSlides.xml b/Source/AlphaTab.Test/TestFiles/MusicXml/TestSlides.xml deleted file mode 100644 index f6055e2bc..000000000 --- a/Source/AlphaTab.Test/TestFiles/MusicXml/TestSlides.xml +++ /dev/null @@ -1,268 +0,0 @@ - - - - - - 2016-12-19 - Guitar Pro 6 - - - - - Track 1 - S-Gt - - 1 - 1 - 26 - 80 - 0 - - - - - - - 1 - - 0 - major - - - - TAB - 5 - - - 6 - - E - 2 - - - A - 2 - - - D - 3 - - - G - 3 - - - B - 3 - - - E - 4 - - - - 0 - 0 - 0 - - - - - - quarter - 120 - - - - - - - C - 4 - - 1 - 1 - quarter - up - - - 2 - 1 - - - - - - - - A - 1 - 2 - - 1 - 1 - quarter - sharp - up - - - 5 - 1 - - - - - - - C - 1 - 4 - - 1 - 1 - quarter - sharp - up - - - 2 - 2 - - - - - - - - C - 3 - - 1 - 1 - quarter - up - - - 5 - 3 - - - - - - - A - 1 - 2 - - 1 - 1 - quarter - up - - - 5 - 1 - - - - - - - B - 2 - - 1 - 1 - quarter - up - - - 5 - 2 - - - - - - - - - TAB - 5 - - - - - C - 4 - - 1 - 1 - quarter - down - - - 2 - 1 - - - - - - C - 1 - 4 - - 1 - 1 - quarter - sharp - down - - - 2 - 2 - - - - - - D - 4 - - 1 - 1 - quarter - down - - - 2 - 3 - - - - - - D - 1 - 4 - - 1 - 1 - quarter - sharp - down - - - 2 - 4 - - - - - - diff --git a/Source/AlphaTab.Test/TestFiles/MusicXml/TestStrings.xml b/Source/AlphaTab.Test/TestFiles/MusicXml/TestStrings.xml deleted file mode 100644 index b7d248088..000000000 --- a/Source/AlphaTab.Test/TestFiles/MusicXml/TestStrings.xml +++ /dev/null @@ -1,199 +0,0 @@ - - - - - - 2016-12-19 - Guitar Pro 6 - - - - - Spur 1 - S-Gt - - 1 - 1 - 26 - 80 - 0 - - - - - - - 1 - - 0 - major - - - - TAB - 5 - - - 6 - - E - 2 - - - A - 2 - - - D - 3 - - - G - 3 - - - B - 3 - - - E - 4 - - - - 0 - 0 - 0 - - - - - - quarter - 120 - - - - - - - F - 4 - - 1 - 1 - quarter - up - - - 1 - 1 - - - - - - - C - 1 - 4 - - 1 - 1 - quarter - sharp - up - - - 2 - 2 - - - - - - - A - 1 - 3 - - 1 - 1 - quarter - sharp - up - - - 3 - 3 - - - - - - - F - 1 - 3 - - 1 - 1 - quarter - sharp - up - - - 4 - 4 - - - - - - - D - 3 - - 1 - 1 - quarter - up - - - 5 - 5 - - - - - - - A - 1 - 2 - - 1 - 1 - quarter - sharp - up - - - 6 - 6 - - - - - - 3 - 1 - half - - - - - diff --git a/Source/AlphaTab.Test/TestFiles/MusicXml/TestStrokes.xml b/Source/AlphaTab.Test/TestFiles/MusicXml/TestStrokes.xml deleted file mode 100644 index 65e3872c1..000000000 --- a/Source/AlphaTab.Test/TestFiles/MusicXml/TestStrokes.xml +++ /dev/null @@ -1,259 +0,0 @@ - - - - - - 2016-12-19 - Guitar Pro 6 - - - - - Track 1 - S-Gt - - 1 - 1 - 26 - 80 - 0 - - - - - - - 1 - - 0 - major - - - - TAB - 5 - - - 6 - - E - 2 - - - A - 2 - - - D - 3 - - - G - 3 - - - B - 3 - - - E - 4 - - - - 0 - 0 - 0 - - - - - - quarter - 120 - - - - - - - C - 4 - - 1 - 1 - quarter - up - - - 2 - 1 - - - - - - - - G - 3 - - 1 - 1 - quarter - up - - - 3 - 0 - - - - - - - - E - 3 - - 1 - 1 - quarter - up - - - 4 - 2 - - - - - - - - C - 3 - - 1 - 1 - quarter - up - - - 5 - 3 - - - - - - - C - 4 - - 1 - 1 - quarter - up - - - 2 - 1 - - - - - - - - G - 3 - - 1 - 1 - quarter - up - - - 3 - 0 - - - - - - - - E - 3 - - 1 - 1 - quarter - up - - - 4 - 2 - - - - - - - - C - 3 - - 1 - 1 - quarter - up - - - 5 - 3 - - - - - - - B - 2 - - 1 - 1 - quarter - up - - - - 5 - 2 - - - - - - B - 2 - - 1 - 1 - quarter - up - - - - 5 - 2 - - - - - - diff --git a/Source/AlphaTab.Test/TestFiles/MusicXml/TestTremolo.xml b/Source/AlphaTab.Test/TestFiles/MusicXml/TestTremolo.xml deleted file mode 100644 index 13843bd9d..000000000 --- a/Source/AlphaTab.Test/TestFiles/MusicXml/TestTremolo.xml +++ /dev/null @@ -1,185 +0,0 @@ - - - - - - 2016-12-19 - Guitar Pro 6 - - - - - Track 1 - S-Gt - - 1 - 1 - 26 - 80 - 0 - - - - - - - 1 - - 0 - major - - - - TAB - 5 - - - 6 - - E - 2 - - - A - 2 - - - D - 3 - - - G - 3 - - - B - 3 - - - E - 4 - - - - 0 - 0 - 0 - - - - - - quarter - 120 - - - - - - - F - 4 - - 4 - 1 - whole - down - - - 3 - 10 - - - - - - - - TAB - 5 - - - - - F - 4 - - 4 - 1 - whole - down - - - 3 - 10 - - - - - - - - TAB - 5 - - - - - F - 4 - - 4 - 1 - whole - down - - - 3 - 10 - - - - - - - - TAB - 5 - - - - - F - 4 - - 4 - 1 - whole - down - - - 3 - 10 - - - - - - - - TAB - 5 - - - - - 4 - 1 - whole - - - - diff --git a/Source/AlphaTab.Test/TestFiles/MusicXml/TestTrills.xml b/Source/AlphaTab.Test/TestFiles/MusicXml/TestTrills.xml deleted file mode 100644 index a8b53bb20..000000000 --- a/Source/AlphaTab.Test/TestFiles/MusicXml/TestTrills.xml +++ /dev/null @@ -1,164 +0,0 @@ - - - - - - 2016-12-19 - Guitar Pro 6 - - - - - Track 1 - S-Gt - - 1 - 1 - 26 - 80 - 0 - - - - - - - 1 - - 0 - major - - - - TAB - 5 - - - 6 - - E - 2 - - - A - 2 - - - D - 3 - - - G - 3 - - - B - 3 - - - E - 4 - - - - 0 - 0 - 0 - - - - - - quarter - 120 - - - - - - - C - 1 - 4 - - 1 - 1 - quarter - sharp - down - - - - - - 2 - 2 - - - - - - C - 1 - 4 - - 1 - 1 - quarter - down - - - 3 - - - 2 - 2 - - - - - - C - 1 - 4 - - 1 - 1 - quarter - down - - - 2 - - - 2 - 2 - - - - - - C - 1 - 4 - - 1 - 1 - quarter - down - - - 1 - - - 2 - 2 - - - - - - diff --git a/Source/AlphaTab.Test/TestFiles/MusicXml/TestTuplets.xml b/Source/AlphaTab.Test/TestFiles/MusicXml/TestTuplets.xml deleted file mode 100644 index 703b609a9..000000000 --- a/Source/AlphaTab.Test/TestFiles/MusicXml/TestTuplets.xml +++ /dev/null @@ -1,267 +0,0 @@ - - - - - - 2016-12-19 - Guitar Pro 6 - - - - - Track 1 - S-Gt - - 1 - 1 - 26 - 80 - 0 - - - - - - - 3 - - 0 - major - - - - TAB - 5 - - - 6 - - E - 2 - - - A - 2 - - - D - 3 - - - G - 3 - - - B - 3 - - - E - 4 - - - - 0 - 0 - 0 - - - - - - quarter - 120 - - - - - - - C - 3 - - 2 - 1 - quarter - - 3 - 2 - - up - - - - 5 - 3 - - - - - - C - 3 - - 2 - 1 - quarter - - 3 - 2 - - up - - - 5 - 3 - - - - - - C - 3 - - 2 - 1 - quarter - - 3 - 2 - - up - - - - 5 - 3 - - - - - - 6 - 1 - half - - - - - 5 - - TAB - 5 - - - - - A - 1 - 2 - - 4 - 1 - quarter - sharp - - 5 - 4 - - up - - - - 5 - 1 - - - - - - A - 1 - 2 - - 4 - 1 - quarter - - 5 - 4 - - up - - - 5 - 1 - - - - - - A - 1 - 2 - - 4 - 1 - quarter - - 5 - 4 - - up - - - 5 - 1 - - - - - - A - 1 - 2 - - 4 - 1 - quarter - - 5 - 4 - - up - - - 5 - 1 - - - - - - A - 1 - 2 - - 4 - 1 - quarter - - 5 - 4 - - up - - - 5 - 1 - - - - - - diff --git a/Source/AlphaTab.Test/TestFiles/MusicXml/TestVibrato.xml b/Source/AlphaTab.Test/TestFiles/MusicXml/TestVibrato.xml deleted file mode 100644 index f4e94f64c..000000000 --- a/Source/AlphaTab.Test/TestFiles/MusicXml/TestVibrato.xml +++ /dev/null @@ -1,150 +0,0 @@ - - - - - - 2016-12-19 - Guitar Pro 6 - - - - - Track 1 - S-Gt - - 1 - 1 - 26 - 80 - 0 - - - - - - - 1 - - 0 - major - - - - TAB - 5 - - - 6 - - E - 2 - - - A - 2 - - - D - 3 - - - G - 3 - - - B - 3 - - - E - 4 - - - - 0 - 0 - 0 - - - - - - quarter - 120 - - - - - - - D - 4 - - 1 - 1 - quarter - down - - - 2 - 3 - - - - - - D - 4 - - 1 - 1 - quarter - down - - - 2 - 3 - - - - - - D - 1 - 4 - - 1 - 1 - quarter - sharp - down - - - 2 - 4 - - - - - - D - 1 - 4 - - 1 - 1 - quarter - down - - - 2 - 4 - - - - - - diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/ActorPreludeSample.mxl b/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/ActorPreludeSample.mxl deleted file mode 100644 index 4f7d93c30..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/ActorPreludeSample.mxl and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/ActorPreludeSample.pdf b/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/ActorPreludeSample.pdf deleted file mode 100644 index 9f8a7a8f6..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/ActorPreludeSample.pdf and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/ActorPreludeSample.xml b/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/ActorPreludeSample.xml deleted file mode 100644 index d20894f72..000000000 --- a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/ActorPreludeSample.xml +++ /dev/null @@ -1,44672 +0,0 @@ - - - - Prelude to a Tragedy - - Lee Actor - © 2004 Polygames. All Rights Reserved. - - Finale 2011 for Windows - Dolet 6.0 for Finale - 2011-08-08 - - - - - - - 3.9956 - 40 - - - 3560 - 2797 - - 141 - 115 - 127 - 45 - - - 177 - 79 - 127 - 45 - - - - - 0 - 0 - - 39 - 39 - - - - - - - 93 - - - 0.957 - 5.2083 - 0.957 - 1.875 - 5.0391 - 1.875 - 0.957 - 1.875 - 0.957 - 0.957 - 60 - 60 - 60 - 8 - - - - - - title - Prelude to a Tragedy - - - composer - Lee Actor (2003) - - - rights - © 2004 Polygames. All Rights Reserved. - - - page number - - 2 - - - - page number - - 3 - - - - page number - - 4 - - - - - bracket - yes - - - Piccolo - Picc. - - Picc. (V2k) - - - 1 - 73 - 80 - 0 - - - - 1 -2 - yes - - - Flutes - Fl. - - Fl. (V2k) - - - 2 - 74 - 80 - 0 - - - - - 1 -2 - yes - - - Oboes - Ob. - - Ob. (V2k) - - - 3 - 69 - 80 - 0 - - - - - English Horn - E. H. - - E.H. (V2k) - - - 4 - 70 - 80 - 0 - - - - 1 -2 - yes - - - Clarinets in Bb - - Clarinets in B - flat - - Cl. - - Clar. (V2k) - - - 5 - 72 - 80 - 0 - - - - - Bass Clarinet in Bb - - Bass Clarinet in B - flat - - B. Cl. - - B. Cl. (V2k) - - - 5 - 72 - 80 - 0 - - - - 1 -2 - yes - - - Bassoons - Bsn. - - Bsn. (V2k) - - - 7 - 71 - 80 - 0 - - - - - - Horns in F - Hn. - brace - no - - - bracket - yes - - - 1 -2 - 1 -2 - - Hn. (V2k) - - - 8 - 61 - 80 - 0 - - - - 3 -4 - 3 -4 - - Hn. 2 (V2k) - - - 8 - 61 - 80 - 0 - - - - - 1 -2 - yes - - - Trumpets in C - Tpt. - - Tpt. (V2k) - - - 9 - 57 - 80 - 0 - - - - - Trombones - Trb. - brace - yes - - - 1 -2 - 1 -2 - - Trb. (V2k) - - - 11 - 58 - 80 - 0 - - - - 3 - 3 - - B Trb. (V2k) - - - 11 - 58 - 80 - 0 - - - - - Tuba - Tba. - - Tuba (V2k) - - - 12 - 59 - 80 - 0 - - - - - bracket - yes - - - Timpani - Timp. - - Timp. (V2k) - - - 13 - 48 - 80 - 0 - - - - Percussion - Perc. - brace - yes - - - 1 - 1 - - Splash Cymbal - - - 10 - 1 - 56 - 80 - 0 - - - - 2 - 2 - - Bass Drum - - - 10 - 1 - 36 - 80 - 0 - - - - - - Harp - Hp. - - Harp (V2k) - - - 6 - 47 - 80 - 0 - - - - bracket - yes - - - Violin I - Vln. I - - Vln. (V2k) - - - 14 - 49 - 80 - 0 - - - - Violin II - Vln. II - - Vln. 2 (V2k) - - - 15 - 49 - 80 - 0 - - - - Viola - Vla. - - Va. (V2k) - - - 16 - 50 - 80 - 0 - - - - Violoncello - Vc. - - Vc. (V2k) - - - 16 - 50 - 80 - 0 - - - - Contrabass - Cb. - - Cb. (V2k) - - - 16 - 50 - 80 - 0 - - - - - - - - - - 3560 - 2797 - - 177 - 79 - 64 - 45 - - - - - 150 - 0 - - 191 - - system - - - 8 - - 0 - major - - - - G - 2 - - - 108 - - - 0 - 0 - 1 - - - - - Moderato - - -   - - - - quarter - 85 - - - - - - - 24 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - - - - -

    - - - - - - - B - 5 - - 24 - - 1 - half - - down - - - - - - - - - - - - - - B - 5 - - 16 - - - 1 - half - down - - - - - - - - - - - 3560 - 2797 - - 141 - 115 - 104 - 45 - - - - 22 - - - - - - 100 - - - - - B - 5 - - 32 - - - 1 - whole - - - - - - - - - - - - - - - B - 5 - - 24 - - - 1 - half - - down - - - - - - - - - - - - B - 5 - - 24 - - - 1 - half - - down - - - - - - - - - - - - - - - B - 5 - - 16 - - - 1 - half - down - - - - - - - - - - - - - - - B - 5 - - 32 - - 1 - whole - - - - - - - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - - - - - 16 - - - - - 8 - 1 - quarter - - - - 8 - 1 - quarter - - - - B - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - - - -1 - - - - C - 6 - - 2 - 1 - 16th - down - continue - continue - - - - D - 6 - - 2 - 1 - 16th - down - continue - continue - - - - E - -1 - 6 - - 2 - 1 - 16th - flat - down - end - end - - - - - - - - - -1 - - - - - - - - - - - - - - - - F - 6 - - 2 - 1 - 16th - down - - - - 2 - 1 - 16th - - - - 4 - 1 - eighth - - - - 8 - 1 - quarter - - - - 8 - 1 - quarter - - - - - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - 3560 - 2797 - - 177 - 79 - 109 - 45 - - - - 81 - - - - - - 108 - - - - - 32 - 1 - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - 3560 - 2797 - - 141 - 115 - 104 - 45 - - - - 113 - - - - - - - - 16 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - - - - - - - - - D - 1 - 5 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - - - - - - E - 5 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 5 - - 2 - 1 - 16th - sharp - down - continue - continue - - - - G - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - A - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - B - -1 - 5 - - 2 - 1 - 16th - flat - down - continue - continue - - - - C - 6 - - 2 - 1 - 16th - down - continue - continue - - - - C - 1 - 6 - - 2 - 1 - 16th - sharp - down - end - end - - - - - - - D - 6 - - 2 - 1 - 16th - down - begin - begin - - - - - - - D - 1 - 6 - - 2 - 1 - 16th - sharp - down - continue - continue - - - - E - 6 - - 2 - 1 - 16th - down - continue - continue - - - - F - 6 - - 2 - 1 - 16th - natural - down - end - end - - - - - - - - - -1 - - - - - - - - - - - - - - - - F - 1 - 6 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - G - 6 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 6 - - 2 - 1 - 16th - down - continue - continue - - - - G - 6 - - 2 - 1 - 16th - down - end - end - - - - - - - F - 1 - 6 - - 2 - 1 - 16th - down - begin - begin - - - - - - - G - 6 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 6 - - 2 - 1 - 16th - down - continue - continue - - - - G - 6 - - 2 - 1 - 16th - down - end - end - - - - - - - F - 1 - 6 - - 2 - 1 - 16th - down - begin - begin - - - - - - - G - 6 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 6 - - 2 - 1 - 16th - down - continue - continue - - - - G - 6 - - 2 - 1 - 16th - down - end - end - - - - - - - - - - - - - - 16 - 1 - - - - - - - - - - - - - - - - - - - F - 1 - 6 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - G - 6 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 6 - - 2 - 1 - 16th - down - continue - continue - - - - G - 6 - - 2 - 1 - 16th - down - end - end - - - - - - - F - 1 - 6 - - 2 - 1 - 16th - down - begin - begin - - - - - - - G - 6 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 6 - - 2 - 1 - 16th - down - continue - continue - - - - G - 6 - - 2 - 1 - 16th - down - end - end - - - - - - - 8 - 1 - quarter - - - - - - - - - 95 - - none - - - 8 - - 0 - major - - - - G - 2 - - - 108 - - - - - 24 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - - - - -

    - - - - - - - B - 5 - - 24 - - 1 - half - - down - - - - - - - - F - 1 - 6 - - 24 - - 1 - half - - sharp - down - - - - - - - - - -21 - - - - - - - - - - - B - 5 - - 16 - - 1 - half - down - - - - - - - - F - 1 - 6 - - 16 - - 1 - half - down - - - - - - - - - -1 - - - - - - - 95 - - - - - - 100 - - - - - - - - - - - - - A - 1 - 5 - - 32 - 1 - whole - sharp - - - - - E - 1 - 6 - - 32 - 1 - whole - sharp - - - - - - -30 - - - - - - -1 - - - - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - - - - - 16 - - - - - 8 - 1 - quarter - - - - 8 - 1 - quarter - - - - 1. - - - - - B - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - - - -1 - - - - C - 6 - - 2 - 1 - 16th - down - continue - continue - - - - D - 6 - - 2 - 1 - 16th - down - continue - continue - - - - E - -1 - 6 - - 2 - 1 - 16th - flat - down - end - end - - - - - - - - - -1 - - - - - - - - - - - - - - - - F - 6 - - 2 - 1 - 16th - down - - - - 2 - 1 - 16th - - - - 4 - 1 - eighth - - - - 8 - 1 - quarter - - - - 8 - 1 - quarter - - - - - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - 95 - - - - - - 108 - - - - - 32 - 1 - - - - - - - - -

    - - - - - - - G - 5 - - 32 - 1 - whole - - - - - C - 6 - - 32 - 1 - whole - - - - - - -29 - - - - - - -1 - - - - - - - - - - - - - - - - - - - F - 1 - 5 - - 24 - 1 - half - - sharp - down - - - - - B - 5 - - 24 - 1 - half - - down - - - - - - -22 - - - - - - -1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - -

    - - - - - - - 1. - - - - - F - 1 - 5 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - G - 5 - - 2 - 1 - 16th - down - continue - continue - - - - A - 5 - - 2 - 1 - 16th - down - continue - continue - - - - B - -1 - 5 - - 2 - 1 - 16th - flat - down - end - end - - - - - - - F - 1 - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - G - 5 - - 2 - 1 - 16th - down - continue - continue - - - - A - 5 - - 2 - 1 - 16th - down - continue - continue - - - - B - -1 - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - C - 6 - - 2 - 1 - 16th - down - begin - begin - - - - - - - D - -1 - 6 - - 2 - 1 - 16th - flat - down - continue - continue - - - - E - -1 - 6 - - 2 - 1 - 16th - flat - down - continue - continue - - - - E - 6 - - 2 - 1 - 16th - natural - down - end - end - - - - - - - - - - 95 - - - - - - - - 1. - - - - - F - 6 - - 2 - 1 - 16th - natural - down - begin - begin - - - - - - - G - -1 - 6 - - 2 - 1 - 16th - flat - down - continue - continue - - - - F - 6 - - 2 - 1 - 16th - down - continue - continue - - - - E - -1 - 6 - - 2 - 1 - 16th - flat - down - end - end - - - - - - - D - 6 - - 2 - 1 - 16th - natural - down - begin - begin - - - - - - - C - 6 - - 2 - 1 - 16th - down - continue - continue - - - - B - 5 - - 2 - 1 - 16th - natural - down - continue - continue - - - - A - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - - - - - - - - - - - - - - - - A - 4 - - 2 - 1 - 16th - down - begin - begin - - - - - - - - A - 5 - - 2 - 1 - 16th - down - - - - B - -1 - 4 - - 2 - 1 - 16th - flat - down - continue - continue - - - - - B - -1 - 5 - - 2 - 1 - 16th - flat - down - - - - C - 5 - - 2 - 1 - 16th - down - continue - continue - - - - - C - 6 - - 2 - 1 - 16th - down - - - - D - -1 - 5 - - 2 - 1 - 16th - flat - down - end - end - - - - - - - - D - -1 - 6 - - 2 - 1 - 16th - flat - down - - - - A - 4 - - 2 - 1 - 16th - down - begin - begin - - - - - - - - A - 5 - - 2 - 1 - 16th - down - - - - B - -1 - 4 - - 2 - 1 - 16th - down - continue - continue - - - - - B - -1 - 5 - - 2 - 1 - 16th - down - - - - C - 5 - - 2 - 1 - 16th - down - continue - continue - - - - - C - 6 - - 2 - 1 - 16th - down - - - - C - 1 - 5 - - 2 - 1 - 16th - sharp - down - end - end - - - - - - - - C - 1 - 6 - - 2 - 1 - 16th - sharp - down - - - - D - 1 - 5 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - - D - 1 - 6 - - 2 - 1 - 16th - sharp - down - - - - E - 5 - - 2 - 1 - 16th - down - continue - continue - - - - - E - 6 - - 2 - 1 - 16th - down - - - - F - 1 - 5 - - 2 - 1 - 16th - sharp - down - continue - continue - - - - - F - 1 - 6 - - 2 - 1 - 16th - sharp - down - - - - G - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - - G - 6 - - 2 - 1 - 16th - down - - - - - - - - - - - G - 1 - 5 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - - G - 1 - 6 - - 2 - 1 - 16th - sharp - down - - - - A - 5 - - 2 - 1 - 16th - down - continue - continue - - - - - A - 6 - - 2 - 1 - 16th - down - - - - G - 1 - 5 - - 2 - 1 - 16th - down - continue - continue - - - - - G - 1 - 6 - - 2 - 1 - 16th - down - - - - F - 1 - 5 - - 2 - 1 - 16th - sharp - down - end - end - - - - - - - - F - 1 - 6 - - 2 - 1 - 16th - sharp - down - - - - F - 5 - - 2 - 1 - 16th - natural - down - begin - begin - - - - - - - - F - 6 - - 2 - 1 - 16th - natural - down - - - - E - -1 - 5 - - 2 - 1 - 16th - flat - down - continue - continue - - - - - E - -1 - 6 - - 2 - 1 - 16th - flat - down - - - - D - 5 - - 2 - 1 - 16th - down - continue - continue - - - - - D - 6 - - 2 - 1 - 16th - down - - - - C - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - - C - 6 - - 2 - 1 - 16th - down - - - - - - - - - - - - - - - - - - - C - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - - C - 6 - - 2 - 1 - 16th - down - - - - C - 1 - 5 - - 2 - 1 - 16th - sharp - down - continue - continue - - - - - C - 1 - 6 - - 2 - 1 - 16th - sharp - down - - - - D - 1 - 5 - - 2 - 1 - 16th - sharp - down - continue - continue - - - - - D - 1 - 6 - - 2 - 1 - 16th - sharp - down - - - - E - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - - E - 6 - - 2 - 1 - 16th - down - - - - F - 1 - 5 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - - F - 1 - 6 - - 2 - 1 - 16th - sharp - down - - - - G - 5 - - 2 - 1 - 16th - down - continue - continue - - - - - G - 6 - - 2 - 1 - 16th - down - - - - A - 5 - - 2 - 1 - 16th - down - continue - continue - - - - - A - 6 - - 2 - 1 - 16th - down - - - - B - -1 - 5 - - 2 - 1 - 16th - flat - down - end - end - - - - - - - - B - -1 - 6 - - 2 - 1 - 16th - flat - down - - - - A - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - - A - 6 - - 2 - 1 - 16th - down - - - - G - 5 - - 2 - 1 - 16th - down - continue - continue - - - - - G - 6 - - 2 - 1 - 16th - down - - - - F - 1 - 5 - - 2 - 1 - 16th - down - continue - continue - - - - - F - 1 - 6 - - 2 - 1 - 16th - down - - - - E - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - - E - 6 - - 2 - 1 - 16th - down - - - - - - - - - - - - - - - - a 2 - - - - - D - 1 - 5 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - - - - - - E - 5 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 5 - - 2 - 1 - 16th - sharp - down - continue - continue - - - - G - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - A - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - B - -1 - 5 - - 2 - 1 - 16th - flat - down - continue - continue - - - - C - 6 - - 2 - 1 - 16th - down - continue - continue - - - - C - 1 - 6 - - 2 - 1 - 16th - sharp - down - end - end - - - - - - - D - 6 - - 2 - 1 - 16th - down - begin - begin - - - - - - - D - 1 - 6 - - 2 - 1 - 16th - sharp - down - continue - continue - - - - E - 6 - - 2 - 1 - 16th - down - continue - continue - - - - F - 6 - - 2 - 1 - 16th - natural - down - end - end - - - - - - - - - -1 - - - - - - - - - - - - - - - - F - 1 - 6 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - G - 6 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 6 - - 2 - 1 - 16th - down - continue - continue - - - - G - 6 - - 2 - 1 - 16th - down - end - end - - - - - - - F - 1 - 6 - - 2 - 1 - 16th - down - begin - begin - - - - - - - G - 6 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 6 - - 2 - 1 - 16th - down - continue - continue - - - - G - 6 - - 2 - 1 - 16th - down - end - end - - - - - - - F - 1 - 6 - - 2 - 1 - 16th - down - begin - begin - - - - - - - G - 6 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 6 - - 2 - 1 - 16th - down - continue - continue - - - - G - 6 - - 2 - 1 - 16th - down - end - end - - - - - - - - - - - - - - 16 - 1 - - - - - - - - - - - - - - - - - - - F - 1 - 6 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - G - 6 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 6 - - 2 - 1 - 16th - down - continue - continue - - - - G - 6 - - 2 - 1 - 16th - down - end - end - - - - - - - F - 1 - 6 - - 2 - 1 - 16th - down - begin - begin - - - - - - - G - 6 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 6 - - 2 - 1 - 16th - down - continue - continue - - - - G - 6 - - 2 - 1 - 16th - down - end - end - - - - - - - 8 - 1 - quarter - - - - - - - - - 84 - - none - - - 8 - - 0 - major - - - - G - 2 - - - 108 - - - - - 24 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - - - - -

    - - - - - - - B - 5 - - 24 - - 1 - half - - down - - - - - - - - D - 6 - - 24 - - 1 - half - - down - - - - - - - - - -21 - - - - - - - - - - - B - 5 - - 16 - - 1 - half - down - - - - - - - - D - 6 - - 16 - - 1 - half - down - - - - - - - - - -1 - - - - - - - 84 - - - - - - 100 - - - - - - - - - - - - - A - 1 - 5 - - 32 - 1 - whole - sharp - - - - - C - 1 - 6 - - 32 - 1 - whole - sharp - - - - - - -30 - - - - - - -2 - - - - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - - - - - 16 - - - - - 8 - 1 - quarter - - - - 8 - 1 - quarter - - - - 1. - - - - - B - 4 - - 2 - 1 - 16th - down - begin - begin - - - - - - - - - -1 - - - - C - 5 - - 2 - 1 - 16th - down - continue - continue - - - - D - 5 - - 2 - 1 - 16th - down - continue - continue - - - - - - 1 - - - - E - -1 - 5 - - 2 - 1 - 16th - flat - down - end - end - - - - - - - - - - - - - - - - - - - F - 5 - - 2 - 1 - 16th - down - - - - 2 - 1 - 16th - - - - 4 - 1 - eighth - - - - 8 - 1 - quarter - - - - 8 - 1 - quarter - - - - - - - 159 - - - - - - - - 32 - 1 - - - - - - - - - - - A - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - - - - - - - - - - - - 1. - - - - - B - -1 - 4 - - 2 - 1 - 16th - flat - down - begin - begin - - - - - - - - - - - - C - -1 - 5 - - 2 - 1 - 16th - flat - down - continue - continue - - - - B - -1 - 4 - - 2 - 1 - 16th - down - continue - continue - - - - C - -1 - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - B - -1 - 4 - - 2 - 1 - 16th - down - begin - begin - - - - - - - C - -1 - 5 - - 2 - 1 - 16th - down - continue - continue - - - - B - -1 - 4 - - 2 - 1 - 16th - down - continue - continue - - - - A - -1 - 4 - - 2 - 1 - 16th - flat - down - end - end - - - - - - - G - 4 - - 2 - 1 - 16th - up - begin - begin - - - - - - - A - -1 - 4 - - 2 - 1 - 16th - up - continue - continue - - - - G - 4 - - 2 - 1 - 16th - up - continue - continue - - - - F - 4 - - 2 - 1 - 16th - up - end - end - - - - - - - - - -1 - - - - - - - 84 - - - - - - 108 - - - - - -

    - - - - - - - E - 4 - - 2 - 1 - 16th - up - - - - 2 - 1 - 16th - - - - 4 - 1 - eighth - - - - 8 - 1 - quarter - - - - 16 - 1 - half - - - - - - - - -

    - - - - - - - E - 5 - - 32 - 1 - whole - - - - - B - 5 - - 32 - 1 - whole - - - - - - -29 - - - - - - -1 - - - - - - - - - - - - - - - - - - - D - 1 - 5 - - 24 - 1 - half - - sharp - down - - - - - A - 1 - 5 - - 24 - 1 - half - - sharp - down - - - - - - -22 - - - - - - -1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - 84 - - - - - - - - 16 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - - - - - - - - - 1. - - - - - C - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - C - 1 - 5 - - 2 - 1 - 16th - sharp - down - continue - continue - - - - D - 1 - 5 - - 2 - 1 - 16th - sharp - down - continue - continue - - - - E - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - F - 1 - 5 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - G - 5 - - 2 - 1 - 16th - down - continue - continue - - - - A - 5 - - 2 - 1 - 16th - down - continue - continue - - - - B - -1 - 5 - - 2 - 1 - 16th - flat - down - end - end - - - - - - - A - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - G - 5 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 5 - - 2 - 1 - 16th - down - continue - continue - - - - E - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - - - - - - - - - - - - - D - 1 - 4 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - - D - 1 - 5 - - 2 - 1 - 16th - sharp - down - - - - - - - - - E - 4 - - 2 - 1 - 16th - down - continue - continue - - - - - E - 5 - - 2 - 1 - 16th - down - - - - F - 1 - 4 - - 2 - 1 - 16th - sharp - down - continue - continue - - - - - F - 1 - 5 - - 2 - 1 - 16th - sharp - down - - - - G - 4 - - 2 - 1 - 16th - down - end - end - - - - - - - - G - 5 - - 2 - 1 - 16th - down - - - - A - 4 - - 2 - 1 - 16th - down - begin - begin - - - - - - - - A - 5 - - 2 - 1 - 16th - down - - - - B - -1 - 4 - - 2 - 1 - 16th - flat - down - continue - continue - - - - - B - -1 - 5 - - 2 - 1 - 16th - flat - down - - - - C - 5 - - 2 - 1 - 16th - down - continue - continue - - - - - C - 6 - - 2 - 1 - 16th - down - - - - C - 1 - 5 - - 2 - 1 - 16th - sharp - down - end - end - - - - - - - - C - 1 - 6 - - 2 - 1 - 16th - sharp - down - - - - D - 5 - - 2 - 1 - 16th - natural - down - begin - begin - - - - - - - - D - 6 - - 2 - 1 - 16th - natural - down - - - - D - 1 - 5 - - 2 - 1 - 16th - sharp - down - continue - continue - - - - - D - 1 - 6 - - 2 - 1 - 16th - sharp - down - - - - E - 5 - - 2 - 1 - 16th - down - continue - continue - - - - - E - 6 - - 2 - 1 - 16th - down - - - - F - 5 - - 2 - 1 - 16th - natural - down - end - end - - - - - - - - F - 6 - - 2 - 1 - 16th - natural - down - - - - - - -1 - - - - - - - - - - - - - - - - F - 1 - 5 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - - F - 1 - 6 - - 2 - 1 - 16th - sharp - down - - - - G - 5 - - 2 - 1 - 16th - down - continue - continue - - - - - G - 6 - - 2 - 1 - 16th - down - - - - F - 1 - 5 - - 2 - 1 - 16th - down - continue - continue - - - - - F - 1 - 6 - - 2 - 1 - 16th - down - - - - G - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - - G - 6 - - 2 - 1 - 16th - down - - - - F - 1 - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - - F - 1 - 6 - - 2 - 1 - 16th - down - - - - G - 5 - - 2 - 1 - 16th - down - continue - continue - - - - - G - 6 - - 2 - 1 - 16th - down - - - - F - 1 - 5 - - 2 - 1 - 16th - down - continue - continue - - - - - F - 1 - 6 - - 2 - 1 - 16th - down - - - - G - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - - G - 6 - - 2 - 1 - 16th - down - - - - F - 1 - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - - F - 1 - 6 - - 2 - 1 - 16th - down - - - - G - 5 - - 2 - 1 - 16th - down - continue - continue - - - - - G - 6 - - 2 - 1 - 16th - down - - - - F - 1 - 5 - - 2 - 1 - 16th - down - continue - continue - - - - - F - 1 - 6 - - 2 - 1 - 16th - down - - - - G - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - - G - 6 - - 2 - 1 - 16th - down - - - - - - - - - - - 16 - 1 - - - - - - - - - - - - - - - - - - - F - 1 - 5 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - - F - 1 - 6 - - 2 - 1 - 16th - sharp - down - - - - G - 5 - - 2 - 1 - 16th - down - continue - continue - - - - - G - 6 - - 2 - 1 - 16th - down - - - - F - 1 - 5 - - 2 - 1 - 16th - down - continue - continue - - - - - F - 1 - 6 - - 2 - 1 - 16th - down - - - - G - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - - G - 6 - - 2 - 1 - 16th - down - - - - F - 1 - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - - F - 1 - 6 - - 2 - 1 - 16th - down - - - - G - 5 - - 2 - 1 - 16th - down - continue - continue - - - - - G - 6 - - 2 - 1 - 16th - down - - - - F - 1 - 5 - - 2 - 1 - 16th - down - continue - continue - - - - - F - 1 - 6 - - 2 - 1 - 16th - down - - - - G - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - - G - 6 - - 2 - 1 - 16th - down - - - - 8 - 1 - quarter - - - - - - - - - 88 - - none - - - 8 - - 0 - major - - - - G - 2 - - - 108 - - - -4 - -7 - - - - - 24 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - 71 - - - - - - 100 - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - -

    - - - - - - - D - -1 - 4 - - 24 - - 1 - half - - flat - up - - - - - - - - - -21 - - - - - - - - - - - D - -1 - 4 - - 16 - - 1 - half - up - - - - - - - - - -1 - - - - - - - - - - - - - - - - - - - C - 4 - - 32 - 1 - whole - - - - - - -29 - - - - - - -1 - - - - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - 88 - - - - - - 108 - - - - - 32 - 1 - - - - - - - - -

    - - - - - - - D - 5 - - 32 - 1 - whole - - - - - - -29 - - - - - - -1 - - - - - - - - - - - - - - - - - - - C - 1 - 5 - - 24 - 1 - half - - sharp - down - - - - - - -22 - - - - - - -1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - 88 - - - - - - - - 16 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - - - - - - - - - G - 4 - - 2 - 1 - 16th - up - begin - begin - - - - - - - G - 1 - 4 - - 2 - 1 - 16th - sharp - up - continue - continue - - - - A - 1 - 4 - - 2 - 1 - 16th - sharp - up - continue - continue - - - - B - 4 - - 2 - 1 - 16th - up - end - end - - - - - - - C - 1 - 5 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - D - 5 - - 2 - 1 - 16th - down - continue - continue - - - - E - 5 - - 2 - 1 - 16th - down - continue - continue - - - - F - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - E - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - D - 5 - - 2 - 1 - 16th - down - continue - continue - - - - C - 1 - 5 - - 2 - 1 - 16th - down - continue - continue - - - - B - 4 - - 2 - 1 - 16th - down - end - end - - - - - - - - - - - - - - - - - - - A - 1 - 4 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - - - - - - B - 4 - - 2 - 1 - 16th - down - continue - continue - - - - C - 1 - 5 - - 2 - 1 - 16th - sharp - down - continue - continue - - - - D - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - E - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - F - 5 - - 2 - 1 - 16th - down - continue - continue - - - - G - 5 - - 2 - 1 - 16th - down - continue - continue - - - - G - 1 - 5 - - 2 - 1 - 16th - sharp - down - end - end - - - - - - - A - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - A - 1 - 5 - - 2 - 1 - 16th - sharp - down - continue - continue - - - - B - 5 - - 2 - 1 - 16th - down - continue - continue - - - - C - 6 - - 2 - 1 - 16th - natural - down - end - end - - - - - - - - - -1 - - - - - - - - - - - - - - - - C - 1 - 6 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - D - 6 - - 2 - 1 - 16th - down - continue - continue - - - - C - 1 - 6 - - 2 - 1 - 16th - down - continue - continue - - - - D - 6 - - 2 - 1 - 16th - down - end - end - - - - - - - C - 1 - 6 - - 2 - 1 - 16th - down - begin - begin - - - - - - - D - 6 - - 2 - 1 - 16th - down - continue - continue - - - - C - 1 - 6 - - 2 - 1 - 16th - down - continue - continue - - - - D - 6 - - 2 - 1 - 16th - down - end - end - - - - - - - C - 1 - 6 - - 2 - 1 - 16th - down - begin - begin - - - - - - - D - 6 - - 2 - 1 - 16th - down - continue - continue - - - - C - 1 - 6 - - 2 - 1 - 16th - down - continue - continue - - - - D - 6 - - 2 - 1 - 16th - down - end - end - - - - - - - - - - - - - - 16 - 1 - - - - - - - - - - - - - - - - - - - C - 1 - 6 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - D - 6 - - 2 - 1 - 16th - down - continue - continue - - - - C - 1 - 6 - - 2 - 1 - 16th - down - continue - continue - - - - D - 6 - - 2 - 1 - 16th - down - end - end - - - - - - - C - 1 - 6 - - 2 - 1 - 16th - down - begin - begin - - - - - - - D - 6 - - 2 - 1 - 16th - down - continue - continue - - - - C - 1 - 6 - - 2 - 1 - 16th - down - continue - continue - - - - D - 6 - - 2 - 1 - 16th - down - end - end - - - - - - - 8 - 1 - quarter - - - - - - - - none - - - 8 - - 0 - major - - - - G - 2 - - - 108 - - - -1 - -2 - - - - - 24 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - - - - -

    - - - - - - - C - 1 - 6 - - 24 - - 1 - half - - sharp - down - - - - - - - - E - 6 - - 24 - - 1 - half - - down - - - - - - - - - -21 - - - - - - - - - - - C - 1 - 6 - - 16 - - 1 - half - down - - - - - - - - E - 6 - - 16 - - 1 - half - down - - - - - - - - - -1 - - - - - - - 83 - - - - - - 100 - - - - - - - - - - - - - C - 6 - - 32 - 1 - whole - natural - - - - - E - -1 - 6 - - 32 - 1 - whole - flat - - - - - - -30 - - - - - - -1 - - - - - - - - - - - 24 - 1 - - - - - - - - -

    - - - - - - - 1. - - - - - A - -1 - 3 - - 24 - - 1 - half - - flat - up - - - - - - - - - -21 - - - - - - - - - - - A - -1 - 3 - - 16 - - 1 - half - up - - - - - - - - - -1 - - - - - - - - - - - - - - - - - - - G - 3 - - 32 - 1 - whole - - - - - - -30 - - - - - - -1 - - - - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - 88 - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - - - - - 8 - - - - - 8 - 1 - quarter - - - - 2. - - - - - F - 1 - 3 - - 2 - 1 - 16th - sharp - up - begin - begin - - - - - - - G - 3 - - 2 - 1 - 16th - up - continue - continue - - - - F - 1 - 3 - - 2 - 1 - 16th - up - continue - continue - - - - G - 3 - - 2 - 1 - 16th - up - end - end - - - - - - - 8 - 1 - quarter - - - - - - - - F - 1 - 3 - - 2 - 1 - 16th - sharp - up - begin - begin - - - - - - - G - 3 - - 2 - 1 - 16th - up - continue - continue - - - - F - 1 - 3 - - 2 - 1 - 16th - up - continue - continue - - - - G - 3 - - 2 - 1 - 16th - up - end - end - - - - - - - F - 1 - 3 - - 2 - 1 - 16th - up - begin - begin - - - - - - - G - 3 - - 2 - 1 - 16th - up - continue - continue - - - - F - 1 - 3 - - 2 - 1 - 16th - up - continue - continue - - - - G - 3 - - 2 - 1 - 16th - up - end - end - - - - - - - F - 1 - 3 - - 2 - 1 - 16th - up - begin - begin - - - - - - - G - 3 - - 2 - 1 - 16th - up - continue - continue - - - - A - 3 - - 2 - 1 - 16th - up - continue - continue - - - - B - -1 - 3 - - 2 - 1 - 16th - flat - up - end - end - - - - - - - - - - - F - 1 - 3 - - 2 - 1 - 16th - sharp - up - begin - begin - - - - - - - G - 3 - - 2 - 1 - 16th - up - continue - continue - - - - F - 1 - 3 - - 2 - 1 - 16th - up - continue - continue - - - - G - 3 - - 2 - 1 - 16th - up - end - end - - - - - - - F - 1 - 3 - - 2 - 1 - 16th - up - begin - begin - - - - - - - G - 3 - - 2 - 1 - 16th - up - continue - continue - - - - A - 3 - - 2 - 1 - 16th - up - continue - continue - - - - B - -1 - 3 - - 2 - 1 - 16th - flat - up - end - end - - - - - - - F - 1 - 3 - - 2 - 1 - 16th - up - begin - begin - - - - - - - G - 3 - - 2 - 1 - 16th - up - continue - continue - - - - A - 3 - - 2 - 1 - 16th - up - continue - continue - - - - B - -1 - 3 - - 2 - 1 - 16th - up - end - end - - - - - - - - - - - - - - F - 1 - 3 - - 2 - 1 - 16th - sharp - up - begin - begin - - - - - - - G - 3 - - 2 - 1 - 16th - up - continue - continue - - - - F - 1 - 3 - - 2 - 1 - 16th - up - continue - continue - - - - G - 3 - - 2 - 1 - 16th - up - end - end - - - - - - - F - 1 - 3 - - 2 - 1 - 16th - up - begin - begin - - - - - - - G - 3 - - 2 - 1 - 16th - up - continue - continue - - - - F - 1 - 3 - - 2 - 1 - 16th - up - continue - continue - - - - G - 3 - - 2 - 1 - 16th - up - end - end - - - - - - - - - - - - - - - - - - - - - - F - 1 - 3 - - 2 - 1 - 16th - sharp - up - begin - begin - - - - - - - - F - 1 - 4 - - 2 - 1 - 16th - sharp - up - - - - - - 1 - - - - G - 3 - - 2 - 1 - 16th - up - continue - continue - - - - - G - 4 - - 2 - 1 - 16th - up - - - - F - 1 - 3 - - 2 - 1 - 16th - up - continue - continue - - - - - F - 1 - 4 - - 2 - 1 - 16th - up - - - - G - 3 - - 2 - 1 - 16th - up - end - end - - - - - - - - G - 4 - - 2 - 1 - 16th - up - - - - F - 1 - 3 - - 2 - 1 - 16th - up - begin - begin - - - - - - - - F - 1 - 4 - - 2 - 1 - 16th - up - - - - G - 3 - - 2 - 1 - 16th - up - continue - continue - - - - - G - 4 - - 2 - 1 - 16th - up - - - - A - 3 - - 2 - 1 - 16th - up - continue - continue - - - - - A - 4 - - 2 - 1 - 16th - up - - - - B - -1 - 3 - - 2 - 1 - 16th - flat - up - end - end - - - - - - - - B - -1 - 4 - - 2 - 1 - 16th - flat - up - - - - F - 1 - 3 - - 2 - 1 - 16th - up - begin - begin - - - - - - - - F - 1 - 4 - - 2 - 1 - 16th - up - - - - G - 3 - - 2 - 1 - 16th - up - continue - continue - - - - - G - 4 - - 2 - 1 - 16th - up - - - - F - 1 - 3 - - 2 - 1 - 16th - up - continue - continue - - - - - F - 1 - 4 - - 2 - 1 - 16th - up - - - - G - 3 - - 2 - 1 - 16th - up - end - end - - - - - - - - G - 4 - - 2 - 1 - 16th - up - - - - F - 1 - 3 - - 2 - 1 - 16th - up - begin - begin - - - - - - - - F - 1 - 4 - - 2 - 1 - 16th - up - - - - G - 3 - - 2 - 1 - 16th - up - continue - continue - - - - - G - 4 - - 2 - 1 - 16th - up - - - - A - 3 - - 2 - 1 - 16th - up - continue - continue - - - - - A - 4 - - 2 - 1 - 16th - up - - - - B - -1 - 3 - - 2 - 1 - 16th - up - end - end - - - - - - - - B - -1 - 4 - - 2 - 1 - 16th - up - - - - - - -1 - - - - - - - - - - - - - - - - - - - C - 4 - - 2 - 1 - 16th - up - begin - begin - - - - - - - - C - 5 - - 2 - 1 - 16th - up - - - - - - - - - D - -1 - 4 - - 2 - 1 - 16th - flat - up - continue - continue - - - - - D - -1 - 5 - - 2 - 1 - 16th - flat - up - - - - C - 4 - - 2 - 1 - 16th - up - continue - continue - - - - - C - 5 - - 2 - 1 - 16th - up - - - - D - -1 - 4 - - 2 - 1 - 16th - up - end - end - - - - - - - - D - -1 - 5 - - 2 - 1 - 16th - up - - - - C - 4 - - 2 - 1 - 16th - up - begin - begin - - - - - - - - C - 5 - - 2 - 1 - 16th - up - - - - D - -1 - 4 - - 2 - 1 - 16th - up - continue - continue - - - - - D - -1 - 5 - - 2 - 1 - 16th - up - - - - C - 4 - - 2 - 1 - 16th - up - continue - continue - - - - - C - 5 - - 2 - 1 - 16th - up - - - - B - -1 - 3 - - 2 - 1 - 16th - flat - up - end - end - - - - - - - - B - -1 - 4 - - 2 - 1 - 16th - flat - up - - - - A - 3 - - 2 - 1 - 16th - up - begin - begin - - - - - - - - A - 4 - - 2 - 1 - 16th - up - - - - B - -1 - 3 - - 2 - 1 - 16th - up - continue - continue - - - - - B - -1 - 4 - - 2 - 1 - 16th - up - - - - A - 3 - - 2 - 1 - 16th - up - continue - continue - - - - - A - 4 - - 2 - 1 - 16th - up - - - - G - 3 - - 2 - 1 - 16th - up - end - end - - - - - - - - G - 4 - - 2 - 1 - 16th - up - - - - - - -1 - - - - - - - - - 108 - - - - - -

    - - - - - - - F - 1 - 3 - - 2 - 1 - 16th - sharp - up - - - - - F - 1 - 4 - - 2 - 1 - 16th - sharp - up - - - - 2 - 1 - 16th - - - - 4 - 1 - eighth - - - - 8 - 1 - quarter - - - - 16 - 1 - half - - - - - - - - -

    - - - - - - - C - 1 - 5 - - 32 - 1 - whole - sharp - - - - - D - 5 - - 32 - 1 - whole - - - - - - -29 - - - - - - -1 - - - - - - - - - - - - - - - - - - - C - 5 - - 24 - 1 - half - - natural - down - - - - - D - -1 - 5 - - 24 - 1 - half - - flat - down - - - - - - -22 - - - - - - -1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - -

    - - - - - - - 1. - - - - - G - 1 - 4 - - 2 - 1 - 16th - sharp - up - begin - begin - - - - - - - A - 4 - - 2 - 1 - 16th - up - continue - continue - - - - B - 4 - - 2 - 1 - 16th - up - continue - continue - - - - C - 5 - - 2 - 1 - 16th - natural - up - end - end - - - - - - - G - 1 - 4 - - 2 - 1 - 16th - up - begin - begin - - - - - - - A - 4 - - 2 - 1 - 16th - up - continue - continue - - - - B - 4 - - 2 - 1 - 16th - up - continue - continue - - - - C - 5 - - 2 - 1 - 16th - up - end - end - - - - - - - D - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - E - -1 - 5 - - 2 - 1 - 16th - flat - down - continue - continue - - - - F - 5 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 5 - - 2 - 1 - 16th - sharp - down - end - end - - - - - - - - - - - - - - 1. - - - - - G - 5 - - 2 - 1 - 16th - natural - down - begin - begin - - - - - - - A - -1 - 5 - - 2 - 1 - 16th - flat - down - continue - continue - - - - G - 5 - - 2 - 1 - 16th - down - continue - continue - - - - F - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - E - 5 - - 2 - 1 - 16th - natural - down - begin - begin - - - - - - - D - 5 - - 2 - 1 - 16th - down - continue - continue - - - - C - 1 - 5 - - 2 - 1 - 16th - sharp - down - continue - continue - - - - B - 4 - - 2 - 1 - 16th - down - end - end - - - - - - - - - - - - - - - - - - - - - - B - 3 - - 2 - 1 - 16th - up - begin - begin - - - - - - - - B - 4 - - 2 - 1 - 16th - up - - - - C - 4 - - 2 - 1 - 16th - natural - up - continue - continue - - - - - C - 5 - - 2 - 1 - 16th - natural - up - - - - D - 4 - - 2 - 1 - 16th - up - continue - continue - - - - - D - 5 - - 2 - 1 - 16th - up - - - - E - -1 - 4 - - 2 - 1 - 16th - flat - up - end - end - - - - - - - - E - -1 - 5 - - 2 - 1 - 16th - flat - up - - - - B - 3 - - 2 - 1 - 16th - up - begin - begin - - - - - - - - B - 4 - - 2 - 1 - 16th - up - - - - C - 4 - - 2 - 1 - 16th - up - continue - continue - - - - - C - 5 - - 2 - 1 - 16th - up - - - - D - 4 - - 2 - 1 - 16th - up - continue - continue - - - - - D - 5 - - 2 - 1 - 16th - up - - - - E - -1 - 4 - - 2 - 1 - 16th - up - end - end - - - - - - - - E - -1 - 5 - - 2 - 1 - 16th - up - - - - F - 4 - - 2 - 1 - 16th - down - begin - begin - - - - - - - - F - 5 - - 2 - 1 - 16th - down - - - - F - 1 - 4 - - 2 - 1 - 16th - sharp - down - continue - continue - - - - - F - 1 - 5 - - 2 - 1 - 16th - sharp - down - - - - G - 1 - 4 - - 2 - 1 - 16th - sharp - down - continue - continue - - - - - G - 1 - 5 - - 2 - 1 - 16th - sharp - down - - - - A - 4 - - 2 - 1 - 16th - down - end - end - - - - - - - - A - 5 - - 2 - 1 - 16th - down - - - - - - - - - - - A - 1 - 4 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - - A - 1 - 5 - - 2 - 1 - 16th - sharp - down - - - - B - 4 - - 2 - 1 - 16th - down - continue - continue - - - - - B - 5 - - 2 - 1 - 16th - down - - - - A - 1 - 4 - - 2 - 1 - 16th - down - continue - continue - - - - - A - 1 - 5 - - 2 - 1 - 16th - down - - - - G - 1 - 4 - - 2 - 1 - 16th - sharp - down - end - end - - - - - - - - G - 1 - 5 - - 2 - 1 - 16th - sharp - down - - - - G - 4 - - 2 - 1 - 16th - natural - down - begin - begin - - - - - - - - G - 5 - - 2 - 1 - 16th - natural - down - - - - F - 4 - - 2 - 1 - 16th - down - continue - continue - - - - - F - 5 - - 2 - 1 - 16th - down - - - - E - 4 - - 2 - 1 - 16th - down - continue - continue - - - - - E - 5 - - 2 - 1 - 16th - down - - - - D - 4 - - 2 - 1 - 16th - down - end - end - - - - - - - - D - 5 - - 2 - 1 - 16th - down - - - - - - - - - - - - - - - - - - - D - 4 - - 2 - 1 - 16th - up - begin - begin - - - - - - - - D - 5 - - 2 - 1 - 16th - up - - - - E - -1 - 4 - - 2 - 1 - 16th - flat - up - continue - continue - - - - - E - -1 - 5 - - 2 - 1 - 16th - flat - up - - - - F - 4 - - 2 - 1 - 16th - up - continue - continue - - - - - F - 5 - - 2 - 1 - 16th - up - - - - F - 1 - 4 - - 2 - 1 - 16th - sharp - up - end - end - - - - - - - - F - 1 - 5 - - 2 - 1 - 16th - sharp - up - - - - G - 1 - 4 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - - G - 1 - 5 - - 2 - 1 - 16th - sharp - down - - - - A - 4 - - 2 - 1 - 16th - down - continue - continue - - - - - A - 5 - - 2 - 1 - 16th - down - - - - B - 4 - - 2 - 1 - 16th - down - continue - continue - - - - - B - 5 - - 2 - 1 - 16th - down - - - - C - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - - C - 6 - - 2 - 1 - 16th - down - - - - B - 4 - - 2 - 1 - 16th - down - begin - begin - - - - - - - - B - 5 - - 2 - 1 - 16th - down - - - - A - 4 - - 2 - 1 - 16th - down - continue - continue - - - - - A - 5 - - 2 - 1 - 16th - down - - - - G - 1 - 4 - - 2 - 1 - 16th - down - continue - continue - - - - - G - 1 - 5 - - 2 - 1 - 16th - down - - - - F - 1 - 4 - - 2 - 1 - 16th - down - end - end - - - - - - - - F - 1 - 5 - - 2 - 1 - 16th - down - - - - - - - - - - - - - - - - F - 4 - - 2 - 1 - 16th - natural - down - begin - begin - - - - - - - - F - 5 - - 2 - 1 - 16th - natural - down - - - - - - - - - F - 1 - 4 - - 2 - 1 - 16th - sharp - down - continue - continue - - - - - F - 1 - 5 - - 2 - 1 - 16th - sharp - down - - - - G - 1 - 4 - - 2 - 1 - 16th - sharp - down - continue - continue - - - - - G - 1 - 5 - - 2 - 1 - 16th - sharp - down - - - - A - 4 - - 2 - 1 - 16th - down - end - end - - - - - - - - A - 5 - - 2 - 1 - 16th - down - - - - B - 4 - - 2 - 1 - 16th - down - begin - begin - - - - - - - - B - 5 - - 2 - 1 - 16th - down - - - - C - 5 - - 2 - 1 - 16th - down - continue - continue - - - - - C - 6 - - 2 - 1 - 16th - down - - - - D - 5 - - 2 - 1 - 16th - down - continue - continue - - - - - D - 6 - - 2 - 1 - 16th - down - - - - D - 1 - 5 - - 2 - 1 - 16th - sharp - down - end - end - - - - - - - - D - 1 - 6 - - 2 - 1 - 16th - sharp - down - - - - E - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - - E - 6 - - 2 - 1 - 16th - natural - down - - - - E - 1 - 5 - - 2 - 1 - 16th - sharp - down - continue - continue - - - - - E - 1 - 6 - - 2 - 1 - 16th - sharp - down - - - - F - 1 - 5 - - 2 - 1 - 16th - down - continue - continue - - - - - F - 1 - 6 - - 2 - 1 - 16th - sharp - down - - - - G - 5 - - 2 - 1 - 16th - natural - down - end - end - - - - - - - - G - 6 - - 2 - 1 - 16th - natural - down - - - - - - -1 - - - - - - - - - - - - - - - - G - 1 - 5 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - - G - 1 - 6 - - 2 - 1 - 16th - sharp - down - - - - A - 5 - - 2 - 1 - 16th - down - continue - continue - - - - - A - 6 - - 2 - 1 - 16th - down - - - - G - 1 - 5 - - 2 - 1 - 16th - down - continue - continue - - - - - G - 1 - 6 - - 2 - 1 - 16th - down - - - - A - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - - A - 6 - - 2 - 1 - 16th - down - - - - G - 1 - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - - G - 1 - 6 - - 2 - 1 - 16th - down - - - - A - 5 - - 2 - 1 - 16th - down - continue - continue - - - - - A - 6 - - 2 - 1 - 16th - down - - - - G - 1 - 5 - - 2 - 1 - 16th - down - continue - continue - - - - - G - 1 - 6 - - 2 - 1 - 16th - down - - - - A - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - - A - 6 - - 2 - 1 - 16th - down - - - - G - 1 - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - - G - 1 - 6 - - 2 - 1 - 16th - down - - - - A - 5 - - 2 - 1 - 16th - down - continue - continue - - - - - A - 6 - - 2 - 1 - 16th - down - - - - G - 1 - 5 - - 2 - 1 - 16th - down - continue - continue - - - - - G - 1 - 6 - - 2 - 1 - 16th - down - - - - A - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - - A - 6 - - 2 - 1 - 16th - down - - - - - - - - - - - 16 - 1 - - - - - - - - - - - - - - - - - - - G - 1 - 5 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - - G - 1 - 6 - - 2 - 1 - 16th - sharp - down - - - - A - 5 - - 2 - 1 - 16th - down - continue - continue - - - - - A - 6 - - 2 - 1 - 16th - down - - - - G - 1 - 5 - - 2 - 1 - 16th - down - continue - continue - - - - - G - 1 - 6 - - 2 - 1 - 16th - down - - - - A - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - - A - 6 - - 2 - 1 - 16th - down - - - - G - 1 - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - - G - 1 - 6 - - 2 - 1 - 16th - down - - - - A - 5 - - 2 - 1 - 16th - down - continue - continue - - - - - A - 6 - - 2 - 1 - 16th - down - - - - G - 1 - 5 - - 2 - 1 - 16th - down - continue - continue - - - - - G - 1 - 6 - - 2 - 1 - 16th - down - - - - A - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - - A - 6 - - 2 - 1 - 16th - down - - - - 8 - 1 - quarter - - - - - - - - - 89 - - none - - - 8 - - 0 - major - - - - G - 2 - - - 108 - - - -1 - -2 - -1 - - - - - 24 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - 81 - - - - - - 100 - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - -

    - - - - - - - A - -1 - 3 - - 24 - - 1 - half - - flat - up - - - - - - - - - -21 - - - - - - - - - - - A - -1 - 3 - - 16 - - 1 - half - up - - - - - - - - - -1 - - - - - - - - - - - - - - - - - - - G - 3 - - 32 - 1 - whole - - - - - - -29 - - - - - - -2 - - - - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - 89 - - - - - - - - 32 - 1 - - - - - - - - - - - - - - - 8 - - - - - 8 - 1 - quarter - - - - solo - - - - - F - 1 - 3 - - 2 - 1 - 16th - sharp - up - begin - begin - - - - - - - G - 3 - - 2 - 1 - 16th - up - continue - continue - - - - F - 1 - 3 - - 2 - 1 - 16th - up - continue - continue - - - - G - 3 - - 2 - 1 - 16th - up - end - end - - - - - - - 8 - 1 - quarter - - - - - - - - 8 - 1 - quarter - - - - F - 1 - 3 - - 2 - 1 - 16th - sharp - up - begin - begin - - - - - - - G - 3 - - 2 - 1 - 16th - up - continue - continue - - - - F - 1 - 3 - - 2 - 1 - 16th - up - continue - continue - - - - G - 3 - - 2 - 1 - 16th - up - end - end - - - - - - - 8 - 1 - quarter - - - - - - - - F - 1 - 3 - - 2 - 1 - 16th - sharp - up - begin - begin - - - - - - - G - 3 - - 2 - 1 - 16th - up - continue - continue - - - - F - 1 - 3 - - 2 - 1 - 16th - up - continue - continue - - - - G - 3 - - 2 - 1 - 16th - up - end - end - - - - - - - F - 1 - 3 - - 2 - 1 - 16th - up - begin - begin - - - - - - - G - 3 - - 2 - 1 - 16th - up - continue - continue - - - - F - 1 - 3 - - 2 - 1 - 16th - up - continue - continue - - - - G - 3 - - 2 - 1 - 16th - up - end - end - - - - - - - F - 1 - 3 - - 2 - 1 - 16th - up - begin - begin - - - - - - - G - 3 - - 2 - 1 - 16th - up - continue - continue - - - - A - 3 - - 2 - 1 - 16th - up - continue - continue - - - - B - -1 - 3 - - 2 - 1 - 16th - flat - up - end - end - - - - - - - - - - - F - 1 - 3 - - 2 - 1 - 16th - sharp - up - begin - begin - - - - - - - G - 3 - - 2 - 1 - 16th - up - continue - continue - - - - F - 1 - 3 - - 2 - 1 - 16th - up - continue - continue - - - - G - 3 - - 2 - 1 - 16th - up - end - end - - - - - - - F - 1 - 3 - - 2 - 1 - 16th - up - begin - begin - - - - - - - G - 3 - - 2 - 1 - 16th - up - continue - continue - - - - A - 3 - - 2 - 1 - 16th - up - continue - continue - - - - B - -1 - 3 - - 2 - 1 - 16th - flat - up - end - end - - - - - - - F - 1 - 3 - - 2 - 1 - 16th - up - begin - begin - - - - - - - G - 3 - - 2 - 1 - 16th - up - continue - continue - - - - A - 3 - - 2 - 1 - 16th - up - continue - continue - - - - B - -1 - 3 - - 2 - 1 - 16th - up - end - end - - - - - - - - - - - - - - F - 1 - 3 - - 2 - 1 - 16th - sharp - up - begin - begin - - - - - - - G - 3 - - 2 - 1 - 16th - up - continue - continue - - - - F - 1 - 3 - - 2 - 1 - 16th - up - continue - continue - - - - G - 3 - - 2 - 1 - 16th - up - end - end - - - - - - - F - 1 - 3 - - 2 - 1 - 16th - up - begin - begin - - - - - - - G - 3 - - 2 - 1 - 16th - up - continue - continue - - - - F - 1 - 3 - - 2 - 1 - 16th - up - continue - continue - - - - G - 3 - - 2 - 1 - 16th - up - end - end - - - - - - - - - - - - - - - - 1 - - - - F - 1 - 3 - - 2 - 1 - 16th - sharp - up - begin - begin - - - - - - - G - 3 - - 2 - 1 - 16th - up - continue - continue - - - - F - 1 - 3 - - 2 - 1 - 16th - up - continue - continue - - - - G - 3 - - 2 - 1 - 16th - up - end - end - - - - - - - F - 1 - 3 - - 2 - 1 - 16th - up - begin - begin - - - - - - - G - 3 - - 2 - 1 - 16th - up - continue - continue - - - - A - 3 - - 2 - 1 - 16th - up - continue - continue - - - - B - -1 - 3 - - 2 - 1 - 16th - flat - up - end - end - - - - - - - F - 1 - 3 - - 2 - 1 - 16th - up - begin - begin - - - - - - - G - 3 - - 2 - 1 - 16th - up - continue - continue - - - - F - 1 - 3 - - 2 - 1 - 16th - up - continue - continue - - - - G - 3 - - 2 - 1 - 16th - up - end - end - - - - - - - F - 1 - 3 - - 2 - 1 - 16th - up - begin - begin - - - - - - - G - 3 - - 2 - 1 - 16th - up - continue - continue - - - - A - 3 - - 2 - 1 - 16th - up - continue - continue - - - - B - -1 - 3 - - 2 - 1 - 16th - up - end - end - - - - - - - - - -1 - - - - - - - - - - - - - - - - - - - C - 4 - - 2 - 1 - 16th - up - begin - begin - - - - - - - - - - - - D - -1 - 4 - - 2 - 1 - 16th - flat - up - continue - continue - - - - C - 4 - - 2 - 1 - 16th - up - continue - continue - - - - D - -1 - 4 - - 2 - 1 - 16th - up - end - end - - - - - - - C - 4 - - 2 - 1 - 16th - up - begin - begin - - - - - - - D - -1 - 4 - - 2 - 1 - 16th - up - continue - continue - - - - C - 4 - - 2 - 1 - 16th - up - continue - continue - - - - B - -1 - 3 - - 2 - 1 - 16th - flat - up - end - end - - - - - - - A - 3 - - 2 - 1 - 16th - up - begin - begin - - - - - - - B - -1 - 3 - - 2 - 1 - 16th - up - continue - continue - - - - A - 3 - - 2 - 1 - 16th - up - continue - continue - - - - - - 1 - - - - G - 3 - - 2 - 1 - 16th - up - end - end - - - - - - - - - - 89 - - - - - - 108 - - - - - -

    - - - - - - - F - 1 - 3 - - 2 - 1 - 16th - sharp - up - - - - 2 - 1 - 16th - - - - 4 - 1 - eighth - - - - 8 - 1 - quarter - - - - 16 - 1 - half - - - - - - - - -

    - - - - - - - F - 1 - 5 - - 32 - 1 - whole - - - - - - -29 - - - - - - -1 - - - - - - - - - - - - - - - - - - - E - 1 - 5 - - 24 - 1 - half - - sharp - down - - - - - - -22 - - - - - - -1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - 89 - - - - - - - - 16 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - - - - - - - - - D - 4 - - 2 - 1 - 16th - up - begin - begin - - - - - - - E - -1 - 4 - - 2 - 1 - 16th - flat - up - continue - continue - - - - F - 4 - - 2 - 1 - 16th - up - continue - continue - - - - F - 1 - 4 - - 2 - 1 - 16th - sharp - up - end - end - - - - - - - G - 1 - 4 - - 2 - 1 - 16th - sharp - up - begin - begin - - - - - - - A - 4 - - 2 - 1 - 16th - up - continue - continue - - - - B - 4 - - 2 - 1 - 16th - up - continue - continue - - - - C - 5 - - 2 - 1 - 16th - up - end - end - - - - - - - B - 4 - - 2 - 1 - 16th - up - begin - begin - - - - - - - A - 4 - - 2 - 1 - 16th - up - continue - continue - - - - G - 1 - 4 - - 2 - 1 - 16th - up - continue - continue - - - - F - 1 - 4 - - 2 - 1 - 16th - up - end - end - - - - - - - - - - - - - - - - - - - F - 4 - - 2 - 1 - 16th - natural - up - begin - begin - - - - - - - - - - - - F - 1 - 4 - - 2 - 1 - 16th - sharp - up - continue - continue - - - - G - 1 - 4 - - 2 - 1 - 16th - sharp - up - continue - continue - - - - A - 4 - - 2 - 1 - 16th - up - end - end - - - - - - - B - 4 - - 2 - 1 - 16th - down - begin - begin - - - - - - - C - 5 - - 2 - 1 - 16th - down - continue - continue - - - - D - 5 - - 2 - 1 - 16th - down - continue - continue - - - - D - 1 - 5 - - 2 - 1 - 16th - sharp - down - end - end - - - - - - - E - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - F - 5 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 5 - - 2 - 1 - 16th - sharp - down - continue - continue - - - - G - 5 - - 2 - 1 - 16th - natural - down - end - end - - - - - - - - - -1 - - - - - - - - - - - - - - - - G - 1 - 5 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - A - 5 - - 2 - 1 - 16th - down - continue - continue - - - - G - 1 - 5 - - 2 - 1 - 16th - down - continue - continue - - - - A - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - G - 1 - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - A - 5 - - 2 - 1 - 16th - down - continue - continue - - - - G - 1 - 5 - - 2 - 1 - 16th - down - continue - continue - - - - A - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - G - 1 - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - A - 5 - - 2 - 1 - 16th - down - continue - continue - - - - G - 1 - 5 - - 2 - 1 - 16th - down - continue - continue - - - - A - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - - - - - - - - 16 - 1 - - - - - - - - - - - - - - - - - - - G - 1 - 5 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - A - 5 - - 2 - 1 - 16th - down - continue - continue - - - - G - 1 - 5 - - 2 - 1 - 16th - down - continue - continue - - - - A - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - G - 1 - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - A - 5 - - 2 - 1 - 16th - down - continue - continue - - - - G - 1 - 5 - - 2 - 1 - 16th - down - continue - continue - - - - A - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - 8 - 1 - quarter - - - - - - - - none - - - 8 - - 0 - major - - - - F - 4 - - - 108 - - - - - 24 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - 97 - - - - - - 100 - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - -

    - - - - - - - B - 2 - - 24 - - 1 - half - - up - - - - - - - - D - 3 - - 24 - - 1 - half - - up - - - - - - - - - -21 - - - - - - - - - - - B - 2 - - 16 - - 1 - half - up - - - - - - - - D - 3 - - 16 - - 1 - half - up - - - - - - - - - -1 - - - - - - - - - - - - - - - - - - - A - 1 - 2 - - 32 - 1 - whole - sharp - - - - - C - 1 - 3 - - 32 - 1 - whole - sharp - - - - - - -30 - - - - - - -1 - - - - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - - - - - 8 - - - - - 8 - 1 - quarter - - - - B - 1 - - 2 - 1 - 16th - up - begin - begin - - - - - - - - B - 2 - - 2 - 1 - 16th - up - - - - - - 1 - - - - C - 2 - - 2 - 1 - 16th - up - continue - continue - - - - - C - 3 - - 2 - 1 - 16th - up - - - - D - 2 - - 2 - 1 - 16th - up - continue - continue - - - - - D - 3 - - 2 - 1 - 16th - up - - - - E - -1 - 2 - - 2 - 1 - 16th - flat - up - end - end - - - - - - - - E - -1 - 3 - - 2 - 1 - 16th - flat - up - - - - B - 1 - - 2 - 1 - 16th - up - begin - begin - - - - - - - - B - 2 - - 2 - 1 - 16th - up - - - - C - 2 - - 2 - 1 - 16th - up - continue - continue - - - - - C - 3 - - 2 - 1 - 16th - up - - - - D - 2 - - 2 - 1 - 16th - up - continue - continue - - - - - D - 3 - - 2 - 1 - 16th - up - - - - E - -1 - 2 - - 2 - 1 - 16th - up - end - end - - - - - - - - E - -1 - 3 - - 2 - 1 - 16th - up - - - - - - - - - - - - - - - - - - - - - F - 2 - - 24 - 1 - half - - up - - - - - - - - - - F - 3 - - 24 - 1 - half - - up - - - - - - - - - - - a 2 - - - - - - - 9 - - - - E - 2 - - 32 - 1 - whole - natural - - - - - - - - - - - -1 - - - - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - -

    - - - - - - - a 2 - - - - - E - 3 - - 32 - 1 - whole - - - - - - -29 - - - - - - -1 - - - - - - - - - - - - - - - - - - - E - 2 - - 24 - 1 - half - - up - - - - - - -22 - - - - - - -1 - - - - - - - - - 108 - - - - - 32 - 1 - - - - - - - - -

    - - - - - - - B - 3 - - 32 - 1 - whole - - - - - C - 4 - - 32 - 1 - whole - - - - - - -29 - - - - - - -1 - - - - - - - - - - - - - - - - - - - A - 1 - 3 - - 24 - 1 - half - - sharp - down - - - - - B - 3 - - 24 - 1 - half - - down - - - - - - -22 - - - - - - -1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - - - - - - - - - C - 2 - - 16 - 1 - half - up - - - - - - - - C - 3 - - 16 - 1 - half - up - - - - B - 1 - - 8 - 1 - quarter - up - - - - - - - - B - 2 - - 8 - 1 - quarter - up - - - - - - - - - - - - - - - - E - -1 - 2 - - 16 - 1 - half - flat - up - - - - - - - - E - -1 - 3 - - 16 - 1 - half - flat - up - - - - - - -14 - - - - D - 2 - - 8 - 1 - quarter - up - - - - - - - - D - 3 - - 8 - 1 - quarter - up - - - - - - -1 - - - - - - - - - - - - - - - - F - 1 - 2 - - 24 - - 1 - half - - sharp - up - - - - - - - - F - 1 - 3 - - 24 - - 1 - half - - sharp - up - - - - - - - - - - - - - - F - 1 - 2 - - 16 - - - 1 - half - up - - - - - - - - - F - 1 - 3 - - 16 - - - 1 - half - up - - - - - - - - - - - - - - - F - 1 - 2 - - 24 - - 1 - half - - up - - - - - - - - F - 1 - 3 - - 24 - - 1 - half - - up - - - - - - - - - - - - 112 - - none - - - 8 - - 0 - major - - - - G - 2 - - - 108 - - - -4 - -7 - - - - - 24 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - 85 - - - 1 -2 - - - - - - 100 - - - - - 32 - 1 - - - - - - - 1 -2 - - - - - - - - 24 - 1 - - - - - - - - -

    - - - - - - - F - 1 - 3 - - 24 - - 1 - half - - sharp - up - - - - - - - - A - 3 - - 24 - - 1 - half - - up - - - - - - - - - -20 - - - - - - - - - - - F - 1 - 3 - - 16 - - 1 - half - up - - - - - - - - A - 3 - - 16 - - 1 - half - up - - - - - - - - - -1 - - - - - - - - - - - - - - - - - - - F - 3 - - 32 - 1 - whole - natural - - - - - A - -1 - 3 - - 32 - 1 - whole - flat - - - - - - -29 - - - - - - -1 - - - - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - 98 - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - -

    - - - - - - - D - 5 - - 32 - 1 - whole - - - - - G - 5 - - 32 - 1 - whole - - - - - - -29 - - - - - - -1 - - - - - - - - - - - - - - - - - - - C - 1 - 5 - - 24 - 1 - half - - sharp - down - - - - - F - 1 - 5 - - 24 - 1 - half - - sharp - down - - - - - - -22 - - - - - - -1 - - - - - - - 103 - - - - - - 108 - - - - - 32 - 1 - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - 112 - - - - - - - - 16 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - - - - - - - - - C - 5 - - 16 - 1 - half - down - - - - - - - - G - 5 - - 16 - 1 - half - down - - - - B - 4 - - 8 - 1 - quarter - down - - - - - - - - F - 1 - 5 - - 8 - 1 - quarter - sharp - down - - - - - - - - - - - - - - - - B - -1 - 4 - - 16 - 1 - half - flat - down - - - - - - - - E - -1 - 5 - - 16 - 1 - half - flat - down - - - - - - -14 - - - - A - 4 - - 8 - 1 - quarter - down - - - - - - - - D - 5 - - 8 - 1 - quarter - down - - - - - - -1 - - - - - - - - - - - - - - - - A - 4 - - 24 - - 1 - half - - down - - - - - - - - F - 1 - 5 - - 24 - - 1 - half - - sharp - down - - - - - - - - - - - - - - A - 4 - - 16 - - - 1 - half - down - - - - - - - - - F - 1 - 5 - - 16 - - - 1 - half - down - - - - - - - - - - - - - - - A - 4 - - 24 - - 1 - half - - down - - - - - - - - F - 1 - 5 - - 24 - - 1 - half - - down - - - - - - - - - - - none - - - 8 - - 0 - major - - - - G - 2 - - - 108 - - - -4 - -7 - - - - - 24 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - 100 - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - 74 - - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - -

    - - - - - - - B - 4 - - 32 - 1 - whole - - - - - F - 1 - 5 - - 32 - 1 - whole - sharp - - - - - - -29 - - - - - - -1 - - - - - - - - - - - - - - - - - - - B - -1 - 4 - - 24 - 1 - half - - flat - down - - - - - F - 5 - - 24 - 1 - half - - natural - down - - - - - - -22 - - - - - - -1 - - - - - - - 98 - - - - - - 108 - - - - - 32 - 1 - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - - - - - - - - - G - 4 - - 16 - 1 - half - down - - - - - - - - E - -1 - 5 - - 16 - 1 - half - flat - down - - - - F - 1 - 4 - - 8 - 1 - quarter - sharp - up - - - - - - - - D - 5 - - 8 - 1 - quarter - up - - - - - - - - - - - - - - - - F - 1 - 4 - - 16 - 1 - half - sharp - up - - - - - - - - B - 4 - - 16 - 1 - half - up - - - - - - -14 - - - - F - 4 - - 8 - 1 - quarter - natural - up - - - - - - - - B - -1 - 4 - - 8 - 1 - quarter - flat - up - - - - - - -1 - - - - - - - - - - - - - - - - D - 4 - - 24 - - 1 - half - - up - - - - - - - - D - 5 - - 24 - - 1 - half - - up - - - - - - - - - - - - - - D - 4 - - 16 - - - 1 - half - up - - - - - - - - - D - 5 - - 16 - - - 1 - half - up - - - - - - - - - - - - - - - D - 4 - - 24 - - 1 - half - - up - - - - - - - - D - 5 - - 24 - - 1 - half - - up - - - - - - - - - - - - 108 - - none - - - 8 - - 0 - major - - - - G - 2 - - - 108 - - - - - 24 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - 100 - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - 103 - - - - - - 108 - - - - - 32 - 1 - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - 108 - - - - - - - - 16 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - - - - - - - - - B - 4 - - 16 - 1 - half - down - - - - - - - - E - 5 - - 16 - 1 - half - down - - - - - - -14 - - - - B - -1 - 4 - - 8 - 1 - quarter - flat - down - - - - - - - - E - -1 - 5 - - 8 - 1 - quarter - flat - down - - - - - - -1 - - - - - - - - - - - - - - - - D - 5 - - 24 - - 1 - half - - down - - - - - - - - G - 5 - - 24 - - 1 - half - - down - - - - - - - - - - - - - - D - 5 - - 16 - - - 1 - half - down - - - - - - - - - G - 5 - - 16 - - - 1 - half - down - - - - - - - - - - - - - - - D - 5 - - 24 - - 1 - half - - down - - - - - - - - G - 5 - - 24 - - 1 - half - - down - - - - - - - - - - - - 78 - - none - - - 8 - - 0 - major - - - - F - 4 - - - 108 - - - - - 24 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - 100 - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - 91 - - - 1 -2 - - - - - - - - - 32 - 1 - - - - - - - 1 -2 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - -

    - - - - - - - B - 3 - - 32 - 1 - whole - - - - - C - 4 - - 32 - 1 - whole - - - - - - -30 - - - - - - -1 - - - - - - - - - - - - - - - - - - - A - 1 - 3 - - 24 - 1 - half - - sharp - down - - - - - B - 3 - - 24 - 1 - half - - down - - - - - - -22 - - - - - - -1 - - - - - - - 92 - - - - - - 108 - - - - - 32 - 1 - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - -

    - - - - - - - D - 3 - - 24 - 1 - half - - down - - - - - - - - G - 3 - - 24 - 1 - half - - down - - - - - - - 78 - - - - - - - - C - 1 - 3 - - 16 - 1 - half - sharp - down - - - - - - - - F - 1 - 3 - - 16 - 1 - half - sharp - down - - - - - - - - - - - - - - - - - - - F - 3 - - 24 - 1 - half - - down - - - - - - - - B - -1 - 3 - - 24 - 1 - half - - flat - down - - - - - - - - - - - E - 3 - - 16 - 1 - half - down - - - - - - - - A - 3 - - 16 - 1 - half - down - - - - - - - - - - - - - - - - - - - A - -1 - 3 - - 16 - 1 - half - flat - down - - - - - - - - D - -1 - 4 - - 16 - 1 - half - flat - down - - - - G - 3 - - 8 - 1 - quarter - down - - - - - - - - C - 4 - - 8 - 1 - quarter - down - - - - - - - - C - 4 - - - - - - - - - - - - - B - 3 - - 16 - 1 - half - down - - - - - - - - E - 4 - - 16 - 1 - half - down - - - - - - -14 - - - - B - -1 - 3 - - 8 - 1 - quarter - flat - down - - - - - - - - E - -1 - 4 - - 8 - 1 - quarter - flat - down - - - - - - -1 - - - - - - - - - - - - - - - - D - 4 - - 24 - - 1 - half - - down - - - - - - - - G - 4 - - 24 - - 1 - half - - down - - - - - - - - - - - - - - D - 4 - - 16 - - - 1 - half - down - - - - - - - - - G - 4 - - 16 - - - 1 - half - down - - - - - - - - - - - - - - - D - 4 - - 24 - - 1 - half - - down - - - - - - - - G - 4 - - 24 - - 1 - half - - down - - - - - - - - - - - - 74 - - none - - - 8 - - 0 - major - - - - F - 4 - - - 108 - - - - - 24 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - 100 - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - 74 - - - - - - 108 - - - - - 32 - 1 - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - -

    - - - - - - - B - 2 - - 24 - 1 - half - - up - - - - - - - - - - 74 - - - - - - - - B - -1 - 2 - - 16 - 1 - half - flat - up - - - - - - - - - - - - - - - - - - - - - - D - 3 - - 24 - 1 - half - - down - - - - - - - - - - - - - - C - 1 - 3 - - 16 - 1 - half - sharp - up - - - - - - - - - - - - - - - - - - - - - - F - 3 - - 16 - 1 - half - down - - - - - - - E - 3 - - 8 - 1 - quarter - down - - - - - - - - - - - - - - - - - - - A - -1 - 3 - - 16 - 1 - half - flat - down - - - - - - - - - -14 - - - - G - 3 - - 8 - 1 - quarter - down - - - - - - - - - -1 - - - - - - - - - - - - - - - - F - 1 - 3 - - 24 - - 1 - half - - sharp - down - - - - - - - - - - - - - - F - 1 - 3 - - 16 - - - 1 - half - down - - - - - - - - - - - - - - - F - 1 - 3 - - 24 - - 1 - half - - down - - - - - - - - - - - - 88 - - none - - - 8 - - 0 - major - - - - F - 4 - - - 108 - - - - - 24 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - 100 - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - 88 - - - - - - 108 - - - - - 32 - 1 - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - -

    - - - - - - - F - 1 - 2 - - 24 - 1 - half - - sharp - up - - - - - - - - - - 88 - - - - - - - - F - 2 - - 16 - 1 - half - natural - up - - - - - - - - - - - - - - - - - - - - - - A - 2 - - 24 - 1 - half - - up - - - - - - - - - - - - - - G - 1 - 2 - - 16 - 1 - half - sharp - up - - - - - - - - - - - - - - - - - - - - - - C - 2 - - 16 - 1 - half - up - - - - - - - B - 1 - - 8 - 1 - quarter - up - - - - - - - - - - - - - - - - - - - E - -1 - 2 - - 16 - 1 - half - flat - up - - - - - - - - - -14 - - - - D - 2 - - 8 - 1 - quarter - up - - - - - - - - - -1 - - - - - - - - - - - - - - - - F - 1 - 2 - - 24 - - 1 - half - - sharp - up - - - - - - - - - - - - - - F - 1 - 2 - - 16 - - - 1 - half - up - - - - - - - - - - - - - - - F - 1 - 2 - - 24 - - 1 - half - - up - - - - - - - - - - - - 113 - - none - - - 1024 - - 0 - major - - - - F - 4 - - - 108 - - - - - 3072 - 1 - - - - - - - - - - - 4096 - 1 - - - - - - - - 4096 - 1 - - - - - - - - - - - 3072 - 1 - - - - - - - - - - - 2048 - 1 - - - - - - - - - - - 4096 - 1 - - - - - - - - - - - 3072 - 1 - - - - - - - - - - - 2048 - 1 - - - - - - - - - 100 - - - - - 4096 - 1 - - - - - - - - - - - 3072 - 1 - - - - - - - - 3072 - 1 - - - - - - - - - - - 2048 - 1 - - - - - - - - - - - 4096 - 1 - - - - - - - - - - - 3072 - 1 - - - - - - - - 3072 - 1 - - - - - - - - 3072 - 1 - - - - - - - - 3072 - 1 - - - - - - - 72 - - - - - - - - - 4096 - 1 - - - - - - - - - - - 3072 - 1 - - - - - - - - 3072 - 1 - - - - - - - - - - - - 2048 - - - - - 1024 - 1 - quarter - - - - 1024 - 1 - quarter - - - - B - -1 - 2 - - 256 - 1 - 16th - flat - up - begin - begin - - - - B - -1 - 2 - - 256 - 1 - 16th - up - continue - end - - - - B - -1 - 2 - - 512 - 1 - eighth - up - end - - - - - - - - 1024 - 1 - quarter - - - - B - -1 - 2 - - 256 - 1 - 16th - flat - up - begin - begin - - - - B - -1 - 2 - - 256 - 1 - 16th - up - continue - end - - - - B - -1 - 2 - - 512 - 1 - eighth - up - end - - - - B - -1 - 2 - - 256 - 1 - 16th - up - begin - begin - - - - B - -1 - 2 - - 256 - 1 - 16th - up - continue - end - - - - B - -1 - 2 - - 512 - 1 - eighth - up - end - - - - - - - - - - - 2048 - 1 - - - - - - - - - - - - - - - 1024 - - - - - - - - - 3072 - - - - - 1024 - 1 - quarter - - - - B - -1 - 2 - - 256 - 1 - 16th - flat - up - begin - begin - - - - B - -1 - 2 - - 256 - 1 - 16th - up - continue - end - - - - B - -1 - 2 - - 512 - 1 - eighth - up - end - - - - 1024 - 1 - quarter - - - - B - -1 - 2 - - 256 - 1 - 16th - up - begin - begin - - - - B - -1 - 2 - - 256 - 1 - 16th - up - continue - end - - - - B - -1 - 2 - - 512 - 1 - eighth - up - end - - - - - - - - - - - - - - - - - - - - - - - 1024 - - - - - - - - - 2048 - - - - - B - -1 - 2 - - 256 - 1 - 16th - flat - up - begin - begin - - - - B - -1 - 2 - - 256 - 1 - 16th - up - continue - end - - - - B - -1 - 2 - - 512 - 1 - eighth - up - end - - - - B - -1 - 2 - - 256 - 1 - 16th - up - begin - begin - - - - B - -1 - 2 - - 256 - 1 - 16th - up - continue - end - - - - B - -1 - 2 - - 512 - 1 - eighth - up - end - - - - B - -1 - 2 - - 256 - 1 - 16th - up - begin - begin - - - - B - -1 - 2 - - 256 - 1 - 16th - up - continue - end - - - - B - -1 - 2 - - 512 - 1 - eighth - up - end - - - - - - - 113 - - - - - - 108 - - - - - 4096 - 1 - - - - - - - - -

    - - - - - - - E - 3 - - 4096 - 1 - whole - - - 3 - - - - - 4096 - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - - - -131 - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - 16 - 2 - - - - - - -54 - - - - - - - - - - - - - - - - - - - E - 3 - - 3072 - 1 - half - - down - - - 3 - - - - - 3072 - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - - - -98 - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - - E - 3 - - 85 - 2 - down - - - 12 - 2 - - - - - - -76 - - - - - - - - 3072 - 1 - - - - - - - - 3072 - 1 - - - - - - - - 3072 - 1 - - - - - - - - 3072 - 1 - - - - - - - - 3072 - 1 - - - - - - - 113 - - - - - - - - 2048 - 1 - - - - - - - - - - - 3072 - 1 - - - - - - - - - - - 2048 - 1 - - - - - - - - - - - 3072 - 1 - - - - - - - - 3072 - 1 - - - - - - - - - - - - - - - - F - 1 - 2 - - 1024 - 1 - quarter - sharp - up - - - - F - 1 - 2 - - 1024 - 1 - quarter - up - - - - F - 1 - 2 - - 1024 - 1 - quarter - up - - - - - - - - - - - 2048 - 1 - - - - - - - - - - - - - - - - - - - F - 1 - 2 - - 1024 - 1 - quarter - sharp - up - - - - F - 1 - 2 - - 1024 - 1 - quarter - up - - - - 1024 - 1 - quarter - - - - - - - - - 70 - - none - - - 8 - - 0 - major - - - - percussion - - - 108 - - - - - 24 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - 100 - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - 70 - - - - - - 108 - - - - - 32 - 1 - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - -

    - - - - - - - Susp. Cym. - - - - - E - 5 - - 24 - - - 1 - half - - down - - - - 3 - - - - - 24 - - - - E - 5 - - 2 - - 2 - 16th - down - begin - begin - - - - E - 5 - - 2 - - 2 - 16th - down - continue - continue - - - - E - 5 - - 2 - - 2 - 16th - down - continue - continue - - - - E - 5 - - 2 - - 2 - 16th - down - continue - continue - - - - E - 5 - - 2 - - 2 - 16th - down - continue - continue - - - - E - 5 - - 2 - - 2 - 16th - down - continue - continue - - - - E - 5 - - 2 - - 2 - 16th - down - continue - continue - - - - E - 5 - - 2 - - 2 - 16th - down - continue - continue - - - - E - 5 - - 2 - - 2 - 16th - down - continue - continue - - - - E - 5 - - 2 - - 2 - 16th - down - continue - continue - - - - E - 5 - - 2 - - 2 - 16th - down - continue - continue - - - - E - 5 - - 2 - - 2 - 16th - down - end - end - - - - - - - 70 - - - - - - - - (Susp. Cym.) - - - - - E - 5 - - 16 - - - - 1 - half - down - - - - - 3 - - - - - 16 - - - - E - 5 - - 2 - - 2 - 16th - down - begin - begin - - - - E - 5 - - 2 - - 2 - 16th - down - continue - continue - - - - E - 5 - - 2 - - 2 - 16th - down - continue - continue - - - - E - 5 - - 2 - - 2 - 16th - down - continue - continue - - - - E - 5 - - 2 - - 2 - 16th - down - continue - continue - - - - E - 5 - - 2 - - 2 - 16th - down - continue - continue - - - - E - 5 - - 2 - - 2 - 16th - down - continue - continue - - - - E - 5 - - 2 - - 2 - 16th - down - end - end - - - - - - - - - - - - - - - - E - 5 - - 24 - - - - 1 - half - - down - - - - - 3 - - - - - 24 - - - - E - 5 - - 2 - - 2 - 16th - down - begin - begin - - - - E - 5 - - 2 - - 2 - 16th - down - continue - continue - - - - E - 5 - - 2 - - 2 - 16th - down - continue - continue - - - - E - 5 - - 2 - - 2 - 16th - down - continue - continue - - - - E - 5 - - 2 - - 2 - 16th - down - continue - continue - - - - E - 5 - - 2 - - 2 - 16th - down - continue - continue - - - - E - 5 - - 2 - - 2 - 16th - down - continue - continue - - - - E - 5 - - 2 - - 2 - 16th - down - continue - continue - - - - E - 5 - - 2 - - 2 - 16th - down - continue - continue - - - - E - 5 - - 2 - - 2 - 16th - down - continue - continue - - - - E - 5 - - 2 - - 2 - 16th - down - continue - continue - - - - E - 5 - - 2 - - 2 - 16th - down - end - end - - - - - - - - - - - E - 5 - - 16 - - - - 1 - half - down - - - - - 3 - - - - - 16 - - - - E - 5 - - 2 - - 2 - 16th - down - begin - begin - - - - E - 5 - - 2 - - 2 - 16th - down - continue - continue - - - - E - 5 - - 2 - - 2 - 16th - down - continue - continue - - - - E - 5 - - 2 - - 2 - 16th - down - continue - continue - - - - E - 5 - - 2 - - 2 - 16th - down - continue - continue - - - - E - 5 - - 2 - - 2 - 16th - down - continue - continue - - - - E - 5 - - 2 - - 2 - 16th - down - continue - continue - - - - E - 5 - - 2 - - 2 - 16th - down - end - end - - - - - - - - - - - E - 5 - - 24 - - - - 1 - half - - down - - - - - 3 - - - - - 24 - - - - E - 5 - - 2 - - 2 - 16th - down - begin - begin - - - - E - 5 - - 2 - - 2 - 16th - down - continue - continue - - - - E - 5 - - 2 - - 2 - 16th - down - continue - continue - - - - E - 5 - - 2 - - 2 - 16th - down - continue - continue - - - - E - 5 - - 2 - - 2 - 16th - down - continue - continue - - - - E - 5 - - 2 - - 2 - 16th - down - continue - continue - - - - E - 5 - - 2 - - 2 - 16th - down - continue - continue - - - - E - 5 - - 2 - - 2 - 16th - down - continue - continue - - - - E - 5 - - 2 - - 2 - 16th - down - continue - continue - - - - E - 5 - - 2 - - 2 - 16th - down - continue - continue - - - - E - 5 - - 2 - - 2 - 16th - down - continue - continue - - - - E - 5 - - 2 - - 2 - 16th - down - end - end - - - - - - - - E - 5 - - 24 - - - 1 - half - - down - - - - 3 - - - - - 24 - - - - E - 5 - - 2 - - 2 - 16th - down - begin - begin - - - - E - 5 - - 2 - - 2 - 16th - down - continue - continue - - - - E - 5 - - 2 - - 2 - 16th - down - continue - continue - - - - E - 5 - - 2 - - 2 - 16th - down - end - end - - - - E - 5 - - 2 - - 2 - 16th - down - begin - begin - - - - E - 5 - - 2 - - 2 - 16th - down - end - end - - - - E - 5 - - 12 - - 2 - quarter - - down - - - - - - -1 - - - - - - - - - - - - - - - - - E - 5 - - 24 - - 1 - half - - down - - - - - - - - - - - 16 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - - 62 - - none - - - 1024 - - 0 - major - - - - percussion - - - 108 - - - - - 3072 - 1 - - - - - - - - - - - 4096 - 1 - - - - - - - - 4096 - 1 - - - - - - - - - - - 3072 - 1 - - - - - - - - - - - 2048 - 1 - - - - - - - - - - - 4096 - 1 - - - - - - - - - - - 3072 - 1 - - - - - - - - - - - 2048 - 1 - - - - - - - - - 100 - - - - - 4096 - 1 - - - - - - - - - - - 3072 - 1 - - - - - - - - 3072 - 1 - - - - - - - - - - - 2048 - 1 - - - - - - - - - - - 4096 - 1 - - - - - - - - - - - 3072 - 1 - - - - - - - - 3072 - 1 - - - - - - - - 3072 - 1 - - - - - - - - 3072 - 1 - - - - - - - - - - - 4096 - 1 - - - - - - - - - - - 3072 - 1 - - - - - - - - 3072 - 1 - - - - - - - - 3072 - 1 - - - - - - - - 3072 - 1 - - - - - - - - - - - 2048 - 1 - - - - - - - - - - - 4096 - 1 - - - - - - - - - - - 3072 - 1 - - - - - - - 62 - - - - - - 108 - - - - - 4096 - 1 - - - - - - - - 4096 - 1 - - - - - - - - - - - 3072 - 1 - - - - - - - - 3072 - 1 - - - - - - - - 3072 - 1 - - - - - - - - 3072 - 1 - - - - - - - - 3072 - 1 - - - - - - - - -

    - - - - - - - B.D. - - - - - F - 4 - - 3072 - - - 1 - half - - up - - - - 3 - - - - - 3072 - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - 12 - 2 - - - - - - - 62 - - - - - - - - (B.D.) - - - - - F - 4 - - 2048 - - - - 1 - half - up - - - - - 3 - - - - - 2048 - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - 8 - 2 - - - - - - - - - - - - - 79 - - - - F - 4 - - 3072 - - - - 1 - half - - up - - - - - 3 - - - - - 3072 - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - 12 - 2 - - - - - - - - - - - F - 4 - - 2048 - - - - 1 - half - up - - - - - 3 - - - - - 2048 - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - 8 - 2 - - - - - - - - - - - F - 4 - - 3072 - - - - 1 - half - - up - - - - - 3 - - - - - 3072 - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - 12 - 2 - - - - - - - - F - 4 - - 3072 - - - 1 - half - - up - - - - 3 - - - - - 3072 - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - - F - 4 - - 102 - - 2 - up - - - 12 - 2 - - - - - - -54 - - - - - - - - - - - - - - - - F - 4 - - 1024 - - 1 - quarter - up - - - - F - 4 - - 1024 - - 1 - quarter - up - - - - F - 4 - - 1024 - - 1 - quarter - up - - - - - - - - - - - 2048 - 1 - - - - - - - - - - - - - - - - - - - F - 4 - - 1024 - - 1 - quarter - up - - - - F - 4 - - 1024 - - 1 - quarter - up - - - - 1024 - 1 - quarter - - - - - - - - - 85 - - - 77 - - none - - - 8 - - 0 - major - - - 2 - - G - 2 - - - F - 4 - - - 108 - - - 108 - - - - - - - - - 1 - - - - - B - 5 - - 8 - 1 - quarter - down - 1 - - - - - B - 6 - - 8 - 1 - quarter - down - 1 - - - - 8 - 1 - quarter - 1 - - - - 8 - 1 - quarter - 1 - - - 24 - - - - 24 - 2 - 2 - - - - - - - - - - - 32 - 1 - 1 - - - 32 - - - - 32 - 2 - 2 - - - - - - - - 32 - 1 - 1 - - - 32 - - - - 32 - 2 - 2 - - - - - - - - - - - 24 - 1 - 1 - - - 24 - - - - 24 - 2 - 2 - - - - - - - - - - - 16 - 1 - 1 - - - 16 - - - - 16 - 2 - 2 - - - - - - - - - - - 32 - 1 - 1 - - - 32 - - - - 32 - 2 - 2 - - - - - - - - - - - 24 - 1 - 1 - - - 24 - - - - 24 - 2 - 2 - - - - - - - - - - - 16 - 1 - 1 - - - 16 - - - - 16 - 2 - 2 - - - - - - - - - 100 - - - 100 - - - - - 32 - 1 - 1 - - - 32 - - - - 32 - 2 - 2 - - - - - - - - - - - 24 - 1 - 1 - - - 24 - - - - 24 - 2 - 2 - - - - - - - - 24 - 1 - 1 - - - 24 - - - - 24 - 2 - 2 - - - - - - - - - - - 16 - 1 - 1 - - - 16 - - - - 16 - 2 - 2 - - - - - - - - - - - 32 - 1 - 1 - - - 32 - - - - 32 - 2 - 2 - - - - - - - - - - - 24 - 1 - 1 - - - 24 - - - - 24 - 2 - 2 - - - - - - - - 24 - 1 - 1 - - - 24 - - - - 24 - 2 - 2 - - - - - - - - 24 - 1 - 1 - - - 24 - - - - 24 - 2 - 2 - - - - - - - - 24 - 1 - 1 - - - 24 - - - - 24 - 2 - 2 - - - - - - - - - - - 32 - 1 - 1 - - - 32 - - - - 32 - 2 - 2 - - - - - - - - - - - 24 - 1 - 1 - - - 24 - - - - 24 - 2 - 2 - - - - - - - - 24 - 1 - 1 - - - 24 - - - - 24 - 2 - 2 - - - - - - - - 24 - 1 - 1 - - - 24 - - - - 24 - 2 - 2 - - - - - - - - 24 - 1 - 1 - - - 24 - - - - 24 - 2 - 2 - - - - - - - - - - - 16 - 1 - 1 - - - 16 - - - - 16 - 2 - 2 - - - - - - - - - - - 32 - 1 - 1 - - - 32 - - - - 32 - 2 - 2 - - - - - - - - - - - 24 - 1 - 1 - - - 24 - - - - 24 - 2 - 2 - - - - - - - 85 - - - 77 - - - - - - G - 2 - - - 108 - - - 108 - - - - - - - - - 1 - - - - - E - 5 - - 8 - 1 - quarter - down - 1 - - - - - E - 6 - - 8 - 1 - quarter - down - 1 - - - - E - 5 - - 8 - 1 - quarter - down - 1 - - - - - E - 6 - - 8 - 1 - quarter - down - 1 - - - - E - 5 - - 8 - 1 - quarter - down - 1 - - - - - E - 6 - - 8 - 1 - quarter - down - 1 - - - - E - 5 - - 8 - 1 - quarter - down - 1 - - - - - E - 6 - - 8 - 1 - quarter - down - 1 - - - 32 - - - - mf - - 2 - 2 - - - - - 2 - 2 - 16th - 2 - - - - C - 1 - 5 - - 2 - 2 - 16th - sharp - up - 2 - begin - begin - - - - A - 4 - - 2 - 2 - 16th - up - 2 - continue - continue - - - - E - 4 - - 2 - 2 - 16th - up - 2 - end - end - - - - F - 4 - - 8 - 2 - quarter - up - 2 - - - - 2 - 2 - 16th - 2 - - - - C - 1 - 5 - - 2 - 2 - 16th - up - 2 - begin - begin - - - - A - 4 - - 2 - 2 - 16th - up - 2 - continue - continue - - - - E - 4 - - 2 - 2 - 16th - up - 2 - end - end - - - - F - 4 - - 8 - 2 - quarter - up - 2 - - - - - - - - - - 1 - - - - E - 5 - - 8 - 1 - quarter - down - 1 - - - - - E - 6 - - 8 - 1 - quarter - down - 1 - - - - E - 5 - - 8 - 1 - quarter - down - 1 - - - - - E - 6 - - 8 - 1 - quarter - down - 1 - - - - E - 5 - - 8 - 1 - quarter - down - 1 - - - - - E - 6 - - 8 - 1 - quarter - down - 1 - - - - E - 5 - - 8 - 1 - quarter - down - 1 - - - - - E - 6 - - 8 - 1 - quarter - down - 1 - - - - - - -1 - 1 - - - 32 - - - - 2 - 2 - 16th - 2 - - - - C - 1 - 5 - - 2 - 2 - 16th - sharp - up - 2 - begin - begin - - - - A - 4 - - 2 - 2 - 16th - up - 2 - continue - continue - - - - E - 4 - - 2 - 2 - 16th - up - 2 - end - end - - - - F - 4 - - 8 - 2 - quarter - up - 2 - - - - 2 - 2 - 16th - 2 - - - - C - 1 - 5 - - 2 - 2 - 16th - up - 2 - begin - begin - - - - A - 4 - - 2 - 2 - 16th - up - 2 - continue - continue - - - - E - 4 - - 2 - 2 - 16th - up - 2 - end - end - - - - F - 4 - - 8 - 2 - quarter - up - 2 - - - - - - - - - - - - - - - 1 - - - - - E - 5 - - 8 - 1 - quarter - down - 1 - - - - - E - 6 - - 8 - 1 - quarter - down - 1 - - - - - - -6 - 1 - - - - E - 5 - - 8 - 1 - quarter - down - 1 - - - - - E - 6 - - 8 - 1 - quarter - down - 1 - - - - E - 5 - - 8 - 1 - quarter - down - 1 - - - - - E - 6 - - 8 - 1 - quarter - down - 1 - - - - - - -1 - 1 - - - 24 - - - - ff - - 2 - 2 - - - - - 2 - 2 - 16th - 2 - - - - C - 1 - 5 - - 2 - 2 - 16th - sharp - up - 2 - begin - begin - - - - A - 4 - - 2 - 2 - 16th - up - 2 - continue - continue - - - - E - 4 - - 2 - 2 - 16th - up - 2 - end - end - - - - F - 4 - - 8 - 2 - quarter - up - 2 - - - - 2 - 2 - 16th - 2 - - - - C - 1 - 5 - - 2 - 2 - 16th - up - 2 - begin - begin - - - - A - 4 - - 2 - 2 - 16th - up - 2 - continue - continue - - - - E - 4 - - 2 - 2 - 16th - up - 2 - end - end - - - - - - - - - - - - 1 - - - - - E - 5 - - 2 - 1 - 16th - up - 1 - begin - begin - - - - C - 1 - 5 - - 2 - 1 - 16th - sharp - up - 1 - continue - continue - - - - A - 4 - - 2 - 1 - 16th - up - 1 - continue - continue - - - - E - 4 - - 2 - 1 - 16th - up - 1 - end - end - - - - 8 - 1 - quarter - 1 - - - - E - 5 - - 2 - 1 - 16th - up - 1 - begin - begin - - - - C - 1 - 5 - - 2 - 1 - 16th - up - 1 - continue - continue - - - - A - 4 - - 2 - 1 - 16th - up - 1 - continue - continue - - - - E - 4 - - 2 - 1 - 16th - up - 1 - end - end - - - 24 - - - - mf - - 2 - - - - - F - 3 - - 8 - 2 - quarter - up - 2 - - - - - F - 4 - - 8 - 2 - quarter - up - 2 - - - - F - 3 - - 8 - 2 - quarter - up - 2 - - - - - F - 4 - - 8 - 2 - quarter - up - 2 - - - - F - 3 - - 8 - 2 - quarter - up - 2 - - - - - F - 4 - - 8 - 2 - quarter - up - 2 - - - - - - - - 8 - 1 - quarter - 1 - - - - 8 - 1 - quarter - 1 - - - - E - 5 - - 2 - 1 - 16th - up - 1 - begin - begin - - - - C - 1 - 5 - - 2 - 1 - 16th - sharp - up - 1 - continue - continue - - - - A - 4 - - 2 - 1 - 16th - up - 1 - continue - continue - - - - E - 4 - - 2 - 1 - 16th - up - 1 - end - end - - - 24 - - - - 8 - 2 - quarter - 2 - - - - 8 - 2 - quarter - 2 - - - - F - 3 - - 8 - 2 - quarter - up - 2 - - - - - F - 4 - - 8 - 2 - quarter - up - 2 - - - - - - - - 8 - 1 - quarter - 1 - - - - 8 - 1 - quarter - 1 - - - - E - 5 - - 2 - 1 - 16th - up - 1 - begin - begin - - - - C - 1 - 5 - - 2 - 1 - 16th - sharp - up - 1 - continue - continue - - - - A - 4 - - 2 - 1 - 16th - up - 1 - continue - continue - - - - E - 4 - - 2 - 1 - 16th - up - 1 - end - end - - - 24 - - - - F - 3 - - 8 - 2 - quarter - up - 2 - - - - - F - 4 - - 8 - 2 - quarter - up - 2 - - - - 8 - 2 - quarter - 2 - - - - F - 3 - - 8 - 2 - quarter - up - 2 - - - - - F - 4 - - 8 - 2 - quarter - up - 2 - - - - - - - - 8 - 1 - quarter - 1 - - - - E - 5 - - 2 - 1 - 16th - up - 1 - begin - begin - - - - C - 1 - 5 - - 2 - 1 - 16th - sharp - up - 1 - continue - continue - - - - A - 4 - - 2 - 1 - 16th - up - 1 - continue - continue - - - - E - 4 - - 2 - 1 - 16th - up - 1 - end - end - - - - E - 5 - - 2 - 1 - 16th - up - 1 - begin - begin - - - - C - 1 - 5 - - 2 - 1 - 16th - up - 1 - continue - continue - - - - A - 4 - - 2 - 1 - 16th - up - 1 - continue - continue - - - - E - 4 - - 2 - 1 - 16th - up - 1 - end - end - - - 24 - - - - 8 - 2 - quarter - 2 - - - - F - 3 - - 8 - 2 - quarter - up - 2 - - - - - F - 4 - - 8 - 2 - quarter - up - 2 - - - - F - 3 - - 8 - 2 - quarter - up - 2 - - - - - F - 4 - - 8 - 2 - quarter - up - 2 - - - - - - - - 24 - 1 - 1 - - - 24 - - - - 24 - 2 - 2 - - - - - - - 73 - - - 75 - - - - - - - - 16 - 1 - 1 - - - 16 - - - - 16 - 2 - 2 - - - - - - - - - - - 24 - 1 - 1 - - - 24 - - - - 24 - 2 - 2 - - - - - - - - - - - 16 - 1 - 1 - - - 16 - - - - 16 - 2 - 2 - - - - - - - - - - - 24 - 1 - 1 - - - 24 - - - - 24 - 2 - 2 - - - - - - - - 24 - 1 - 1 - - - 24 - - - - 24 - 2 - 2 - - - - - - - - 24 - 1 - 1 - - - 24 - - - - 24 - 2 - 2 - - - - - - - - - - - 16 - 1 - 1 - - - 16 - - - - 16 - 2 - 2 - - - - - - - - - - - 24 - 1 - 1 - - - 24 - - - - 24 - 2 - 2 - - - - - - - - - 121 - - none - - - 8 - - 0 - major - - - - G - 2 - - - 108 - - - - - Moderato - - -   - - - - quarter - 85 - - - - - - - -

    - - - - - - - E - 5 - - 24 - - 1 - half - - down - - - - - - - - B - 5 - - 24 - - 1 - half - - down - diamond - - - - - - - - - - - - - - E - 5 - - 32 - - - 1 - whole - - - - - - - - - B - 5 - - 32 - - - 1 - whole - diamond - - - - - - - - - - - - E - 5 - - 32 - - - 1 - whole - - - - - - - - - B - 5 - - 32 - - - 1 - whole - diamond - - - - - - - - - - - - - - - E - 5 - - 24 - - - 1 - half - - down - - - - - - - - - B - 5 - - 24 - - - 1 - half - - down - diamond - - - - - - - - - - - - - - - E - 5 - - 16 - - - 1 - half - down - - - - - - - - - B - 5 - - 16 - - - 1 - half - down - diamond - - - - - - - - - - - - - - - E - 5 - - 32 - - - 1 - whole - - - - - - - - - B - 5 - - 32 - - - 1 - whole - diamond - - - - - - - - - - - - - - - E - 5 - - 24 - - - 1 - half - - down - - - - - - - - - B - 5 - - 24 - - - 1 - half - - down - diamond - - - - - - - - - - - - - - - E - 5 - - 16 - - - 1 - half - down - - - - - - - - - B - 5 - - 16 - - - 1 - half - down - diamond - - - - - - - - - - - 135 - - - - - - 100 - - - - - E - 5 - - 32 - - - 1 - whole - - - - - - - - - B - 5 - - 32 - - - 1 - whole - diamond - - - - - - - - - - - - - - - E - 5 - - 24 - - - 1 - half - - down - - - - - - - - - B - 5 - - 24 - - - 1 - half - - down - diamond - - - - - - - - - - - - E - 5 - - 24 - - - 1 - half - - down - - - - - - - - - B - 5 - - 24 - - - 1 - half - - down - diamond - - - - - - - - - - - - - - - E - 5 - - 16 - - - 1 - half - down - - - - - - - - - B - 5 - - 16 - - - 1 - half - down - diamond - - - - - - - - - - - - - - - E - 5 - - 32 - - 1 - whole - - - - - - - - B - 5 - - 32 - - 1 - whole - diamond - - - - - - - - - - - - - - 24 - 1 - - - - - - - - - - - - 8 - - - - - - - - - 16 - - - - - 8 - 1 - quarter - - - - B - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - - - - C - 6 - - 2 - 1 - 16th - down - continue - continue - - - - D - 6 - - 2 - 1 - 16th - down - continue - continue - - - - E - -1 - 6 - - 2 - 1 - 16th - flat - down - end - end - - - - - - - B - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - C - 6 - - 2 - 1 - 16th - down - continue - continue - - - - D - 6 - - 2 - 1 - 16th - down - continue - continue - - - - E - -1 - 6 - - 2 - 1 - 16th - down - end - end - - - - - - - - - - - - - - - - - - - B - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - - - 1 - - - - C - 6 - - 2 - 1 - 16th - down - continue - continue - - - - D - 6 - - 2 - 1 - 16th - down - continue - continue - - - - E - -1 - 6 - - 2 - 1 - 16th - flat - down - end - end - - - - - - - B - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - C - 6 - - 2 - 1 - 16th - down - continue - continue - - - - D - 6 - - 2 - 1 - 16th - down - continue - continue - - - - E - -1 - 6 - - 2 - 1 - 16th - down - end - end - - - - - - - B - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - C - 6 - - 2 - 1 - 16th - down - continue - continue - - - - D - 6 - - 2 - 1 - 16th - down - continue - continue - - - - E - -1 - 6 - - 2 - 1 - 16th - down - end - end - - - - - - - - - -1 - - - - - - - - - - - - - - - - F - 6 - - 2 - 1 - 16th - down - - - - 2 - 1 - 16th - - - - 4 - 1 - eighth - - - - 8 - 1 - quarter - - - - 8 - 1 - quarter - - - - - - - 83 - - - - - - - - 32 - 1 - - - - - - - - - - - A - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - - - - pizz. - - - - 46 - - - - - - - - - - - - - - E - 5 - - 8 - 1 - quarter - down - - - - E - 5 - - 8 - 1 - quarter - down - - - - - - - - - - - - - - - 16 - - - - - E - 5 - - 8 - 1 - quarter - down - - - - B - -1 - 4 - - 8 - 1 - quarter - flat - down - - - - E - 5 - - 8 - 1 - quarter - down - - - - B - -1 - 4 - - 8 - 1 - quarter - down - - - - - - - - - - - - - - - - - - - - - - - 8 - - - - - - - - - 16 - - - - - B - -1 - 4 - - 8 - 1 - quarter - flat - down - - - - B - -1 - 4 - - 8 - 1 - quarter - down - - - - B - -1 - 4 - - 8 - 1 - quarter - down - - - - - - - 121 - - - - - - 108 - - - - - 32 - 1 - - - - - - - - -

    - - - - - - - arco - - - - 49 - - - - - - E - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - - - - - - 1 - - - - F - 5 - - 2 - 1 - 16th - down - continue - continue - - - - E - 5 - - 2 - 1 - 16th - down - continue - continue - - - - F - 5 - - 2 - 1 - 16th - down - end - end - - - - E - 5 - - 2 - 1 - 16th - down - begin - begin - - - - F - 5 - - 2 - 1 - 16th - down - continue - continue - - - - G - 5 - - 2 - 1 - 16th - down - continue - continue - - - - A - -1 - 5 - - 2 - 1 - 16th - flat - down - end - end - - - - - - - E - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - F - 5 - - 2 - 1 - 16th - down - continue - continue - - - - E - 5 - - 2 - 1 - 16th - down - continue - continue - - - - F - 5 - - 2 - 1 - 16th - down - end - end - - - - E - 5 - - 2 - 1 - 16th - down - begin - begin - - - - F - 5 - - 2 - 1 - 16th - down - continue - continue - - - - G - 5 - - 2 - 1 - 16th - down - continue - continue - - - - A - -1 - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - - - -1 - - - - - - - - - - - - - - - - - - - B - -1 - 5 - - 2 - 1 - 16th - flat - down - begin - begin - - - - - - - - - - - - C - -1 - 6 - - 2 - 1 - 16th - flat - down - continue - continue - - - - B - -1 - 5 - - 2 - 1 - 16th - down - continue - continue - - - - C - -1 - 6 - - 2 - 1 - 16th - down - end - end - - - - - - - B - -1 - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - C - -1 - 6 - - 2 - 1 - 16th - down - continue - continue - - - - B - -1 - 5 - - 2 - 1 - 16th - down - continue - continue - - - - A - -1 - 5 - - 2 - 1 - 16th - flat - down - end - end - - - - - - - G - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - A - -1 - 5 - - 2 - 1 - 16th - down - continue - continue - - - - G - 5 - - 2 - 1 - 16th - down - continue - continue - - - - - - 1 - - - - F - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - - - - - -

    - - - - - - - E - 5 - - 2 - 1 - 16th - down - - - - 2 - 1 - 16th - - - - 4 - 1 - eighth - - - - 8 - 1 - quarter - - - - 8 - 1 - quarter - - - - - - - - -

    - - - - - - - E - 5 - - 2 - 1 - 16th - up - begin - begin - - - - - - - - - - C - 1 - 5 - - 2 - 1 - 16th - sharp - up - continue - continue - - - - A - 4 - - 2 - 1 - 16th - up - continue - continue - - - - E - 4 - - 2 - 1 - 16th - up - end - end - - - - - - - E - 5 - - 2 - 1 - 16th - up - begin - begin - - - - - - - - - - C - 1 - 5 - - 2 - 1 - 16th - up - continue - continue - - - - A - 4 - - 2 - 1 - 16th - up - continue - continue - - - - E - 4 - - 2 - 1 - 16th - up - end - end - - - - - - - 8 - 1 - quarter - - - - - - - - 8 - 1 - quarter - - - - E - 5 - - 2 - 1 - 16th - up - begin - begin - - - - - - - - - - C - 1 - 5 - - 2 - 1 - 16th - sharp - up - continue - continue - - - - A - 4 - - 2 - 1 - 16th - up - continue - continue - - - - E - 4 - - 2 - 1 - 16th - up - end - end - - - - - - - 8 - 1 - quarter - - - - - - - - E - 5 - - 2 - 1 - 16th - up - begin - begin - - - - - - - - - - C - 1 - 5 - - 2 - 1 - 16th - sharp - up - continue - continue - - - - A - 4 - - 2 - 1 - 16th - up - continue - continue - - - - E - 4 - - 2 - 1 - 16th - up - end - end - - - - - - - 8 - 1 - quarter - - - - E - 5 - - 2 - 1 - 16th - up - begin - begin - - - - - - - - - - C - 1 - 5 - - 2 - 1 - 16th - up - continue - continue - - - - A - 4 - - 2 - 1 - 16th - up - continue - continue - - - - E - 4 - - 2 - 1 - 16th - up - end - end - - - - - - - - - - - -

    - - - - - - - F - 1 - 4 - - 2 - 1 - 16th - sharp - up - begin - begin - - - - - - - - - - G - 4 - - 2 - 1 - 16th - up - continue - continue - - - - A - 4 - - 2 - 1 - 16th - up - continue - continue - - - - B - -1 - 4 - - 2 - 1 - 16th - flat - up - end - end - - - - - - - F - 1 - 4 - - 2 - 1 - 16th - up - begin - begin - - - - - - - G - 4 - - 2 - 1 - 16th - up - continue - continue - - - - A - 4 - - 2 - 1 - 16th - up - continue - continue - - - - B - -1 - 4 - - 2 - 1 - 16th - up - end - end - - - - - - - C - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - D - -1 - 5 - - 2 - 1 - 16th - flat - down - continue - continue - - - - E - -1 - 5 - - 2 - 1 - 16th - flat - down - continue - continue - - - - E - 5 - - 2 - 1 - 16th - natural - down - end - end - - - - - - - - - - 101 - - - - - - - - F - 5 - - 2 - 1 - 16th - natural - down - begin - begin - - - - - - - G - -1 - 5 - - 2 - 1 - 16th - flat - down - continue - continue - - - - F - 5 - - 2 - 1 - 16th - down - continue - continue - - - - E - -1 - 5 - - 2 - 1 - 16th - flat - down - end - end - - - - - - - D - 5 - - 2 - 1 - 16th - natural - down - begin - begin - - - - - - - C - 5 - - 2 - 1 - 16th - down - continue - continue - - - - B - 4 - - 2 - 1 - 16th - natural - down - continue - continue - - - - A - 4 - - 2 - 1 - 16th - down - end - end - - - - - - - - - - - - - - - - - - - - - - A - 4 - - 2 - 1 - 16th - down - begin - begin - - - - - - - - - - B - -1 - 4 - - 2 - 1 - 16th - flat - down - continue - continue - - - - C - 5 - - 2 - 1 - 16th - down - continue - continue - - - - D - -1 - 5 - - 2 - 1 - 16th - flat - down - end - end - - - - - - - A - 4 - - 2 - 1 - 16th - down - begin - begin - - - - - - - B - -1 - 4 - - 2 - 1 - 16th - down - continue - continue - - - - C - 5 - - 2 - 1 - 16th - down - continue - continue - - - - C - 1 - 5 - - 2 - 1 - 16th - sharp - down - end - end - - - - - - - D - 1 - 5 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - E - 5 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 5 - - 2 - 1 - 16th - sharp - down - continue - continue - - - - G - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - - - - - - - - G - 1 - 5 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - A - 5 - - 2 - 1 - 16th - down - continue - continue - - - - G - 1 - 5 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 5 - - 2 - 1 - 16th - sharp - down - end - end - - - - - - - F - 5 - - 2 - 1 - 16th - natural - down - begin - begin - - - - - - - E - -1 - 5 - - 2 - 1 - 16th - flat - down - continue - continue - - - - D - 5 - - 2 - 1 - 16th - down - continue - continue - - - - C - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - - - - - - - - - - - - - - - - C - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - - - - C - 1 - 5 - - 2 - 1 - 16th - sharp - down - continue - continue - - - - D - 1 - 5 - - 2 - 1 - 16th - sharp - down - continue - continue - - - - E - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - F - 1 - 5 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - G - 5 - - 2 - 1 - 16th - down - continue - continue - - - - A - 5 - - 2 - 1 - 16th - down - continue - continue - - - - B - -1 - 5 - - 2 - 1 - 16th - flat - down - end - end - - - - - - - A - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - G - 5 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 5 - - 2 - 1 - 16th - down - continue - continue - - - - E - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - - - - - - - - - - - - - D - 1 - 5 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - - - - - - - - - E - 5 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 5 - - 2 - 1 - 16th - sharp - down - continue - continue - - - - G - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - A - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - B - -1 - 5 - - 2 - 1 - 16th - flat - down - continue - continue - - - - C - 6 - - 2 - 1 - 16th - down - continue - continue - - - - C - 1 - 6 - - 2 - 1 - 16th - sharp - down - end - end - - - - - - - D - 6 - - 2 - 1 - 16th - down - begin - begin - - - - - - - D - 1 - 6 - - 2 - 1 - 16th - sharp - down - continue - continue - - - - E - 6 - - 2 - 1 - 16th - down - continue - continue - - - - F - 6 - - 2 - 1 - 16th - natural - down - end - end - - - - - - - - - -1 - - - - - - - - - - - - - - - - F - 1 - 6 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - G - 6 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 6 - - 2 - 1 - 16th - down - continue - continue - - - - G - 6 - - 2 - 1 - 16th - down - end - end - - - - - - - F - 1 - 6 - - 2 - 1 - 16th - down - begin - begin - - - - - - - G - 6 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 6 - - 2 - 1 - 16th - down - continue - continue - - - - G - 6 - - 2 - 1 - 16th - down - end - end - - - - - - - F - 1 - 6 - - 2 - 1 - 16th - down - begin - begin - - - - - - - G - 6 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 6 - - 2 - 1 - 16th - down - continue - continue - - - - G - 6 - - 2 - 1 - 16th - down - end - end - - - - - - - - - - - - - - 16 - 1 - - - - - - - - - - - - - - - - - - - F - 1 - 6 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - G - 6 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 6 - - 2 - 1 - 16th - down - continue - continue - - - - G - 6 - - 2 - 1 - 16th - down - end - end - - - - - - - F - 1 - 6 - - 2 - 1 - 16th - down - begin - begin - - - - - - - G - 6 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 6 - - 2 - 1 - 16th - down - continue - continue - - - - G - 6 - - 2 - 1 - 16th - down - end - end - - - - - - - 8 - 1 - quarter - - - - - - - - none - - - 8 - - 0 - major - - - - G - 2 - - - 108 - - - - - pizz. - - - - 46 - - - - - - - - - - - - - - B - 5 - - 8 - 1 - quarter - down - - - - 8 - 1 - quarter - - - - 8 - 1 - quarter - - - - - - - - - - - 32 - 1 - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - - - - arco - - - - 49 - - - - - - -

    - - - - - - - B - 4 - - 2 - 1 - 16th - down - begin - begin - - - - - - - - - - C - 5 - - 2 - 1 - 16th - down - continue - continue - - - - B - 4 - - 2 - 1 - 16th - down - continue - continue - - - - C - 5 - - 2 - 1 - 16th - down - end - end - - - - B - 4 - - 2 - 1 - 16th - down - begin - begin - - - - C - 5 - - 2 - 1 - 16th - down - continue - continue - - - - D - 5 - - 2 - 1 - 16th - down - continue - continue - - - - E - -1 - 5 - - 2 - 1 - 16th - flat - down - end - end - - - - B - 4 - - 2 - 1 - 16th - down - begin - begin - - - - C - 5 - - 2 - 1 - 16th - down - continue - continue - - - - B - 4 - - 2 - 1 - 16th - down - continue - continue - - - - C - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - - - - - - - - - - 1 - - - - B - 4 - - 2 - 1 - 16th - down - begin - begin - - - - - - - C - 5 - - 2 - 1 - 16th - down - continue - continue - - - - B - 4 - - 2 - 1 - 16th - down - continue - continue - - - - C - 5 - - 2 - 1 - 16th - down - end - end - - - - B - 4 - - 2 - 1 - 16th - down - begin - begin - - - - C - 5 - - 2 - 1 - 16th - down - continue - continue - - - - D - 5 - - 2 - 1 - 16th - down - continue - continue - - - - - - 1 - - - - E - -1 - 5 - - 2 - 1 - 16th - flat - down - end - end - - - - - - - - - - 76 - - - - - - 100 - - - - - - - - - - - - - F - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - - - - - - G - -1 - 5 - - 2 - 1 - 16th - flat - down - continue - continue - - - - F - 5 - - 2 - 1 - 16th - down - continue - continue - - - - G - -1 - 5 - - 2 - 1 - 16th - down - end - end - - - - F - 5 - - 2 - 1 - 16th - down - begin - begin - - - - G - -1 - 5 - - 2 - 1 - 16th - down - continue - continue - - - - F - 5 - - 2 - 1 - 16th - down - continue - continue - - - - G - -1 - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - F - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - G - -1 - 5 - - 2 - 1 - 16th - down - continue - continue - - - - F - 5 - - 2 - 1 - 16th - down - continue - continue - - - - E - -1 - 5 - - 2 - 1 - 16th - flat - down - end - end - - - - D - 5 - - 2 - 1 - 16th - down - begin - begin - - - - E - -1 - 5 - - 2 - 1 - 16th - down - continue - continue - - - - D - 5 - - 2 - 1 - 16th - down - continue - continue - - - - C - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - - - -1 - - - - - - - - - - - - - - - - - - - B - 4 - - 2 - 1 - 16th - down - - - - 2 - 1 - 16th - - - - 4 - 1 - eighth - - - - 8 - 1 - quarter - - - - 8 - 1 - quarter - - - - - - - - -

    - - - - - - - B - 4 - - 2 - 1 - 16th - down - begin - begin - - - - - - - - - - C - 5 - - 2 - 1 - 16th - down - continue - continue - - - - D - 5 - - 2 - 1 - 16th - down - continue - continue - - - - E - -1 - 5 - - 2 - 1 - 16th - flat - down - end - end - - - - B - 4 - - 2 - 1 - 16th - down - begin - begin - - - - C - 5 - - 2 - 1 - 16th - down - continue - continue - - - - D - 5 - - 2 - 1 - 16th - down - continue - continue - - - - E - -1 - 5 - - 2 - 1 - 16th - down - end - end - - - - B - 4 - - 2 - 1 - 16th - down - begin - begin - - - - C - 5 - - 2 - 1 - 16th - down - continue - continue - - - - B - 4 - - 2 - 1 - 16th - down - continue - continue - - - - C - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - - - - - - - - - - - - - B - 4 - - 2 - 1 - 16th - down - begin - begin - - - - - - - C - 5 - - 2 - 1 - 16th - down - continue - continue - - - - B - 4 - - 2 - 1 - 16th - down - continue - continue - - - - C - 5 - - 2 - 1 - 16th - down - end - end - - - - B - 4 - - 2 - 1 - 16th - down - begin - begin - - - - C - 5 - - 2 - 1 - 16th - down - continue - continue - - - - D - 5 - - 2 - 1 - 16th - down - continue - continue - - - - E - -1 - 5 - - 2 - 1 - 16th - flat - down - end - end - - - - - - - - - -1 - - - - - - - - - - - - - - - - - - - F - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - - - - - - G - -1 - 5 - - 2 - 1 - 16th - flat - down - continue - continue - - - - F - 5 - - 2 - 1 - 16th - down - continue - continue - - - - G - -1 - 5 - - 2 - 1 - 16th - down - end - end - - - - F - 5 - - 2 - 1 - 16th - down - begin - begin - - - - G - -1 - 5 - - 2 - 1 - 16th - down - continue - continue - - - - F - 5 - - 2 - 1 - 16th - down - continue - continue - - - - G - -1 - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - F - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - G - -1 - 5 - - 2 - 1 - 16th - down - continue - continue - - - - F - 5 - - 2 - 1 - 16th - down - continue - continue - - - - E - -1 - 5 - - 2 - 1 - 16th - flat - down - end - end - - - - D - 5 - - 2 - 1 - 16th - down - begin - begin - - - - E - -1 - 5 - - 2 - 1 - 16th - down - continue - continue - - - - D - 5 - - 2 - 1 - 16th - down - continue - continue - - - - C - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - - - -1 - - - - - - - - - - - - - - - - - - - -

    - - - 16 - - - - - B - 4 - - 2 - 1 - 16th - down - - - - 2 - 1 - 16th - - - - 4 - 1 - eighth - - - - 8 - 1 - quarter - - - - B - 4 - - 2 - 1 - 16th - down - begin - begin - - - - - - - - - - C - 5 - - 2 - 1 - 16th - down - continue - continue - - - - B - 4 - - 2 - 1 - 16th - down - continue - continue - - - - C - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - - - - - - - - - 8 - - - - - - - - - 16 - - - - - B - 4 - - 2 - 1 - 16th - down - begin - begin - - - - - - - C - 5 - - 2 - 1 - 16th - down - continue - continue - - - - D - 5 - - 2 - 1 - 16th - down - continue - continue - - - - E - -1 - 5 - - 2 - 1 - 16th - flat - down - end - end - - - - - - - B - 4 - - 2 - 1 - 16th - down - begin - begin - - - - - - - C - 5 - - 2 - 1 - 16th - down - continue - continue - - - - D - 5 - - 2 - 1 - 16th - down - continue - continue - - - - E - -1 - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - B - 4 - - 2 - 1 - 16th - down - begin - begin - - - - - - - C - 5 - - 2 - 1 - 16th - down - continue - continue - - - - D - 5 - - 2 - 1 - 16th - down - continue - continue - - - - E - -1 - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - - - - - - - - - - - - - B - 4 - - 2 - 1 - 16th - down - begin - begin - - - - - - - - - 1 - - - - C - 5 - - 2 - 1 - 16th - down - continue - continue - - - - D - 5 - - 2 - 1 - 16th - down - continue - continue - - - - E - -1 - 5 - - 2 - 1 - 16th - flat - down - end - end - - - - - - - B - 4 - - 2 - 1 - 16th - down - begin - begin - - - - - - - C - 5 - - 2 - 1 - 16th - down - continue - continue - - - - D - 5 - - 2 - 1 - 16th - down - continue - continue - - - - E - -1 - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - B - 4 - - 2 - 1 - 16th - down - begin - begin - - - - - - - C - 5 - - 2 - 1 - 16th - down - continue - continue - - - - D - 5 - - 2 - 1 - 16th - down - continue - continue - - - - E - -1 - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - - - -1 - - - - - - - - - - - - - - - - F - 5 - - 2 - 1 - 16th - down - - - - 2 - 1 - 16th - - - - 4 - 1 - eighth - - - - 8 - 1 - quarter - - - - 8 - 1 - quarter - - - - - - - 73 - - - - - - - - 32 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - pizz. - - 16 - - - 46 - - - - - - - - - - 16 - - - - - 8 - 1 - quarter - - - - 8 - 1 - quarter - - - - B - -1 - 3 - - 8 - 1 - quarter - flat - up - - - - - - - - 8 - 1 - quarter - - - - B - -1 - 3 - - 8 - 1 - quarter - flat - up - - - - B - -1 - 3 - - 8 - 1 - quarter - up - - - - - - - - - - - 16 - 1 - - - - - - - - - - - - - - - 8 - - - - - - - - - 24 - - - - - 8 - 1 - quarter - - - - B - -1 - 3 - - 8 - 1 - quarter - flat - up - - - - 8 - 1 - quarter - - - - B - -1 - 3 - - 8 - 1 - quarter - up - - - - - - - - - - - - - - - - - - - - - - - 8 - - - - - - - - - 16 - - - - - B - -1 - 3 - - 8 - 1 - quarter - flat - up - - - - B - -1 - 3 - - 8 - 1 - quarter - up - - - - B - -1 - 3 - - 8 - 1 - quarter - up - - - - - - - - - 108 - - - - - -

    - - - 16 - - - - - arco - - 16 - - - 49 - - - - - - 16 - 1 - half - - - - E - 4 - - 2 - 1 - 16th - up - begin - begin - - - - - - - - - - F - 4 - - 2 - 1 - 16th - up - continue - continue - - - - E - 4 - - 2 - 1 - 16th - up - continue - continue - - - - F - 4 - - 2 - 1 - 16th - up - end - end - - - - E - 4 - - 2 - 1 - 16th - up - begin - begin - - - - F - 4 - - 2 - 1 - 16th - up - continue - continue - - - - G - 4 - - 2 - 1 - 16th - up - continue - continue - - - - A - -1 - 4 - - 2 - 1 - 16th - flat - up - end - end - - - - - - - - - - - - - 1 - - - - E - 4 - - 2 - 1 - 16th - up - begin - begin - - - - - - - F - 4 - - 2 - 1 - 16th - up - continue - continue - - - - E - 4 - - 2 - 1 - 16th - up - continue - continue - - - - F - 4 - - 2 - 1 - 16th - up - end - end - - - - E - 4 - - 2 - 1 - 16th - up - begin - begin - - - - F - 4 - - 2 - 1 - 16th - up - continue - continue - - - - G - 4 - - 2 - 1 - 16th - up - continue - continue - - - - A - -1 - 4 - - 2 - 1 - 16th - flat - up - end - end - - - - - - - E - 4 - - 2 - 1 - 16th - up - begin - begin - - - - - - - F - 4 - - 2 - 1 - 16th - up - continue - continue - - - - E - 4 - - 2 - 1 - 16th - up - continue - continue - - - - F - 4 - - 2 - 1 - 16th - up - end - end - - - - E - 4 - - 2 - 1 - 16th - up - begin - begin - - - - F - 4 - - 2 - 1 - 16th - up - continue - continue - - - - G - 4 - - 2 - 1 - 16th - up - continue - continue - - - - A - -1 - 4 - - 2 - 1 - 16th - up - end - end - - - - - - - - - -1 - - - - - - - - - - - - - - - - - - - B - -1 - 4 - - 2 - 1 - 16th - flat - down - begin - begin - - - - - - - - - - - - C - -1 - 5 - - 2 - 1 - 16th - flat - down - continue - continue - - - - B - -1 - 4 - - 2 - 1 - 16th - down - continue - continue - - - - C - -1 - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - B - -1 - 4 - - 2 - 1 - 16th - down - begin - begin - - - - - - - C - -1 - 5 - - 2 - 1 - 16th - down - continue - continue - - - - B - -1 - 4 - - 2 - 1 - 16th - down - continue - continue - - - - A - -1 - 4 - - 2 - 1 - 16th - flat - down - end - end - - - - - - - G - 4 - - 2 - 1 - 16th - up - begin - begin - - - - - - - A - -1 - 4 - - 2 - 1 - 16th - up - continue - continue - - - - G - 4 - - 2 - 1 - 16th - up - continue - continue - - - - - - 1 - - - - F - 4 - - 2 - 1 - 16th - up - end - end - - - - - - - - - - - -

    - - - - - - - E - 4 - - 2 - 1 - 16th - up - - - - 2 - 1 - 16th - - - - 4 - 1 - eighth - - - - 8 - 1 - quarter - - - - 8 - 1 - quarter - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - -

    - - - - - - - F - 1 - 4 - - 2 - 1 - 16th - sharp - up - begin - begin - - - - - - - - - - G - 4 - - 2 - 1 - 16th - up - continue - continue - - - - A - 4 - - 2 - 1 - 16th - up - continue - continue - - - - B - -1 - 4 - - 2 - 1 - 16th - flat - up - end - end - - - - - - - F - 1 - 4 - - 2 - 1 - 16th - up - begin - begin - - - - - - - G - 4 - - 2 - 1 - 16th - up - continue - continue - - - - A - 4 - - 2 - 1 - 16th - up - continue - continue - - - - B - -1 - 4 - - 2 - 1 - 16th - up - end - end - - - - - - - C - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - D - -1 - 5 - - 2 - 1 - 16th - flat - down - continue - continue - - - - E - -1 - 5 - - 2 - 1 - 16th - flat - down - continue - continue - - - - E - 5 - - 2 - 1 - 16th - natural - down - end - end - - - - - - - - - - - - - - F - 5 - - 2 - 1 - 16th - natural - down - begin - begin - - - - - - - G - -1 - 5 - - 2 - 1 - 16th - flat - down - continue - continue - - - - F - 5 - - 2 - 1 - 16th - down - continue - continue - - - - E - -1 - 5 - - 2 - 1 - 16th - flat - down - end - end - - - - - - - D - 5 - - 2 - 1 - 16th - natural - down - begin - begin - - - - - - - C - 5 - - 2 - 1 - 16th - down - continue - continue - - - - B - 4 - - 2 - 1 - 16th - natural - down - continue - continue - - - - A - 4 - - 2 - 1 - 16th - down - end - end - - - - - - - - - - - - - - - - - - - - - - A - 4 - - 2 - 1 - 16th - down - begin - begin - - - - - - - - - - B - -1 - 4 - - 2 - 1 - 16th - flat - down - continue - continue - - - - C - 5 - - 2 - 1 - 16th - down - continue - continue - - - - D - -1 - 5 - - 2 - 1 - 16th - flat - down - end - end - - - - - - - A - 4 - - 2 - 1 - 16th - down - begin - begin - - - - - - - B - -1 - 4 - - 2 - 1 - 16th - down - continue - continue - - - - C - 5 - - 2 - 1 - 16th - down - continue - continue - - - - C - 1 - 5 - - 2 - 1 - 16th - sharp - down - end - end - - - - - - - D - 1 - 5 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - E - 5 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 5 - - 2 - 1 - 16th - sharp - down - continue - continue - - - - G - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - - - - - - - - G - 1 - 5 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - A - 5 - - 2 - 1 - 16th - down - continue - continue - - - - G - 1 - 5 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 5 - - 2 - 1 - 16th - sharp - down - end - end - - - - - - - F - 5 - - 2 - 1 - 16th - natural - down - begin - begin - - - - - - - E - -1 - 5 - - 2 - 1 - 16th - flat - down - continue - continue - - - - D - 5 - - 2 - 1 - 16th - down - continue - continue - - - - C - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - - - - - - - - - - - - - - - - C - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - - - - C - 1 - 5 - - 2 - 1 - 16th - sharp - down - continue - continue - - - - D - 1 - 5 - - 2 - 1 - 16th - sharp - down - continue - continue - - - - E - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - F - 1 - 5 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - G - 5 - - 2 - 1 - 16th - down - continue - continue - - - - A - 5 - - 2 - 1 - 16th - down - continue - continue - - - - B - -1 - 5 - - 2 - 1 - 16th - flat - down - end - end - - - - - - - A - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - G - 5 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 5 - - 2 - 1 - 16th - down - continue - continue - - - - E - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - - - - - - - - - - - - - D - 1 - 5 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - - - - - - - - - E - 5 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 5 - - 2 - 1 - 16th - sharp - down - continue - continue - - - - G - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - A - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - B - -1 - 5 - - 2 - 1 - 16th - flat - down - continue - continue - - - - C - 6 - - 2 - 1 - 16th - down - continue - continue - - - - C - 1 - 6 - - 2 - 1 - 16th - sharp - down - end - end - - - - - - - D - 6 - - 2 - 1 - 16th - down - begin - begin - - - - - - - D - 1 - 6 - - 2 - 1 - 16th - sharp - down - continue - continue - - - - E - 6 - - 2 - 1 - 16th - down - continue - continue - - - - F - 6 - - 2 - 1 - 16th - natural - down - end - end - - - - - - - - - -1 - - - - - - - - - - - - - - - - F - 1 - 6 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - G - 6 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 6 - - 2 - 1 - 16th - down - continue - continue - - - - G - 6 - - 2 - 1 - 16th - down - end - end - - - - - - - F - 1 - 6 - - 2 - 1 - 16th - down - begin - begin - - - - - - - G - 6 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 6 - - 2 - 1 - 16th - down - continue - continue - - - - G - 6 - - 2 - 1 - 16th - down - end - end - - - - - - - F - 1 - 6 - - 2 - 1 - 16th - down - begin - begin - - - - - - - G - 6 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 6 - - 2 - 1 - 16th - down - continue - continue - - - - G - 6 - - 2 - 1 - 16th - down - end - end - - - - - - - - - - - - - - 16 - 1 - - - - - - - - - - - - - - - - - - - F - 1 - 6 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - G - 6 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 6 - - 2 - 1 - 16th - down - continue - continue - - - - G - 6 - - 2 - 1 - 16th - down - end - end - - - - - - - F - 1 - 6 - - 2 - 1 - 16th - down - begin - begin - - - - - - - G - 6 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 6 - - 2 - 1 - 16th - down - continue - continue - - - - G - 6 - - 2 - 1 - 16th - down - end - end - - - - - - - 8 - 1 - quarter - - - - - - - - none - - - 8 - - 0 - major - - - - C - 3 - - - 108 - - - - - -

    - - - - - - - B - 3 - - 2 - 1 - 16th - up - begin - begin - - - - - - - - - - C - 4 - - 2 - 1 - 16th - up - continue - continue - - - - B - 3 - - 2 - 1 - 16th - up - continue - continue - - - - C - 4 - - 2 - 1 - 16th - up - end - end - - - - B - 3 - - 2 - 1 - 16th - up - begin - begin - - - - C - 4 - - 2 - 1 - 16th - up - continue - continue - - - - B - 3 - - 2 - 1 - 16th - up - continue - continue - - - - C - 4 - - 2 - 1 - 16th - up - end - end - - - - B - 3 - - 2 - 1 - 16th - up - begin - begin - - - - C - 4 - - 2 - 1 - 16th - up - continue - continue - - - - B - 3 - - 2 - 1 - 16th - up - continue - continue - - - - C - 4 - - 2 - 1 - 16th - up - end - end - - - - - - - - - - - - - - B - 3 - - 2 - 1 - 16th - up - begin - begin - - - - - - - C - 4 - - 2 - 1 - 16th - up - continue - continue - - - - B - 3 - - 2 - 1 - 16th - up - continue - continue - - - - C - 4 - - 2 - 1 - 16th - up - end - end - - - - B - 3 - - 2 - 1 - 16th - down - begin - begin - - - - C - 4 - - 2 - 1 - 16th - down - continue - continue - - - - D - 4 - - 2 - 1 - 16th - down - continue - continue - - - - E - -1 - 4 - - 2 - 1 - 16th - flat - down - end - end - - - - - - - B - 3 - - 2 - 1 - 16th - up - begin - begin - - - - - - - C - 4 - - 2 - 1 - 16th - up - continue - continue - - - - B - 3 - - 2 - 1 - 16th - up - continue - continue - - - - C - 4 - - 2 - 1 - 16th - up - end - end - - - - B - 3 - - 2 - 1 - 16th - down - begin - begin - - - - C - 4 - - 2 - 1 - 16th - down - continue - continue - - - - D - 4 - - 2 - 1 - 16th - down - continue - continue - - - - E - -1 - 4 - - 2 - 1 - 16th - down - end - end - - - - - - - - - - - F - 4 - - 2 - 1 - 16th - down - begin - begin - - - - - - - G - -1 - 4 - - 2 - 1 - 16th - flat - down - continue - continue - - - - F - 4 - - 2 - 1 - 16th - down - continue - continue - - - - G - -1 - 4 - - 2 - 1 - 16th - down - end - end - - - - F - 4 - - 2 - 1 - 16th - down - begin - begin - - - - G - -1 - 4 - - 2 - 1 - 16th - down - continue - continue - - - - F - 4 - - 2 - 1 - 16th - down - continue - continue - - - - G - -1 - 4 - - 2 - 1 - 16th - down - end - end - - - - - - - F - 4 - - 2 - 1 - 16th - down - begin - begin - - - - - - - G - -1 - 4 - - 2 - 1 - 16th - down - continue - continue - - - - F - 4 - - 2 - 1 - 16th - down - continue - continue - - - - E - -1 - 4 - - 2 - 1 - 16th - flat - down - end - end - - - - D - 4 - - 2 - 1 - 16th - down - begin - begin - - - - E - -1 - 4 - - 2 - 1 - 16th - down - continue - continue - - - - D - 4 - - 2 - 1 - 16th - down - continue - continue - - - - C - 4 - - 2 - 1 - 16th - down - end - end - - - - - - - - - - - - - - B - 3 - - 2 - 1 - 16th - up - begin - begin - - - - - - - - - - C - 4 - - 2 - 1 - 16th - up - continue - continue - - - - B - 3 - - 2 - 1 - 16th - up - continue - continue - - - - C - 4 - - 2 - 1 - 16th - up - end - end - - - - B - 3 - - 2 - 1 - 16th - up - begin - begin - - - - C - 4 - - 2 - 1 - 16th - up - continue - continue - - - - B - 3 - - 2 - 1 - 16th - up - continue - continue - - - - C - 4 - - 2 - 1 - 16th - up - end - end - - - - B - 3 - - 2 - 1 - 16th - up - begin - begin - - - - C - 4 - - 2 - 1 - 16th - up - continue - continue - - - - B - 3 - - 2 - 1 - 16th - up - continue - continue - - - - C - 4 - - 2 - 1 - 16th - up - end - end - - - - - - - - - - - - - - B - 3 - - 2 - 1 - 16th - up - begin - begin - - - - - - - C - 4 - - 2 - 1 - 16th - up - continue - continue - - - - B - 3 - - 2 - 1 - 16th - up - continue - continue - - - - C - 4 - - 2 - 1 - 16th - up - end - end - - - - B - 3 - - 2 - 1 - 16th - down - begin - begin - - - - C - 4 - - 2 - 1 - 16th - down - continue - continue - - - - D - 4 - - 2 - 1 - 16th - down - continue - continue - - - - E - -1 - 4 - - 2 - 1 - 16th - flat - down - end - end - - - - - - - - - - - - - - F - 4 - - 2 - 1 - 16th - down - begin - begin - - - - - - - G - -1 - 4 - - 2 - 1 - 16th - flat - down - continue - continue - - - - F - 4 - - 2 - 1 - 16th - down - continue - continue - - - - G - -1 - 4 - - 2 - 1 - 16th - down - end - end - - - - F - 4 - - 2 - 1 - 16th - down - begin - begin - - - - G - -1 - 4 - - 2 - 1 - 16th - down - continue - continue - - - - F - 4 - - 2 - 1 - 16th - down - continue - continue - - - - G - -1 - 4 - - 2 - 1 - 16th - down - end - end - - - - - - - F - 4 - - 2 - 1 - 16th - down - begin - begin - - - - - - - G - -1 - 4 - - 2 - 1 - 16th - down - continue - continue - - - - F - 4 - - 2 - 1 - 16th - down - continue - continue - - - - E - -1 - 4 - - 2 - 1 - 16th - flat - down - end - end - - - - D - 4 - - 2 - 1 - 16th - down - begin - begin - - - - E - -1 - 4 - - 2 - 1 - 16th - down - continue - continue - - - - D - 4 - - 2 - 1 - 16th - down - continue - continue - - - - C - 4 - - 2 - 1 - 16th - down - end - end - - - - - - - - - - - - - - B - 3 - - 2 - 1 - 16th - up - begin - begin - - - - - - - - - - C - 4 - - 2 - 1 - 16th - up - continue - continue - - - - B - 3 - - 2 - 1 - 16th - up - continue - continue - - - - C - 4 - - 2 - 1 - 16th - up - end - end - - - - B - 3 - - 2 - 1 - 16th - down - begin - begin - - - - C - 4 - - 2 - 1 - 16th - down - continue - continue - - - - D - 4 - - 2 - 1 - 16th - down - continue - continue - - - - E - -1 - 4 - - 2 - 1 - 16th - flat - down - end - end - - - - B - 3 - - 2 - 1 - 16th - up - begin - begin - - - - C - 4 - - 2 - 1 - 16th - up - continue - continue - - - - B - 3 - - 2 - 1 - 16th - up - continue - continue - - - - C - 4 - - 2 - 1 - 16th - up - end - end - - - - - - - - - - - - - - - - - - - B - 3 - - 2 - 1 - 16th - up - begin - begin - - - - - - - C - 4 - - 2 - 1 - 16th - up - continue - continue - - - - B - 3 - - 2 - 1 - 16th - up - continue - continue - - - - C - 4 - - 2 - 1 - 16th - up - end - end - - - - B - 3 - - 2 - 1 - 16th - down - begin - begin - - - - C - 4 - - 2 - 1 - 16th - down - continue - continue - - - - D - 4 - - 2 - 1 - 16th - down - continue - continue - - - - E - -1 - 4 - - 2 - 1 - 16th - flat - down - end - end - - - - - - - - - -1 - - - - - - - - - 100 - - - - - - - - - - - - - F - 4 - - 2 - 1 - 16th - down - begin - begin - - - - - - - - - - - - G - -1 - 4 - - 2 - 1 - 16th - flat - down - continue - continue - - - - F - 4 - - 2 - 1 - 16th - down - continue - continue - - - - G - -1 - 4 - - 2 - 1 - 16th - down - end - end - - - - F - 4 - - 2 - 1 - 16th - down - begin - begin - - - - G - -1 - 4 - - 2 - 1 - 16th - down - continue - continue - - - - F - 4 - - 2 - 1 - 16th - down - continue - continue - - - - G - -1 - 4 - - 2 - 1 - 16th - down - end - end - - - - - - - F - 4 - - 2 - 1 - 16th - down - begin - begin - - - - - - - G - -1 - 4 - - 2 - 1 - 16th - down - continue - continue - - - - F - 4 - - 2 - 1 - 16th - down - continue - continue - - - - E - -1 - 4 - - 2 - 1 - 16th - flat - down - end - end - - - - D - 4 - - 2 - 1 - 16th - down - begin - begin - - - - E - -1 - 4 - - 2 - 1 - 16th - down - continue - continue - - - - D - 4 - - 2 - 1 - 16th - down - continue - continue - - - - C - 4 - - 2 - 1 - 16th - down - end - end - - - - - - - - - -1 - - - - - - - - - - - -

    - - - - - - - B - 3 - - 2 - 1 - 16th - up - begin - begin - - - - - - - C - 4 - - 2 - 1 - 16th - up - continue - continue - - - - B - 3 - - 2 - 1 - 16th - up - continue - continue - - - - C - 4 - - 2 - 1 - 16th - up - end - end - - - - B - 3 - - 2 - 1 - 16th - down - begin - begin - - - - C - 4 - - 2 - 1 - 16th - down - continue - continue - - - - D - 4 - - 2 - 1 - 16th - down - continue - continue - - - - E - -1 - 4 - - 2 - 1 - 16th - flat - down - end - end - - - - - - - B - 3 - - 2 - 1 - 16th - up - begin - begin - - - - - - - C - 4 - - 2 - 1 - 16th - up - continue - continue - - - - B - 3 - - 2 - 1 - 16th - up - continue - continue - - - - C - 4 - - 2 - 1 - 16th - up - end - end - - - - - - - - - - - B - 3 - - 2 - 1 - 16th - down - begin - begin - - - - - - - - - - C - 4 - - 2 - 1 - 16th - down - continue - continue - - - - D - 4 - - 2 - 1 - 16th - down - continue - continue - - - - E - -1 - 4 - - 2 - 1 - 16th - flat - down - end - end - - - - B - 3 - - 2 - 1 - 16th - down - begin - begin - - - - C - 4 - - 2 - 1 - 16th - down - continue - continue - - - - D - 4 - - 2 - 1 - 16th - down - continue - continue - - - - E - -1 - 4 - - 2 - 1 - 16th - down - end - end - - - - B - 3 - - 2 - 1 - 16th - up - begin - begin - - - - C - 4 - - 2 - 1 - 16th - up - continue - continue - - - - B - 3 - - 2 - 1 - 16th - up - continue - continue - - - - C - 4 - - 2 - 1 - 16th - up - end - end - - - - - - - - - - - - - - - - 1 - - - - B - 3 - - 2 - 1 - 16th - up - begin - begin - - - - - - - C - 4 - - 2 - 1 - 16th - up - continue - continue - - - - B - 3 - - 2 - 1 - 16th - up - continue - continue - - - - C - 4 - - 2 - 1 - 16th - up - end - end - - - - B - 3 - - 2 - 1 - 16th - down - begin - begin - - - - C - 4 - - 2 - 1 - 16th - down - continue - continue - - - - D - 4 - - 2 - 1 - 16th - down - continue - continue - - - - E - -1 - 4 - - 2 - 1 - 16th - flat - down - end - end - - - - - - - - - -1 - - - - - - - - - - - - - - - - - - - F - 4 - - 2 - 1 - 16th - down - begin - begin - - - - - - - - - - - - G - -1 - 4 - - 2 - 1 - 16th - flat - down - continue - continue - - - - F - 4 - - 2 - 1 - 16th - down - continue - continue - - - - G - -1 - 4 - - 2 - 1 - 16th - down - end - end - - - - F - 4 - - 2 - 1 - 16th - down - begin - begin - - - - G - -1 - 4 - - 2 - 1 - 16th - down - continue - continue - - - - F - 4 - - 2 - 1 - 16th - down - continue - continue - - - - G - -1 - 4 - - 2 - 1 - 16th - down - end - end - - - - - - - F - 4 - - 2 - 1 - 16th - down - begin - begin - - - - - - - G - -1 - 4 - - 2 - 1 - 16th - down - continue - continue - - - - F - 4 - - 2 - 1 - 16th - down - continue - continue - - - - E - -1 - 4 - - 2 - 1 - 16th - flat - down - end - end - - - - D - 4 - - 2 - 1 - 16th - down - begin - begin - - - - E - -1 - 4 - - 2 - 1 - 16th - down - continue - continue - - - - D - 4 - - 2 - 1 - 16th - down - continue - continue - - - - - - 1 - - - - C - 4 - - 2 - 1 - 16th - down - end - end - - - - - - - - - - - - - - - - - - - - - - -

    - - - 16 - - - - - B - 3 - - 2 - 1 - 16th - down - begin - begin - - - - - - - C - 4 - - 2 - 1 - 16th - down - continue - continue - - - - D - 4 - - 2 - 1 - 16th - down - continue - continue - - - - E - -1 - 4 - - 2 - 1 - 16th - flat - down - end - end - - - - B - 3 - - 2 - 1 - 16th - up - begin - begin - - - - C - 4 - - 2 - 1 - 16th - up - continue - continue - - - - B - 3 - - 2 - 1 - 16th - up - continue - continue - - - - C - 4 - - 2 - 1 - 16th - up - end - end - - - - - - - B - 3 - - 2 - 1 - 16th - up - begin - begin - - - - - - - - - - C - 4 - - 2 - 1 - 16th - up - continue - continue - - - - B - 3 - - 2 - 1 - 16th - up - continue - continue - - - - C - 4 - - 2 - 1 - 16th - up - end - end - - - - - - - - - - - - - - - 8 - - - - - - - - - 16 - - - - - B - 3 - - 2 - 1 - 16th - down - begin - begin - - - - - - - C - 4 - - 2 - 1 - 16th - down - continue - continue - - - - D - 4 - - 2 - 1 - 16th - down - continue - continue - - - - E - -1 - 4 - - 2 - 1 - 16th - flat - down - end - end - - - - - - - B - 3 - - 2 - 1 - 16th - down - begin - begin - - - - - - - C - 4 - - 2 - 1 - 16th - down - continue - continue - - - - D - 4 - - 2 - 1 - 16th - down - continue - continue - - - - E - -1 - 4 - - 2 - 1 - 16th - down - end - end - - - - - - - B - 3 - - 2 - 1 - 16th - down - begin - begin - - - - - - - C - 4 - - 2 - 1 - 16th - down - continue - continue - - - - D - 4 - - 2 - 1 - 16th - down - continue - continue - - - - E - -1 - 4 - - 2 - 1 - 16th - down - end - end - - - - - - - - - - - - - - - - - - - B - 3 - - 2 - 1 - 16th - down - begin - begin - - - - - - - - - 1 - - - - C - 4 - - 2 - 1 - 16th - down - continue - continue - - - - D - 4 - - 2 - 1 - 16th - down - continue - continue - - - - E - -1 - 4 - - 2 - 1 - 16th - flat - down - end - end - - - - - - - B - 3 - - 2 - 1 - 16th - down - begin - begin - - - - - - - C - 4 - - 2 - 1 - 16th - down - continue - continue - - - - D - 4 - - 2 - 1 - 16th - down - continue - continue - - - - E - -1 - 4 - - 2 - 1 - 16th - down - end - end - - - - - - - B - 3 - - 2 - 1 - 16th - down - begin - begin - - - - - - - C - 4 - - 2 - 1 - 16th - down - continue - continue - - - - D - 4 - - 2 - 1 - 16th - down - continue - continue - - - - E - -1 - 4 - - 2 - 1 - 16th - down - end - end - - - - - - - - - -1 - - - - - - - - - - - - - - - - F - 4 - - 2 - 1 - 16th - down - - - - 2 - 1 - 16th - - - - 4 - 1 - eighth - - - - 8 - 1 - quarter - - - - 8 - 1 - quarter - - - - - - - 84 - - - - - - - - 32 - 1 - - - - - - - - - - - pizz. - - 8 - - - 46 - - - - - - - - - - 8 - - - - - 8 - 1 - quarter - - - - E - 3 - - 8 - 1 - quarter - up - - - - 8 - 1 - quarter - - - - - - - - 8 - 1 - quarter - - - - E - 3 - - 8 - 1 - quarter - up - - - - 8 - 1 - quarter - - - - - - - - E - 3 - - 8 - 1 - quarter - up - - - - E - 3 - - 8 - 1 - quarter - up - - - - 8 - 1 - quarter - - - - - - - - E - 3 - - 8 - 1 - quarter - up - - - - 8 - 1 - quarter - - - - 8 - 1 - quarter - - - - - - - - - - - - - - - - - - - E - 3 - - 8 - 1 - quarter - up - - - - E - 3 - - 8 - 1 - quarter - up - - - - - - - - - - - - - - - 16 - - - - - E - 3 - - 8 - 1 - quarter - up - - - - 8 - 1 - quarter - - - - E - 3 - - 8 - 1 - quarter - up - - - - 8 - 1 - quarter - - - - - - - - - - - 24 - 1 - - - - - - - - - 108 - - - - - arco - - - - 50 - - - - - - -

    - - - - - - - E - 3 - - 2 - 1 - 16th - up - begin - begin - - - - - - - - - - F - 3 - - 2 - 1 - 16th - up - continue - continue - - - - E - 3 - - 2 - 1 - 16th - up - continue - continue - - - - F - 3 - - 2 - 1 - 16th - up - end - end - - - - E - 3 - - 2 - 1 - 16th - up - begin - begin - - - - F - 3 - - 2 - 1 - 16th - up - continue - continue - - - - G - 3 - - 2 - 1 - 16th - up - continue - continue - - - - A - -1 - 3 - - 2 - 1 - 16th - flat - up - end - end - - - - - - - E - 3 - - 2 - 1 - 16th - up - begin - begin - - - - - - - F - 3 - - 2 - 1 - 16th - up - continue - continue - - - - E - 3 - - 2 - 1 - 16th - up - continue - continue - - - - F - 3 - - 2 - 1 - 16th - up - end - end - - - - E - 3 - - 2 - 1 - 16th - up - begin - begin - - - - F - 3 - - 2 - 1 - 16th - up - continue - continue - - - - G - 3 - - 2 - 1 - 16th - up - continue - continue - - - - A - -1 - 3 - - 2 - 1 - 16th - up - end - end - - - - - - - - - - - - - 1 - - - - E - 3 - - 2 - 1 - 16th - up - begin - begin - - - - - - - F - 3 - - 2 - 1 - 16th - up - continue - continue - - - - E - 3 - - 2 - 1 - 16th - up - continue - continue - - - - F - 3 - - 2 - 1 - 16th - up - end - end - - - - E - 3 - - 2 - 1 - 16th - up - begin - begin - - - - F - 3 - - 2 - 1 - 16th - up - continue - continue - - - - G - 3 - - 2 - 1 - 16th - up - continue - continue - - - - A - -1 - 3 - - 2 - 1 - 16th - flat - up - end - end - - - - - - - E - 3 - - 2 - 1 - 16th - up - begin - begin - - - - - - - F - 3 - - 2 - 1 - 16th - up - continue - continue - - - - E - 3 - - 2 - 1 - 16th - up - continue - continue - - - - F - 3 - - 2 - 1 - 16th - up - end - end - - - - E - 3 - - 2 - 1 - 16th - up - begin - begin - - - - F - 3 - - 2 - 1 - 16th - up - continue - continue - - - - G - 3 - - 2 - 1 - 16th - up - continue - continue - - - - A - -1 - 3 - - 2 - 1 - 16th - up - end - end - - - - - - - - - -1 - - - - - - - - - - - - - - - - - - - B - -1 - 3 - - 2 - 1 - 16th - flat - up - begin - begin - - - - - - - - - - - - C - -1 - 4 - - 2 - 1 - 16th - flat - up - continue - continue - - - - B - -1 - 3 - - 2 - 1 - 16th - up - continue - continue - - - - C - -1 - 4 - - 2 - 1 - 16th - up - end - end - - - - - - - B - -1 - 3 - - 2 - 1 - 16th - up - begin - begin - - - - - - - C - -1 - 4 - - 2 - 1 - 16th - up - continue - continue - - - - B - -1 - 3 - - 2 - 1 - 16th - up - continue - continue - - - - A - -1 - 3 - - 2 - 1 - 16th - flat - up - end - end - - - - - - - G - 3 - - 2 - 1 - 16th - up - begin - begin - - - - - - - A - -1 - 3 - - 2 - 1 - 16th - up - continue - continue - - - - G - 3 - - 2 - 1 - 16th - up - continue - continue - - - - - - 1 - - - - F - 3 - - 2 - 1 - 16th - up - end - end - - - - - - - - - - - -

    - - - - - - - E - 3 - - 2 - 1 - 16th - up - - - - 2 - 1 - 16th - - - - 4 - 1 - eighth - - - - 8 - 1 - quarter - - - - 8 - 1 - quarter - - - - - - - - - - - - - - - - pizz. - - - - 46 - - - - - - F - 3 - - 8 - 1 - quarter - up - - - - F - 3 - - 8 - 1 - quarter - up - - - - 8 - 1 - quarter - - - - - - - - 8 - 1 - quarter - - - - F - 3 - - 8 - 1 - quarter - up - - - - 8 - 1 - quarter - - - - - - - - F - 3 - - 8 - 1 - quarter - up - - - - 8 - 1 - quarter - - - - F - 3 - - 8 - 1 - quarter - up - - - - - - - - F - 1 - 3 - - 8 - 1 - quarter - sharp - up - - - - 8 - 1 - quarter - - - - 8 - 1 - quarter - - - - - - - - - - - 16 - 1 - - - - - - - - - - - - - - - - - - - arco - - - - 50 - - - - - - A - 3 - - 2 - 1 - 16th - up - begin - begin - - - - - - - - - - B - -1 - 3 - - 2 - 1 - 16th - flat - up - continue - continue - - - - C - 4 - - 2 - 1 - 16th - up - continue - continue - - - - D - -1 - 4 - - 2 - 1 - 16th - flat - up - end - end - - - - - - - A - 3 - - 2 - 1 - 16th - up - begin - begin - - - - - - - B - -1 - 3 - - 2 - 1 - 16th - up - continue - continue - - - - C - 4 - - 2 - 1 - 16th - up - continue - continue - - - - C - 1 - 4 - - 2 - 1 - 16th - sharp - up - end - end - - - - - - - D - 1 - 4 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - E - 4 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 4 - - 2 - 1 - 16th - sharp - down - continue - continue - - - - G - 4 - - 2 - 1 - 16th - down - end - end - - - - - - - - - - - - - - G - 1 - 4 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - A - 4 - - 2 - 1 - 16th - down - continue - continue - - - - G - 1 - 4 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 4 - - 2 - 1 - 16th - sharp - down - end - end - - - - - - - F - 4 - - 2 - 1 - 16th - natural - down - begin - begin - - - - - - - E - -1 - 4 - - 2 - 1 - 16th - flat - down - continue - continue - - - - D - 4 - - 2 - 1 - 16th - down - continue - continue - - - - C - 4 - - 2 - 1 - 16th - down - end - end - - - - - - - - - - - - - - - - - - - - - - C - 4 - - 2 - 1 - 16th - down - begin - begin - - - - - - - - - - C - 1 - 4 - - 2 - 1 - 16th - sharp - down - continue - continue - - - - D - 1 - 4 - - 2 - 1 - 16th - sharp - down - continue - continue - - - - E - 4 - - 2 - 1 - 16th - down - end - end - - - - - - - F - 1 - 4 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - G - 4 - - 2 - 1 - 16th - down - continue - continue - - - - A - 4 - - 2 - 1 - 16th - down - continue - continue - - - - B - -1 - 4 - - 2 - 1 - 16th - flat - down - end - end - - - - - - - A - 4 - - 2 - 1 - 16th - down - begin - begin - - - - - - - G - 4 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 4 - - 2 - 1 - 16th - down - continue - continue - - - - E - 4 - - 2 - 1 - 16th - down - end - end - - - - - - - - - - - - - - - - - - - D - 1 - 4 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - - - - - - - - - E - 4 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 4 - - 2 - 1 - 16th - sharp - down - continue - continue - - - - G - 4 - - 2 - 1 - 16th - down - end - end - - - - - - - A - 4 - - 2 - 1 - 16th - down - begin - begin - - - - - - - B - -1 - 4 - - 2 - 1 - 16th - flat - down - continue - continue - - - - C - 5 - - 2 - 1 - 16th - down - continue - continue - - - - C - 1 - 5 - - 2 - 1 - 16th - sharp - down - end - end - - - - - - - D - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - D - 1 - 5 - - 2 - 1 - 16th - sharp - down - continue - continue - - - - E - 5 - - 2 - 1 - 16th - down - continue - continue - - - - F - 5 - - 2 - 1 - 16th - natural - down - end - end - - - - - - - - - -1 - - - - - - - - - - - - - - - - F - 1 - 5 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - G - 5 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 5 - - 2 - 1 - 16th - down - continue - continue - - - - G - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - F - 1 - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - G - 5 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 5 - - 2 - 1 - 16th - down - continue - continue - - - - G - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - F - 1 - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - G - 5 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 5 - - 2 - 1 - 16th - down - continue - continue - - - - G - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - - - - - - - - 16 - 1 - - - - - - - - - - - - - - - - - - - F - 1 - 5 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - G - 5 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 5 - - 2 - 1 - 16th - down - continue - continue - - - - G - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - F - 1 - 5 - - 2 - 1 - 16th - down - begin - begin - - - - - - - G - 5 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 5 - - 2 - 1 - 16th - down - continue - continue - - - - G - 5 - - 2 - 1 - 16th - down - end - end - - - - - - - 8 - 1 - quarter - - - - - - - - none - - - 8 - - 0 - major - - - - F - 4 - - - 108 - - - - - 24 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - 32 - 1 - - - - - - - - - - - - - - - 8 - - - - - 8 - 1 - quarter - - - - F - 2 - - 16 - - 1 - half - up - - - - - - - - - - - - - - F - 2 - - 16 - - 1 - half - up - - - - - - - - - - - - - - 8 - 1 - quarter - - - - E - 2 - - 24 - 1 - half - - up - - - - - - - - - - - 8 - 1 - quarter - - - - F - 2 - - 16 - - 1 - half - up - - - - - - - - - - - - - - F - 2 - - 16 - - 1 - half - up - - - - - - - - - - - - 100 - - - - - 8 - 1 - quarter - - - - E - 2 - - 24 - 1 - half - - up - - - - - - - - - - - 24 - 1 - - - - - - - - pizz. - - 8 - - - 46 - - - - - - - - - - 8 - - - - - 8 - 1 - quarter - - - - F - 2 - - 8 - 1 - quarter - up - - - - - - - - - 8 - 1 - quarter - - - - - - - - - - - 16 - 1 - - - - - - - - - - - 8 - 1 - quarter - - - - E - 2 - - 8 - 1 - quarter - up - - - - - - - - - 16 - 1 - half - - - - - - - - - - - 24 - 1 - - - - - - - - - - - - 16 - - - - - arco - - 16 - - - 50 - - - - - - 8 - 1 - quarter - - - - 8 - 1 - quarter - - - - B - 2 - - 2 - 1 - 16th - up - begin - begin - - - - - - - - - - C - 3 - - 2 - 1 - 16th - up - continue - continue - - - - D - 3 - - 2 - 1 - 16th - up - continue - continue - - - - E - -1 - 3 - - 2 - 1 - 16th - flat - up - end - end - - - - - - - - - - - - - - - - - - - B - 2 - - 2 - 1 - 16th - up - begin - begin - - - - - - - - - 1 - - - - C - 3 - - 2 - 1 - 16th - up - continue - continue - - - - D - 3 - - 2 - 1 - 16th - up - continue - continue - - - - E - -1 - 3 - - 2 - 1 - 16th - flat - up - end - end - - - - - - - B - 2 - - 2 - 1 - 16th - up - begin - begin - - - - - - - C - 3 - - 2 - 1 - 16th - up - continue - continue - - - - D - 3 - - 2 - 1 - 16th - up - continue - continue - - - - E - -1 - 3 - - 2 - 1 - 16th - up - end - end - - - - - - - B - 2 - - 2 - 1 - 16th - up - begin - begin - - - - - - - C - 3 - - 2 - 1 - 16th - up - continue - continue - - - - D - 3 - - 2 - 1 - 16th - up - continue - continue - - - - E - -1 - 3 - - 2 - 1 - 16th - up - end - end - - - - - - - - - -1 - - - - - - - - - - - - - - - - F - 3 - - 24 - 1 - half - - down - - - - - - - - - - - - - - - 76 - - - - - - - - - - 9 - - - - E - 2 - - 32 - - 1 - whole - - - - - - - - - - - - - - - - - - - - -

    - - - - - - - - - - - - E - 2 - - 24 - - - 1 - half - - up - - - - - - - - - - - - E - 2 - - 24 - - - 1 - half - - up - - - - - - - - - - - - E - 2 - - 24 - - - 1 - half - - up - - - - - - - - - - - - E - 2 - - 24 - - - 1 - half - - up - - - - - - - - - - - - - - - E - 2 - - 16 - - 1 - half - up - - - - - - - - - - - - - - -

    - - - - - - - E - 2 - - 32 - 1 - whole - - - - - - - - - - - -29 - - - - - - -1 - - - - - - - - - - - - - - - - - - - E - 2 - - 24 - - 1 - half - - up - - - - - - - - - - - - -22 - - - - - - -1 - - - - - - - - - 108 - - - - - -

    - - - - - - - E - 2 - - 32 - - 1 - whole - - - - - - - - - - - -

    - - - - - - - E - 2 - - 32 - 1 - whole - - - - - - - - - - - -28 - - - - - - -1 - - - - - - - - - - - - - - - - - - - E - 2 - - 24 - 1 - half - - up - - - - - - - - - - - -22 - - - - - - -1 - - - - - - - - 24 - 1 - - - - - - - - - - - - - - - - pizz. - - - - 46 - - - - - - F - 2 - - 8 - 1 - quarter - up - - - - F - 2 - - 8 - 1 - quarter - up - - - - 8 - 1 - quarter - - - - - - - - 8 - 1 - quarter - - - - F - 2 - - 8 - 1 - quarter - up - - - - 8 - 1 - quarter - - - - - - - - F - 2 - - 8 - 1 - quarter - up - - - - 8 - 1 - quarter - - - - F - 2 - - 8 - 1 - quarter - up - - - - - - - - F - 1 - 2 - - 8 - 1 - quarter - sharp - up - - - - 8 - 1 - quarter - - - - 8 - 1 - quarter - - - - - - - - - - - 16 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - - - - - - - - - arco - - - - 50 - - - - - - C - 3 - - 2 - 1 - 16th - up - begin - begin - - - - - - - - - - C - 1 - 3 - - 2 - 1 - 16th - sharp - up - continue - continue - - - - D - 1 - 3 - - 2 - 1 - 16th - sharp - up - continue - continue - - - - E - 3 - - 2 - 1 - 16th - up - end - end - - - - - - - F - 1 - 3 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - G - 3 - - 2 - 1 - 16th - down - continue - continue - - - - A - 3 - - 2 - 1 - 16th - down - continue - continue - - - - B - -1 - 3 - - 2 - 1 - 16th - flat - down - end - end - - - - - - - A - 3 - - 2 - 1 - 16th - down - begin - begin - - - - - - - G - 3 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 3 - - 2 - 1 - 16th - down - continue - continue - - - - E - 3 - - 2 - 1 - 16th - down - end - end - - - - - - - - - - - - - - - - - - - D - 1 - 3 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - - - - - - - - - E - 3 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 3 - - 2 - 1 - 16th - sharp - down - continue - continue - - - - G - 3 - - 2 - 1 - 16th - down - end - end - - - - - - - A - 3 - - 2 - 1 - 16th - down - begin - begin - - - - - - - B - -1 - 3 - - 2 - 1 - 16th - flat - down - continue - continue - - - - C - 4 - - 2 - 1 - 16th - down - continue - continue - - - - C - 1 - 4 - - 2 - 1 - 16th - sharp - down - end - end - - - - - - - D - 4 - - 2 - 1 - 16th - down - begin - begin - - - - - - - D - 1 - 4 - - 2 - 1 - 16th - sharp - down - continue - continue - - - - E - 4 - - 2 - 1 - 16th - down - continue - continue - - - - F - 4 - - 2 - 1 - 16th - natural - down - end - end - - - - - - - - - -1 - - - - - - - - - - - - - - - - F - 1 - 4 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - G - 4 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 4 - - 2 - 1 - 16th - down - continue - continue - - - - G - 4 - - 2 - 1 - 16th - down - end - end - - - - - - - F - 1 - 4 - - 2 - 1 - 16th - down - begin - begin - - - - - - - G - 4 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 4 - - 2 - 1 - 16th - down - continue - continue - - - - G - 4 - - 2 - 1 - 16th - down - end - end - - - - - - - F - 1 - 4 - - 2 - 1 - 16th - down - begin - begin - - - - - - - G - 4 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 4 - - 2 - 1 - 16th - down - continue - continue - - - - G - 4 - - 2 - 1 - 16th - down - end - end - - - - - - - - - - - - - - 16 - 1 - - - - - - - - - - - - - - - - - - - F - 1 - 4 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - G - 4 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 4 - - 2 - 1 - 16th - down - continue - continue - - - - G - 4 - - 2 - 1 - 16th - down - end - end - - - - - - - F - 1 - 4 - - 2 - 1 - 16th - down - begin - begin - - - - - - - G - 4 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 4 - - 2 - 1 - 16th - down - continue - continue - - - - G - 4 - - 2 - 1 - 16th - down - end - end - - - - - - - 8 - 1 - quarter - - - - - - - - none - - - 8 - - 0 - major - - - - F - 4 - - - 108 - - - 0 - 0 - -1 - - - - - 24 - 1 - - - - - - - - - - - 32 - 1 - - - - - - - - 32 - 1 - - - - - - - - - - - - - - - 8 - - - - - 8 - 1 - quarter - - - - F - 2 - - 16 - - 1 - half - up - - - - - - - - - - - - - - F - 2 - - 16 - - 1 - half - up - - - - - - - - - - - - - - 8 - 1 - quarter - - - - E - 2 - - 24 - 1 - half - - up - - - - - - - - - - - 8 - 1 - quarter - - - - F - 2 - - 16 - - 1 - half - up - - - - - - - - - - - - - - F - 2 - - 16 - - 1 - half - up - - - - - - - - - - - - 100 - - - - - 8 - 1 - quarter - - - - E - 2 - - 24 - 1 - half - - up - - - - - - - - - - - 24 - 1 - - - - - - - - pizz. - - 8 - - - 46 - - - - - - - - - - 8 - - - - - 8 - 1 - quarter - - - - F - 2 - - 8 - 1 - quarter - up - - - - - - - - - 8 - 1 - quarter - - - - - - - - - - - 16 - 1 - - - - - - - - - - - 8 - 1 - quarter - - - - E - 2 - - 8 - 1 - quarter - up - - - - - - - - - 16 - 1 - half - - - - - - - - - - - 24 - 1 - - - - - - - - 24 - 1 - - - - - - - - - - - - - - - - arco - - - - 50 - - - - - - B - 2 - - 2 - 1 - 16th - up - begin - begin - - - - - - - - - - - - 1 - - - - C - 3 - - 2 - 1 - 16th - up - continue - continue - - - - D - 3 - - 2 - 1 - 16th - up - continue - continue - - - - E - -1 - 3 - - 2 - 1 - 16th - flat - up - end - end - - - - - - - B - 2 - - 2 - 1 - 16th - up - begin - begin - - - - - - - C - 3 - - 2 - 1 - 16th - up - continue - continue - - - - D - 3 - - 2 - 1 - 16th - up - continue - continue - - - - E - -1 - 3 - - 2 - 1 - 16th - up - end - end - - - - - - - B - 2 - - 2 - 1 - 16th - up - begin - begin - - - - - - - C - 3 - - 2 - 1 - 16th - up - continue - continue - - - - D - 3 - - 2 - 1 - 16th - up - continue - continue - - - - E - -1 - 3 - - 2 - 1 - 16th - up - end - end - - - - - - - - - -1 - - - - - - - - - - - - - - - - F - 3 - - 24 - 1 - half - - down - - - - - - - - - - - - - - - 91 - - - - - - - - - - 9 - - - - E - 2 - - 32 - - 1 - whole - - - - - - - - - - - - - - - - - - - - -

    - - - - - - - - - - - - E - 2 - - 24 - - - 1 - half - - up - - - - - - - - - - - - E - 2 - - 24 - - - 1 - half - - up - - - - - - - - - - - - E - 2 - - 24 - - - 1 - half - - up - - - - - - - - - - - - E - 2 - - 24 - - - 1 - half - - up - - - - - - - - - - - - - - - E - 2 - - 16 - - 1 - half - up - - - - - - - - - - - - - - -

    - - - - - - - E - 2 - - 32 - 1 - whole - - - - - - - - - - - -29 - - - - - - -1 - - - - - - - - - - - - - - - - - - - E - 2 - - 24 - - 1 - half - - up - - - - - - - - - - - - -22 - - - - - - - - - - - - - - 108 - - - - - -

    - - - - - - - E - 2 - - 32 - - 1 - whole - - - - - - - - - - - -

    - - - - - - - E - 2 - - 32 - 1 - whole - - - - - - - - - - - -29 - - - - - - -1 - - - - - - - - - - - - - - - - - - - E - 2 - - 24 - 1 - half - - up - - - - - - - - - - - -22 - - - - - - -1 - - - - - - - - 24 - 1 - - - - - - - - - - - - - - - - pizz. - - - - 46 - - - - - - F - 2 - - 8 - 1 - quarter - up - - - - F - 2 - - 8 - 1 - quarter - up - - - - 8 - 1 - quarter - - - - - - - - 8 - 1 - quarter - - - - F - 2 - - 8 - 1 - quarter - up - - - - 8 - 1 - quarter - - - - - - - - F - 2 - - 8 - 1 - quarter - up - - - - 8 - 1 - quarter - - - - F - 2 - - 8 - 1 - quarter - up - - - - - - - - F - 1 - 2 - - 8 - 1 - quarter - sharp - up - - - - 8 - 1 - quarter - - - - 8 - 1 - quarter - - - - - - - - - - - 16 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - - - - 16 - 1 - - - - - - - - - - - 24 - 1 - - - - - - - - - - - - - - - - arco - - - - 50 - - - - - - D - 1 - 3 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - - - - - - - - - E - 3 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 3 - - 2 - 1 - 16th - sharp - down - continue - continue - - - - G - 3 - - 2 - 1 - 16th - down - end - end - - - - - - - A - 3 - - 2 - 1 - 16th - down - begin - begin - - - - - - - B - -1 - 3 - - 2 - 1 - 16th - flat - down - continue - continue - - - - C - 4 - - 2 - 1 - 16th - down - continue - continue - - - - C - 1 - 4 - - 2 - 1 - 16th - sharp - down - end - end - - - - - - - D - 4 - - 2 - 1 - 16th - down - begin - begin - - - - - - - D - 1 - 4 - - 2 - 1 - 16th - sharp - down - continue - continue - - - - E - 4 - - 2 - 1 - 16th - down - continue - continue - - - - F - 4 - - 2 - 1 - 16th - natural - down - end - end - - - - - - - - - -1 - - - - - - - - - - - - - - - - F - 1 - 4 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - G - 4 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 4 - - 2 - 1 - 16th - down - continue - continue - - - - G - 4 - - 2 - 1 - 16th - down - end - end - - - - - - - F - 1 - 4 - - 2 - 1 - 16th - down - begin - begin - - - - - - - G - 4 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 4 - - 2 - 1 - 16th - down - continue - continue - - - - G - 4 - - 2 - 1 - 16th - down - end - end - - - - - - - F - 1 - 4 - - 2 - 1 - 16th - down - begin - begin - - - - - - - G - 4 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 4 - - 2 - 1 - 16th - down - continue - continue - - - - G - 4 - - 2 - 1 - 16th - down - end - end - - - - - - - - - - - - - - 16 - 1 - - - - - - - - - - - - - - - - - - - F - 1 - 4 - - 2 - 1 - 16th - sharp - down - begin - begin - - - - - - - G - 4 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 4 - - 2 - 1 - 16th - down - continue - continue - - - - G - 4 - - 2 - 1 - 16th - down - end - end - - - - - - - F - 1 - 4 - - 2 - 1 - 16th - down - begin - begin - - - - - - - G - 4 - - 2 - 1 - 16th - down - continue - continue - - - - F - 1 - 4 - - 2 - 1 - 16th - down - continue - continue - - - - G - 4 - - 2 - 1 - 16th - down - end - end - - - - - - - 8 - 1 - quarter - - - - - diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/MozaChloSample.xml b/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/MozaChloSample.xml deleted file mode 100644 index 4b16f48a7..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/MozaChloSample.xml and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/MozaVeilSample.xml b/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/MozaVeilSample.xml deleted file mode 100644 index f1bf58405..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/MozaVeilSample.xml and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/01a-Pitches-Pitches.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/01a-Pitches-Pitches.gpx deleted file mode 100644 index 82676905f..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/01a-Pitches-Pitches.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/01b-Pitches-Intervals.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/01b-Pitches-Intervals.gpx deleted file mode 100644 index 818cdfa82..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/01b-Pitches-Intervals.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/01c-Pitches-NoVoiceElement.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/01c-Pitches-NoVoiceElement.gpx deleted file mode 100644 index e775ecd3b..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/01c-Pitches-NoVoiceElement.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/01d-Pitches-Microtones.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/01d-Pitches-Microtones.gpx deleted file mode 100644 index 35845600a..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/01d-Pitches-Microtones.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/01e-Pitches-ParenthesizedAccidentals.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/01e-Pitches-ParenthesizedAccidentals.gpx deleted file mode 100644 index b7078c885..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/01e-Pitches-ParenthesizedAccidentals.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/01f-Pitches-ParenthesizedMicrotoneAccidentals.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/01f-Pitches-ParenthesizedMicrotoneAccidentals.gpx deleted file mode 100644 index 738bd2344..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/01f-Pitches-ParenthesizedMicrotoneAccidentals.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/02a-Rests-Durations.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/02a-Rests-Durations.gpx deleted file mode 100644 index 742f5f6f9..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/02a-Rests-Durations.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/02b-Rests-PitchedRests.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/02b-Rests-PitchedRests.gpx deleted file mode 100644 index 57f864f10..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/02b-Rests-PitchedRests.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/02c-Rests-MultiMeasureRests.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/02c-Rests-MultiMeasureRests.gpx deleted file mode 100644 index 113f3fd84..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/02c-Rests-MultiMeasureRests.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/02d-Rests-Multimeasure-TimeSignatures.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/02d-Rests-Multimeasure-TimeSignatures.gpx deleted file mode 100644 index 8761b4c2d..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/02d-Rests-Multimeasure-TimeSignatures.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/02e-Rests-NoType.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/02e-Rests-NoType.gpx deleted file mode 100644 index d94ac9f25..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/02e-Rests-NoType.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/03a-Rhythm-Durations.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/03a-Rhythm-Durations.gpx deleted file mode 100644 index 1d568dc47..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/03a-Rhythm-Durations.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/03b-Rhythm-Backup.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/03b-Rhythm-Backup.gpx deleted file mode 100644 index 27651439e..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/03b-Rhythm-Backup.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/03c-Rhythm-DivisionChange.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/03c-Rhythm-DivisionChange.gpx deleted file mode 100644 index 003cb83a9..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/03c-Rhythm-DivisionChange.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/03d-Rhythm-DottedDurations-Factors.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/03d-Rhythm-DottedDurations-Factors.gpx deleted file mode 100644 index 7f5f8ee8b..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/03d-Rhythm-DottedDurations-Factors.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/11a-TimeSignatures.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/11a-TimeSignatures.gpx deleted file mode 100644 index 3a0dcf13b..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/11a-TimeSignatures.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/11b-TimeSignatures-NoTime.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/11b-TimeSignatures-NoTime.gpx deleted file mode 100644 index 5f4c6f42f..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/11b-TimeSignatures-NoTime.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/11c-TimeSignatures-CompoundSimple.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/11c-TimeSignatures-CompoundSimple.gpx deleted file mode 100644 index 72acec1c8..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/11c-TimeSignatures-CompoundSimple.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/11d-TimeSignatures-CompoundMultiple.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/11d-TimeSignatures-CompoundMultiple.gpx deleted file mode 100644 index 5f0b6d0ee..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/11d-TimeSignatures-CompoundMultiple.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/11e-TimeSignatures-CompoundMixed.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/11e-TimeSignatures-CompoundMixed.gpx deleted file mode 100644 index fdb1d6668..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/11e-TimeSignatures-CompoundMixed.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/11f-TimeSignatures-SymbolMeaning.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/11f-TimeSignatures-SymbolMeaning.gpx deleted file mode 100644 index 70e130057..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/11f-TimeSignatures-SymbolMeaning.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/11g-TimeSignatures-SingleNumber.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/11g-TimeSignatures-SingleNumber.gpx deleted file mode 100644 index 3bad8f600..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/11g-TimeSignatures-SingleNumber.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/11h-TimeSignatures-SenzaMisura.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/11h-TimeSignatures-SenzaMisura.gpx deleted file mode 100644 index de73213b8..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/11h-TimeSignatures-SenzaMisura.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/12a-Clefs.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/12a-Clefs.gpx deleted file mode 100644 index bf4e89b60..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/12a-Clefs.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/12b-Clefs-NoKeyOrClef.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/12b-Clefs-NoKeyOrClef.gpx deleted file mode 100644 index 819b01538..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/12b-Clefs-NoKeyOrClef.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/13a-KeySignatures.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/13a-KeySignatures.gpx deleted file mode 100644 index 6771987fc..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/13a-KeySignatures.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/13b-KeySignatures-ChurchModes.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/13b-KeySignatures-ChurchModes.gpx deleted file mode 100644 index 3b5b39cae..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/13b-KeySignatures-ChurchModes.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/13c-KeySignatures-NonTraditional.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/13c-KeySignatures-NonTraditional.gpx deleted file mode 100644 index 11845c270..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/13c-KeySignatures-NonTraditional.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/13d-KeySignatures-Microtones.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/13d-KeySignatures-Microtones.gpx deleted file mode 100644 index 94ee22e03..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/13d-KeySignatures-Microtones.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/14a-StaffDetails-LineChanges.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/14a-StaffDetails-LineChanges.gpx deleted file mode 100644 index a53302815..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/14a-StaffDetails-LineChanges.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/21a-Chord-Basic.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/21a-Chord-Basic.gpx deleted file mode 100644 index 615382edc..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/21a-Chord-Basic.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/21b-Chords-TwoNotes.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/21b-Chords-TwoNotes.gpx deleted file mode 100644 index d4c657d60..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/21b-Chords-TwoNotes.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/21c-Chords-ThreeNotesDuration.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/21c-Chords-ThreeNotesDuration.gpx deleted file mode 100644 index ca99c81a2..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/21c-Chords-ThreeNotesDuration.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/21d-Chords-SchubertStabatMater.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/21d-Chords-SchubertStabatMater.gpx deleted file mode 100644 index 43a86c869..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/21d-Chords-SchubertStabatMater.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/21e-Chords-PickupMeasures.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/21e-Chords-PickupMeasures.gpx deleted file mode 100644 index 12a54e7a4..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/21e-Chords-PickupMeasures.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/21f-Chord-ElementInBetween.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/21f-Chord-ElementInBetween.gpx deleted file mode 100644 index 3edf1ded1..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/21f-Chord-ElementInBetween.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/22a-Noteheads.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/22a-Noteheads.gpx deleted file mode 100644 index 87fc143ce..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/22a-Noteheads.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/22b-Staff-Notestyles.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/22b-Staff-Notestyles.gpx deleted file mode 100644 index b8830f9cf..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/22b-Staff-Notestyles.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/22c-Noteheads-Chords.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/22c-Noteheads-Chords.gpx deleted file mode 100644 index 77ee1ac6e..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/22c-Noteheads-Chords.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/22d-Parenthesized-Noteheads.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/22d-Parenthesized-Noteheads.gpx deleted file mode 100644 index 46701de9e..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/22d-Parenthesized-Noteheads.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/23a-Tuplets.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/23a-Tuplets.gpx deleted file mode 100644 index 007388d25..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/23a-Tuplets.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/23b-Tuplets-Styles.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/23b-Tuplets-Styles.gpx deleted file mode 100644 index 6bd3a8f0a..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/23b-Tuplets-Styles.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/23c-Tuplet-Display-NonStandard.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/23c-Tuplet-Display-NonStandard.gpx deleted file mode 100644 index 1eb28569a..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/23c-Tuplet-Display-NonStandard.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/23d-Tuplets-Nested.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/23d-Tuplets-Nested.gpx deleted file mode 100644 index a6ef74915..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/23d-Tuplets-Nested.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/23e-Tuplets-Tremolo.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/23e-Tuplets-Tremolo.gpx deleted file mode 100644 index a05a4f9d5..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/23e-Tuplets-Tremolo.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/23f-Tuplets-DurationButNoBracket.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/23f-Tuplets-DurationButNoBracket.gpx deleted file mode 100644 index cc755c4fb..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/23f-Tuplets-DurationButNoBracket.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/24a-GraceNotes.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/24a-GraceNotes.gpx deleted file mode 100644 index 49e5a720d..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/24a-GraceNotes.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/24b-ChordAsGraceNote.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/24b-ChordAsGraceNote.gpx deleted file mode 100644 index d6a3bc363..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/24b-ChordAsGraceNote.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/24c-GraceNote-MeasureEnd.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/24c-GraceNote-MeasureEnd.gpx deleted file mode 100644 index 13fe3378e..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/24c-GraceNote-MeasureEnd.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/24d-AfterGrace.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/24d-AfterGrace.gpx deleted file mode 100644 index 0d8f83352..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/24d-AfterGrace.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/24e-GraceNote-StaffChange.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/24e-GraceNote-StaffChange.gpx deleted file mode 100644 index bbb2846fa..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/24e-GraceNote-StaffChange.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/24f-GraceNote-Slur.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/24f-GraceNote-Slur.gpx deleted file mode 100644 index 982c4a481..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/24f-GraceNote-Slur.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/31a-Directions.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/31a-Directions.gpx deleted file mode 100644 index 93511e119..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/31a-Directions.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/31c-MetronomeMarks.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/31c-MetronomeMarks.gpx deleted file mode 100644 index fef0e1222..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/31c-MetronomeMarks.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/32a-Notations.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/32a-Notations.gpx deleted file mode 100644 index 4efa34934..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/32a-Notations.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/32b-Articulations-Texts.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/32b-Articulations-Texts.gpx deleted file mode 100644 index 084566917..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/32b-Articulations-Texts.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/32c-MultipleNotationChildren.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/32c-MultipleNotationChildren.gpx deleted file mode 100644 index 0eb2cb936..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/32c-MultipleNotationChildren.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/32d-Arpeggio.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/32d-Arpeggio.gpx deleted file mode 100644 index 7b3eba1ee..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/32d-Arpeggio.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33a-Spanners.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33a-Spanners.gpx deleted file mode 100644 index 80f4c13f3..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33a-Spanners.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33b-Spanners-Tie.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33b-Spanners-Tie.gpx deleted file mode 100644 index fc2c8f2a4..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33b-Spanners-Tie.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33c-Spanners-Slurs.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33c-Spanners-Slurs.gpx deleted file mode 100644 index 50025f88b..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33c-Spanners-Slurs.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33d-Spanners-OctaveShifts.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33d-Spanners-OctaveShifts.gpx deleted file mode 100644 index c01023aee..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33d-Spanners-OctaveShifts.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33e-Spanners-OctaveShifts-InvalidSize.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33e-Spanners-OctaveShifts-InvalidSize.gpx deleted file mode 100644 index e6b3f65b2..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33e-Spanners-OctaveShifts-InvalidSize.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33f-Trill-EndingOnGraceNote.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33f-Trill-EndingOnGraceNote.gpx deleted file mode 100644 index 469a84e20..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33f-Trill-EndingOnGraceNote.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33g-Slur-ChordedNotes.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33g-Slur-ChordedNotes.gpx deleted file mode 100644 index 75fa7ec0f..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33g-Slur-ChordedNotes.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33h-Spanners-Glissando.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33h-Spanners-Glissando.gpx deleted file mode 100644 index 0f71d4b62..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33h-Spanners-Glissando.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33i-Ties-NotEnded.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33i-Ties-NotEnded.gpx deleted file mode 100644 index 3dd820cba..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33i-Ties-NotEnded.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41a-MultiParts-Partorder.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41a-MultiParts-Partorder.gpx deleted file mode 100644 index 75060c951..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41a-MultiParts-Partorder.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41b-MultiParts-MoreThan10.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41b-MultiParts-MoreThan10.gpx deleted file mode 100644 index c586b8109..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41b-MultiParts-MoreThan10.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41c-StaffGroups.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41c-StaffGroups.gpx deleted file mode 100644 index 4a720cec1..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41c-StaffGroups.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41d-StaffGroups-Nested.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41d-StaffGroups-Nested.gpx deleted file mode 100644 index 4691f6626..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41d-StaffGroups-Nested.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41e-StaffGroups-InstrumentNames-Linebroken.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41e-StaffGroups-InstrumentNames-Linebroken.gpx deleted file mode 100644 index b147be875..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41e-StaffGroups-InstrumentNames-Linebroken.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41f-StaffGroups-Overlapping.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41f-StaffGroups-Overlapping.gpx deleted file mode 100644 index 28dd8f065..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41f-StaffGroups-Overlapping.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41g-PartNoId.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41g-PartNoId.gpx deleted file mode 100644 index e084efb25..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41g-PartNoId.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41h-TooManyParts.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41h-TooManyParts.gpx deleted file mode 100644 index d2410cde3..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41h-TooManyParts.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41i-PartNameDisplay-Override.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41i-PartNameDisplay-Override.gpx deleted file mode 100644 index 0aa76205e..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41i-PartNameDisplay-Override.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/42a-MultiVoice-TwoVoicesOnStaff-Lyrics.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/42a-MultiVoice-TwoVoicesOnStaff-Lyrics.gpx deleted file mode 100644 index 31bb85786..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/42a-MultiVoice-TwoVoicesOnStaff-Lyrics.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/42b-MultiVoice-MidMeasureClefChange.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/42b-MultiVoice-MidMeasureClefChange.gpx deleted file mode 100644 index 6a5062cd2..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/42b-MultiVoice-MidMeasureClefChange.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/43a-PianoStaff.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/43a-PianoStaff.gpx deleted file mode 100644 index f40df78ee..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/43a-PianoStaff.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/43b-MultiStaff-DifferentKeys.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/43b-MultiStaff-DifferentKeys.gpx deleted file mode 100644 index f40df78ee..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/43b-MultiStaff-DifferentKeys.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/43c-MultiStaff-DifferentKeysAfterBackup.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/43c-MultiStaff-DifferentKeysAfterBackup.gpx deleted file mode 100644 index f40df78ee..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/43c-MultiStaff-DifferentKeysAfterBackup.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/43d-MultiStaff-StaffChange.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/43d-MultiStaff-StaffChange.gpx deleted file mode 100644 index 14434d661..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/43d-MultiStaff-StaffChange.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/43e-Multistaff-ClefDynamics.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/43e-Multistaff-ClefDynamics.gpx deleted file mode 100644 index 589db09f1..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/43e-Multistaff-ClefDynamics.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/45a-SimpleRepeat.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/45a-SimpleRepeat.gpx deleted file mode 100644 index dc9d7cbbc..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/45a-SimpleRepeat.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/45b-RepeatWithAlternatives.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/45b-RepeatWithAlternatives.gpx deleted file mode 100644 index 3a767cef1..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/45b-RepeatWithAlternatives.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/45c-RepeatMultipleTimes.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/45c-RepeatMultipleTimes.gpx deleted file mode 100644 index 6c62a50cd..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/45c-RepeatMultipleTimes.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/45d-Repeats-Nested-Alternatives.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/45d-Repeats-Nested-Alternatives.gpx deleted file mode 100644 index 2f4e11bea..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/45d-Repeats-Nested-Alternatives.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/45e-Repeats-Nested-Alternatives.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/45e-Repeats-Nested-Alternatives.gpx deleted file mode 100644 index 666be5c2f..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/45e-Repeats-Nested-Alternatives.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/45f-Repeats-InvalidEndings.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/45f-Repeats-InvalidEndings.gpx deleted file mode 100644 index ac90c779a..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/45f-Repeats-InvalidEndings.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/45g-Repeats-NotEnded.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/45g-Repeats-NotEnded.gpx deleted file mode 100644 index a44988538..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/45g-Repeats-NotEnded.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/46a-Barlines.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/46a-Barlines.gpx deleted file mode 100644 index 7c97197f2..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/46a-Barlines.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/46b-MidmeasureBarline.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/46b-MidmeasureBarline.gpx deleted file mode 100644 index 0184f364e..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/46b-MidmeasureBarline.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/46c-Midmeasure-Clef.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/46c-Midmeasure-Clef.gpx deleted file mode 100644 index 92402e870..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/46c-Midmeasure-Clef.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/46d-PickupMeasure-ImplicitMeasures.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/46d-PickupMeasure-ImplicitMeasures.gpx deleted file mode 100644 index 863570b54..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/46d-PickupMeasure-ImplicitMeasures.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/46e-PickupMeasure-SecondVoiceStartsLater.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/46e-PickupMeasure-SecondVoiceStartsLater.gpx deleted file mode 100644 index 4a013418d..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/46e-PickupMeasure-SecondVoiceStartsLater.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/46f-IncompleteMeasures.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/46f-IncompleteMeasures.gpx deleted file mode 100644 index 2b9ff972c..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/46f-IncompleteMeasures.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/46g-PickupMeasure-Chordnames-FiguredBass.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/46g-PickupMeasure-Chordnames-FiguredBass.gpx deleted file mode 100644 index 868e18d6d..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/46g-PickupMeasure-Chordnames-FiguredBass.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/51b-Header-Quotes.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/51b-Header-Quotes.gpx deleted file mode 100644 index cf2e93e22..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/51b-Header-Quotes.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/51c-MultipleRights.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/51c-MultipleRights.gpx deleted file mode 100644 index 3f395c1b4..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/51c-MultipleRights.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/51d-EmptyTitle.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/51d-EmptyTitle.gpx deleted file mode 100644 index fefa58217..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/51d-EmptyTitle.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/52a-PageLayout.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/52a-PageLayout.gpx deleted file mode 100644 index a231c4fb0..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/52a-PageLayout.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/52b-Breaks.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/52b-Breaks.gpx deleted file mode 100644 index 6d8d5f271..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/52b-Breaks.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61a-Lyrics.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61a-Lyrics.gpx deleted file mode 100644 index 3096c483e..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61a-Lyrics.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61b-MultipleLyrics.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61b-MultipleLyrics.gpx deleted file mode 100644 index 915548518..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61b-MultipleLyrics.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61c-Lyrics-Pianostaff.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61c-Lyrics-Pianostaff.gpx deleted file mode 100644 index 5acfe6629..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61c-Lyrics-Pianostaff.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61d-Lyrics-Melisma.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61d-Lyrics-Melisma.gpx deleted file mode 100644 index 8a0704851..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61d-Lyrics-Melisma.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61e-Lyrics-Chords.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61e-Lyrics-Chords.gpx deleted file mode 100644 index 534cfa0cf..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61e-Lyrics-Chords.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61f-Lyrics-GracedNotes.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61f-Lyrics-GracedNotes.gpx deleted file mode 100644 index 615a08b48..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61f-Lyrics-GracedNotes.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61g-Lyrics-NameNumber.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61g-Lyrics-NameNumber.gpx deleted file mode 100644 index ffc9de059..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61g-Lyrics-NameNumber.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61h-Lyrics-BeamsMelismata.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61h-Lyrics-BeamsMelismata.gpx deleted file mode 100644 index 94a9a2abb..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61h-Lyrics-BeamsMelismata.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61i-Lyrics-Chords.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61i-Lyrics-Chords.gpx deleted file mode 100644 index d67f19715..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61i-Lyrics-Chords.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61j-Lyrics-Elisions.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61j-Lyrics-Elisions.gpx deleted file mode 100644 index 4ff023138..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61j-Lyrics-Elisions.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61k-Lyrics-SpannersExtenders.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61k-Lyrics-SpannersExtenders.gpx deleted file mode 100644 index a456ed6f5..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61k-Lyrics-SpannersExtenders.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/71a-Chordnames.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/71a-Chordnames.gpx deleted file mode 100644 index 850ecf4a4..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/71a-Chordnames.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/71c-ChordsFrets.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/71c-ChordsFrets.gpx deleted file mode 100644 index e70599bf0..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/71c-ChordsFrets.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/71d-ChordsFrets-Multistaff.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/71d-ChordsFrets-Multistaff.gpx deleted file mode 100644 index 37b1b29b4..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/71d-ChordsFrets-Multistaff.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/71e-TabStaves.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/71e-TabStaves.gpx deleted file mode 100644 index aafe2cdbe..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/71e-TabStaves.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/71f-AllChordTypes.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/71f-AllChordTypes.gpx deleted file mode 100644 index e403356ef..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/71f-AllChordTypes.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/71g-MultipleChordnames.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/71g-MultipleChordnames.gpx deleted file mode 100644 index f03200ac6..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/71g-MultipleChordnames.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/72a-TransposingInstruments.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/72a-TransposingInstruments.gpx deleted file mode 100644 index 3145d2a8b..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/72a-TransposingInstruments.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/72b-TransposingInstruments-Full.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/72b-TransposingInstruments-Full.gpx deleted file mode 100644 index b45886ad1..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/72b-TransposingInstruments-Full.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/72c-TransposingInstruments-Change.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/72c-TransposingInstruments-Change.gpx deleted file mode 100644 index 24db39e49..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/72c-TransposingInstruments-Change.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/73a-Percussion.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/73a-Percussion.gpx deleted file mode 100644 index c9260f76b..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/73a-Percussion.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/74a-FiguredBass.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/74a-FiguredBass.gpx deleted file mode 100644 index 748eae434..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/74a-FiguredBass.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/75a-AccordionRegistrations.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/75a-AccordionRegistrations.gpx deleted file mode 100644 index f6a55c2fb..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/75a-AccordionRegistrations.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/99a-Sibelius5-IgnoreBeaming.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/99a-Sibelius5-IgnoreBeaming.gpx deleted file mode 100644 index dea76aceb..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/99a-Sibelius5-IgnoreBeaming.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/99b-Lyrics-BeamsMelismata-IgnoreBeams.gpx b/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/99b-Lyrics-BeamsMelismata-IgnoreBeams.gpx deleted file mode 100644 index 94a9a2abb..000000000 Binary files a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/99b-Lyrics-BeamsMelismata-IgnoreBeams.gpx and /dev/null differ diff --git a/Source/AlphaTab.Test/Xml/XmlParseTest.cs b/Source/AlphaTab.Test/Xml/XmlParseTest.cs deleted file mode 100644 index 8c684ff30..000000000 --- a/Source/AlphaTab.Test/Xml/XmlParseTest.cs +++ /dev/null @@ -1,145 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using AlphaTab.Xml; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace AlphaTab.Test.Xml -{ - [TestClass] - public class XmlParseTest - { - [TestMethod] - public void ParseSimple() - { - var s = ""; - var xml = new XmlDocument(s); - Assert.IsNotNull(xml.DocumentElement); - Assert.AreEqual("root", xml.DocumentElement.LocalName); - Assert.AreEqual(0, xml.DocumentElement.ChildNodes.Count); - } - - [TestMethod] - public void ParseShorthand() - { - var s = ""; - var xml = new XmlDocument(s); - Assert.IsNotNull(xml.DocumentElement); - Assert.AreEqual("root", xml.DocumentElement.LocalName); - Assert.AreEqual(0, xml.DocumentElement.ChildNodes.Count); - } - - [TestMethod] - public void ParseSingleAttribute() - { - var s = ""; - var xml = new XmlDocument(s); - Assert.IsNotNull(xml.DocumentElement); - Assert.AreEqual("root", xml.DocumentElement.LocalName); - Assert.AreEqual("v", xml.DocumentElement.GetAttribute("att")); - Assert.AreEqual(0, xml.DocumentElement.ChildNodes.Count); - } - - [TestMethod] - public void ParseMultipleAttributes() - { - var s = ""; - var xml = new XmlDocument(s); - Assert.IsNotNull(xml.DocumentElement); - Assert.AreEqual("root", xml.DocumentElement.LocalName); - Assert.AreEqual("v", xml.DocumentElement.GetAttribute("att")); - Assert.AreEqual("v2", xml.DocumentElement.GetAttribute("att2")); - Assert.AreEqual(0, xml.DocumentElement.ChildNodes.Count); - } - - [TestMethod] - public void ParseSimpleText() - { - var s = "Text"; - var xml = new XmlDocument(s); - Assert.IsNotNull(xml.DocumentElement); - Assert.AreEqual("root", xml.DocumentElement.LocalName); - Assert.AreEqual(1, xml.DocumentElement.ChildNodes.Count); - Assert.AreEqual(XmlNodeType.Text, xml.DocumentElement.ChildNodes[0].NodeType); - Assert.AreEqual("Text", xml.DocumentElement.ChildNodes[0].Value); - } - - [TestMethod] - public void ParseChild() - { - var s = ""; - var xml = new XmlDocument(s); - Assert.IsNotNull(xml.DocumentElement); - Assert.AreEqual("root", xml.DocumentElement.LocalName); - Assert.AreEqual(1, xml.DocumentElement.ChildNodes.Count); - Assert.AreEqual(XmlNodeType.Element, xml.DocumentElement.ChildNodes[0].NodeType); - Assert.AreEqual("cc", xml.DocumentElement.ChildNodes[0].LocalName); - } - - [TestMethod] - public void ParseMultiChild() - { - var s = ""; - var xml = new XmlDocument(s); - Assert.IsNotNull(xml.DocumentElement); - Assert.AreEqual("root", xml.DocumentElement.LocalName); - Assert.AreEqual(2, xml.DocumentElement.ChildNodes.Count); - Assert.AreEqual(XmlNodeType.Element, xml.DocumentElement.ChildNodes[0].NodeType); - Assert.AreEqual("cc", xml.DocumentElement.ChildNodes[0].LocalName); - Assert.AreEqual(XmlNodeType.Element, xml.DocumentElement.ChildNodes[1].NodeType); - Assert.AreEqual("cc", xml.DocumentElement.ChildNodes[1].LocalName); - } - - [TestMethod] - public void ParseComments() - { - var s = "value"; - var xml = new XmlDocument(s); - Assert.IsNotNull(xml.DocumentElement); - Assert.AreEqual("test", xml.DocumentElement.LocalName); - Assert.AreEqual(2, xml.DocumentElement.ChildNodes.Count); - Assert.AreEqual(XmlNodeType.Element, xml.DocumentElement.ChildNodes[0].NodeType); - Assert.AreEqual("cc", xml.DocumentElement.ChildNodes[0].LocalName); - Assert.AreEqual("d", xml.DocumentElement.ChildNodes[0].GetAttribute("c")); - Assert.AreEqual(XmlNodeType.Element, xml.DocumentElement.ChildNodes[1].NodeType); - Assert.AreEqual("cc", xml.DocumentElement.ChildNodes[1].LocalName); - Assert.AreEqual(1, xml.DocumentElement.ChildNodes[1].ChildNodes.Count); - Assert.AreEqual(XmlNodeType.Text, xml.DocumentElement.ChildNodes[1].ChildNodes[0].NodeType); - Assert.AreEqual("value", xml.DocumentElement.ChildNodes[1].ChildNodes[0].Value); - } - - [TestMethod] - public void ParseDoctype() - { - var s = ""; - var xml = new XmlDocument(s); - Assert.IsNotNull(xml.DocumentElement); - Assert.AreEqual("test", xml.DocumentElement.LocalName); - Assert.AreEqual(2, xml.DocumentElement.ChildNodes.Count); - Assert.AreEqual(XmlNodeType.Element, xml.DocumentElement.ChildNodes[0].NodeType); - Assert.AreEqual("cc", xml.DocumentElement.ChildNodes[0].LocalName); - Assert.AreEqual(XmlNodeType.Element, xml.DocumentElement.ChildNodes[1].NodeType); - Assert.AreEqual("cc", xml.DocumentElement.ChildNodes[1].LocalName); - } - - [TestMethod] - public void ParseXmlHeadTest() - { - var s = ""; - var xml = new XmlDocument(s); - Assert.IsNotNull(xml.DocumentElement); - Assert.AreEqual("root", xml.DocumentElement.LocalName); - } - - [TestMethod] - public void ParseFull() - { - var s = TestPlatform.LoadFileAsString("TestFiles\\Xml\\GPIF.xml"); - var xml = new XmlDocument(s); - Assert.IsNotNull(xml.DocumentElement); - } - } -} diff --git a/Source/AlphaTab/AlphaTab.Shared.projitems b/Source/AlphaTab/AlphaTab.Shared.projitems deleted file mode 100644 index 542cd61a0..000000000 --- a/Source/AlphaTab/AlphaTab.Shared.projitems +++ /dev/null @@ -1,325 +0,0 @@ - - - - $(MSBuildAllProjects);$(MSBuildThisFileFullPath) - true - 8083b3bc-0681-47f2-9fbf-a5c96107f538 - - - AlphaTab - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Source/AlphaTab/AlphaTab.Shared.shproj b/Source/AlphaTab/AlphaTab.Shared.shproj deleted file mode 100644 index 92f9bb022..000000000 --- a/Source/AlphaTab/AlphaTab.Shared.shproj +++ /dev/null @@ -1,12 +0,0 @@ - - - - 8083b3bc-0681-47f2-9fbf-a5c96107f538 - - - - - - - - diff --git a/Source/AlphaTab/AlphaTabException.cs b/Source/AlphaTab/AlphaTabException.cs deleted file mode 100644 index 9965ce3c0..000000000 --- a/Source/AlphaTab/AlphaTabException.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; - -namespace AlphaTab -{ - ///

    - /// The base class for all errors that can happen in alphaTab. - /// - public class AlphaTabException : Exception - { - /// - /// Initializes a new instance of the class. - /// - /// The message that describes the error. - public AlphaTabException(string message) : base(message) - { - } - } -} diff --git a/Source/AlphaTab/AssemblyInfo.cs b/Source/AlphaTab/AssemblyInfo.cs deleted file mode 100644 index 95a223216..000000000 --- a/Source/AlphaTab/AssemblyInfo.cs +++ /dev/null @@ -1,30 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright (c) 2017, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System.Reflection; - -[assembly: AssemblyTitle("AlphaTab")] -[assembly: AssemblyDescription("alphaTab is a cross platform music notation and guitar tablature rendering library.")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("AlphaTab")] -[assembly: AssemblyCopyright("Copyright © 2017, Daniel Kuschny and Contributors")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Source/AlphaTab/Audio/GeneralMidi.cs b/Source/AlphaTab/Audio/GeneralMidi.cs deleted file mode 100644 index 2fd8f31c1..000000000 --- a/Source/AlphaTab/Audio/GeneralMidi.cs +++ /dev/null @@ -1,177 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Collections; - -namespace AlphaTab.Audio -{ - /// - /// This public class provides names for all general midi instruments. - /// - class GeneralMidi - { - private static FastDictionary _values; - - public static int GetValue(string name) - { - if (_values == null) - { - _values = new FastDictionary(); - _values["acousticgrandpiano"] = 0; - _values["brightacousticpiano"] = 1; - _values["electricgrandpiano"] = 2; - _values["honkytonkpiano"] = 3; - _values["electricpiano1"] = 4; - _values["electricpiano2"] = 5; - _values["harpsichord"] = 6; - _values["clavinet"] = 7; - _values["celesta"] = 8; - _values["glockenspiel"] = 9; - _values["musicbox"] = 10; - _values["vibraphone"] = 11; - _values["marimba"] = 12; - _values["xylophone"] = 13; - _values["tubularbells"] = 14; - _values["dulcimer"] = 15; - _values["drawbarorgan"] = 16; - _values["percussiveorgan"] = 17; - _values["rockorgan"] = 18; - _values["churchorgan"] = 19; - _values["reedorgan"] = 20; - _values["accordion"] = 21; - _values["harmonica"] = 22; - _values["tangoaccordion"] = 23; - _values["acousticguitarnylon"] = 24; - _values["acousticguitarsteel"] = 25; - _values["electricguitarjazz"] = 26; - _values["electricguitarclean"] = 27; - _values["electricguitarmuted"] = 28; - _values["overdrivenguitar"] = 29; - _values["distortionguitar"] = 30; - _values["guitarharmonics"] = 31; - _values["acousticbass"] = 32; - _values["electricbassfinger"] = 33; - _values["electricbasspick"] = 34; - _values["fretlessbass"] = 35; - _values["slapbass1"] = 36; - _values["slapbass2"] = 37; - _values["synthbass1"] = 38; - _values["synthbass2"] = 39; - _values["violin"] = 40; - _values["viola"] = 41; - _values["cello"] = 42; - _values["contrabass"] = 43; - _values["tremolostrings"] = 44; - _values["pizzicatostrings"] = 45; - _values["orchestralharp"] = 46; - _values["timpani"] = 47; - _values["stringensemble1"] = 48; - _values["stringensemble2"] = 49; - _values["synthstrings1"] = 50; - _values["synthstrings2"] = 51; - _values["choiraahs"] = 52; - _values["voiceoohs"] = 53; - _values["synthvoice"] = 54; - _values["orchestrahit"] = 55; - _values["trumpet"] = 56; - _values["trombone"] = 57; - _values["tuba"] = 58; - _values["mutedtrumpet"] = 59; - _values["frenchhorn"] = 60; - _values["brasssection"] = 61; - _values["synthbrass1"] = 62; - _values["synthbrass2"] = 63; - _values["sopranosax"] = 64; - _values["altosax"] = 65; - _values["tenorsax"] = 66; - _values["baritonesax"] = 67; - _values["oboe"] = 68; - _values["englishhorn"] = 69; - _values["bassoon"] = 70; - _values["clarinet"] = 71; - _values["piccolo"] = 72; - _values["flute"] = 73; - _values["recorder"] = 74; - _values["panflute"] = 75; - _values["blownbottle"] = 76; - _values["shakuhachi"] = 77; - _values["whistle"] = 78; - _values["ocarina"] = 79; - _values["lead1square"] = 80; - _values["lead2sawtooth"] = 81; - _values["lead3calliope"] = 82; - _values["lead4chiff"] = 83; - _values["lead5charang"] = 84; - _values["lead6voice"] = 85; - _values["lead7fifths"] = 86; - _values["lead8bassandlead"] = 87; - _values["pad1newage"] = 88; - _values["pad2warm"] = 89; - _values["pad3polysynth"] = 90; - _values["pad4choir"] = 91; - _values["pad5bowed"] = 92; - _values["pad6metallic"] = 93; - _values["pad7halo"] = 94; - _values["pad8sweep"] = 95; - _values["fx1rain"] = 96; - _values["fx2soundtrack"] = 97; - _values["fx3crystal"] = 98; - _values["fx4atmosphere"] = 99; - _values["fx5brightness"] = 100; - _values["fx6goblins"] = 101; - _values["fx7echoes"] = 102; - _values["fx8scifi"] = 103; - _values["sitar"] = 104; - _values["banjo"] = 105; - _values["shamisen"] = 106; - _values["koto"] = 107; - _values["kalimba"] = 108; - _values["bagpipe"] = 109; - _values["fiddle"] = 110; - _values["shanai"] = 111; - _values["tinklebell"] = 112; - _values["agogo"] = 113; - _values["steeldrums"] = 114; - _values["woodblock"] = 115; - _values["taikodrum"] = 116; - _values["melodictom"] = 117; - _values["synthdrum"] = 118; - _values["reversecymbal"] = 119; - _values["guitarfretnoise"] = 120; - _values["breathnoise"] = 121; - _values["seashore"] = 122; - _values["birdtweet"] = 123; - _values["telephonering"] = 124; - _values["helicopter"] = 125; - _values["applause"] = 126; - _values["gunshot"] = 127; - } - name = name.ToLower().Replace(" ", ""); - return _values.ContainsKey(name) ? _values[name] : 0; - } - - public static bool IsPiano(int program) - { - return program <= 7 || (program >= 16 && program <= 23); - } - - public static bool IsGuitar(int program) - { - return (program >= 24 && program <= 39) || program == 105 || program == 43; - } - } -} diff --git a/Source/AlphaTab/Audio/Generator/AlphaSynthMidiFileHandler.cs b/Source/AlphaTab/Audio/Generator/AlphaSynthMidiFileHandler.cs deleted file mode 100644 index 378f1282c..000000000 --- a/Source/AlphaTab/Audio/Generator/AlphaSynthMidiFileHandler.cs +++ /dev/null @@ -1,115 +0,0 @@ -using AlphaTab.Audio.Synth.Midi; -using AlphaTab.Audio.Synth.Midi.Event; -using AlphaTab.Model; - -namespace AlphaTab.Audio.Generator -{ - /// - /// This implementation of the generates a - /// object which can be used in AlphaSynth for playback. - /// - public class AlphaSynthMidiFileHandler : IMidiFileHandler - { - private readonly MidiFile _midiFile; - - /// - /// Initializes a new instance of the class. - /// - /// The midi file. - public AlphaSynthMidiFileHandler(MidiFile midiFile) - { - _midiFile = midiFile; - } - - /// - public void AddTimeSignature(int tick, int timeSignatureNumerator, int timeSignatureDenominator) - { - var denominatorIndex = 0; - while ((timeSignatureDenominator = (timeSignatureDenominator >> 1)) > 0) - { - denominatorIndex++; - } - - var message = new MetaDataEvent(tick, - 0xFF, - (byte)MetaEventTypeEnum.TimeSignature, - new byte[] { (byte)(timeSignatureNumerator & 0xFF), (byte)(denominatorIndex & 0xFF), 48, 8 }); - _midiFile.AddEvent(message); - } - - /// - public void AddRest(int track, int tick, int channel) - { - var message = new SystemExclusiveEvent(tick, (byte)SystemCommonTypeEnum.SystemExclusive, 0, new byte[] { 0xFF }); - _midiFile.AddEvent(message); - } - - /// - public void AddNote(int track, int start, int length, byte key, DynamicValue dynamicValue, byte channel) - { - var velocity = MidiUtils.DynamicToVelocity(dynamicValue); - - var noteOn = new MidiEvent(start, MakeCommand((byte)MidiEventType.NoteOn, channel), FixValue(key), FixValue((byte)velocity)); - _midiFile.AddEvent(noteOn); - - var noteOff = new MidiEvent(start + length, MakeCommand((byte)MidiEventType.NoteOff, channel), FixValue(key), FixValue((byte)velocity)); - _midiFile.AddEvent(noteOff); - } - - private byte MakeCommand(byte command, byte channel) - { - return (byte)((command & 0xF0) | (channel & 0x0F)); - } - - private static byte FixValue(int value) - { - if (value > 127) return 127; - if (value < 0) return 0; - return (byte)value; - } - - /// - public void AddControlChange(int track, int tick, byte channel, byte controller, byte value) - { - var message = new MidiEvent(tick, MakeCommand((byte)MidiEventType.Controller, channel), FixValue(controller), FixValue(value)); - _midiFile.AddEvent(message); - } - - /// - public void AddProgramChange(int track, int tick, byte channel, byte program) - { - var message = new MidiEvent(tick, MakeCommand((byte)MidiEventType.ProgramChange, channel), FixValue(program), 0); - _midiFile.AddEvent(message); - } - - /// - public void AddTempo(int tick, int tempo) - { - // bpm -> microsecond per quarter note - var tempoInUsq = (60000000 / tempo); - - var message = new MetaNumberEvent(tick, - 0xFF, - (byte)MetaEventTypeEnum.Tempo, - tempoInUsq); - _midiFile.AddEvent(message); - } - - /// - public void AddBend(int track, int tick, byte channel, int value) - { - var message = new MidiEvent(tick, MakeCommand((byte)MidiEventType.PitchBend, channel), 0, FixValue(value)); - _midiFile.AddEvent(message); - } - - /// - public void FinishTrack(int track, int tick) - { - var message = new MetaDataEvent(tick, - 0xFF, - (byte)MetaEventTypeEnum.EndOfTrack, - new byte[0]); - _midiFile.AddEvent(message); - } - } -} diff --git a/Source/AlphaTab/Audio/Generator/IMidiFileHandler.cs b/Source/AlphaTab/Audio/Generator/IMidiFileHandler.cs deleted file mode 100644 index ddb720c10..000000000 --- a/Source/AlphaTab/Audio/Generator/IMidiFileHandler.cs +++ /dev/null @@ -1,96 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Model; - -namespace AlphaTab.Audio.Generator -{ - /// - /// A handler is responsible for writing midi events to a custom structure - /// - public interface IMidiFileHandler - { - /// - /// Adds a time signature to the generated midi file - /// - /// The midi ticks when this event should be happening. - /// The time signature numerator - /// The time signature denominator - void AddTimeSignature(int tick, int timeSignatureNumerator, int timeSignatureDenominator); - - /// - /// Adds a rest to the generated midi file. - /// - /// The midi track on which the rest should be "played". - /// The midi ticks when the rest is "playing". - /// The midi channel on which the rest should be "played". - void AddRest(int track, int tick, int channel); - - /// - /// Adds a note to the generated midi file - /// - /// The midi track on which the note should be played. - /// The midi ticks when the note should start playing. - /// The duration the note in midi ticks. - /// The key of the note to play - /// The dynamic which should be applied to the note. - /// The midi channel on which the note should be played. - void AddNote(int track, int start, int length, byte key, DynamicValue dynamicValue, byte channel); - - /// - /// Adds a control change to the generated midi file. - /// - /// The midi track on which the controller should change. - /// The midi ticks when the controller should change. - /// The midi channel on which the controller should change. - /// The midi controller that should change. - /// The value to which the midi controller should change - void AddControlChange(int track, int tick, byte channel, byte controller, byte value); - - /// - /// Add a program change to the generated midi file - /// - /// The midi track on which the program should change. - /// The midi ticks when the program should change. - /// The midi channel on which the program should change. - /// The new program for the selected track and channel. - void AddProgramChange(int track, int tick, byte channel, byte program); - - /// - /// Add a tempo change to the generated midi file. - /// - /// The midi ticks when the tempo should change change. - /// The tempo as BPM - void AddTempo(int tick, int tempo); - - /// - /// Add a bend to the generated midi file. - /// - /// The midi track on which the bend should change. - /// The midi ticks when the bend should change. - /// The midi channel on which the bend should change. - /// The new bend for the selected track and channel. - void AddBend(int track, int tick, byte channel, int value); - - /// - /// Indicates that the track is finished on the given ticks. - /// - /// The track that was finished. - /// The end tick for this track. - void FinishTrack(int track, int tick); - } -} diff --git a/Source/AlphaTab/Audio/Generator/MidiFileGenerator.cs b/Source/AlphaTab/Audio/Generator/MidiFileGenerator.cs deleted file mode 100644 index edfeba984..000000000 --- a/Source/AlphaTab/Audio/Generator/MidiFileGenerator.cs +++ /dev/null @@ -1,1099 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.Audio.Synth.Midi.Event; -using AlphaTab.Collections; -using AlphaTab.Model; -using AlphaTab.Util; - -namespace AlphaTab.Audio.Generator -{ - class MidiNoteDuration - { - public int NoteOnly { get; set; } - public int UntilTieEnd { get; set; } - public int LetRingEnd { get; set; } - } - - /// - /// This generator creates a midi file using a score. - /// - public class MidiFileGenerator - { - private readonly Score _score; - private readonly Settings _settings; - private readonly IMidiFileHandler _handler; - private int _currentTempo; - private BeatTickLookup _currentBarRepeatLookup; - - private const int DefaultDurationDead = 30; - private const int DefaultDurationPalmMute = 80; - - /// - /// Gets a lookup object which can be used to quickly find beats and bars - /// at a given midi tick position. - /// - public MidiTickLookup TickLookup { get; } - - /// - /// Initializes a new instance of the class. - /// - /// The score for which the midi file should be generated. - /// The settings ot use for generation. - /// The handler that should be used for generating midi events. - public MidiFileGenerator(Score score, Settings settings, IMidiFileHandler handler) - { - _score = score; - _settings = settings; - _currentTempo = _score.Tempo; - _handler = handler; - TickLookup = new MidiTickLookup(); - } - - /// - public void Generate() - { - // initialize tracks - for (int i = 0, j = _score.Tracks.Count; i < j; i++) - { - GenerateTrack(_score.Tracks[i]); - } - - Logger.Info("Midi", "Begin midi generation"); - var controller = new MidiPlaybackController(_score); - MasterBar previousMasterBar = null; // store the previous played bar for repeats - while (!controller.Finished) - { - var index = controller.Index; - var bar = _score.MasterBars[index]; - var currentTick = controller.CurrentTick; - controller.ProcessCurrent(); - if (controller.ShouldPlay) - { - GenerateMasterBar(bar, previousMasterBar, currentTick); - for (int i = 0, j = _score.Tracks.Count; i < j; i++) - { - var track = _score.Tracks[i]; - for (int k = 0, l = track.Staves.Count; k < l; k++) - { - var staff = track.Staves[k]; - if (index < staff.Bars.Count) - { - GenerateBar(staff.Bars[index], currentTick); - } - } - } - } - controller.MoveNext(); - previousMasterBar = bar; - } - - for (int i = 0, j = _score.Tracks.Count; i < j; i++) - { - _handler.FinishTrack(_score.Tracks[i].Index, controller.CurrentTick); - } - - TickLookup.Finish(); - Logger.Info("Midi", "Midi generation done"); - } - - #region Track - - private void GenerateTrack(Track track) - { - // channel - GenerateChannel(track, (byte)track.PlaybackInfo.PrimaryChannel, track.PlaybackInfo); - if (track.PlaybackInfo.PrimaryChannel != track.PlaybackInfo.SecondaryChannel) - { - GenerateChannel(track, (byte)track.PlaybackInfo.SecondaryChannel, track.PlaybackInfo); - } - } - - private void GenerateChannel(Track track, byte channel, PlaybackInformation playbackInfo) - { - var volume = ToChannelShort(playbackInfo.Volume); - var balance = ToChannelShort(playbackInfo.Balance); - _handler.AddControlChange(track.Index, 0, channel, (byte)ControllerType.VolumeCoarse, (byte)volume); - _handler.AddControlChange(track.Index, 0, channel, (byte)ControllerType.PanCoarse, (byte)balance); - _handler.AddControlChange(track.Index, 0, channel, (byte)ControllerType.ExpressionControllerCoarse, 127); - - // set parameter that is being updated (0) -> PitchBendRangeCoarse - _handler.AddControlChange(track.Index, 0, channel, (byte)ControllerType.RegisteredParameterFine, 0); - _handler.AddControlChange(track.Index, 0, channel, (byte)ControllerType.RegisteredParameterCourse, 0); - - // Set PitchBendRangeCoarse to 12 - _handler.AddControlChange(track.Index, 0, channel, (byte)ControllerType.DataEntryFine, 0); - _handler.AddControlChange(track.Index, 0, channel, (byte)ControllerType.DataEntryCoarse, 12); - - _handler.AddProgramChange(track.Index, 0, channel, (byte)playbackInfo.Program); - } - - private static int ToChannelShort(int data) - { - var value = Math.Max(-32768, Math.Min(32767, (data * 8) - 1)); - return (Math.Max(value, -1)) + 1; - } - - #endregion - - #region MasterBar - - private void GenerateMasterBar(MasterBar masterBar, MasterBar previousMasterBar, int currentTick) - { - // time signature - if (previousMasterBar == null || - previousMasterBar.TimeSignatureDenominator != masterBar.TimeSignatureDenominator || - previousMasterBar.TimeSignatureNumerator != masterBar.TimeSignatureNumerator) - { - _handler.AddTimeSignature(currentTick, masterBar.TimeSignatureNumerator, masterBar.TimeSignatureDenominator); - } - - // tempo - if (previousMasterBar == null) - { - _handler.AddTempo(currentTick, masterBar.Score.Tempo); - _currentTempo = masterBar.Score.Tempo; - } - else if (masterBar.TempoAutomation != null) - { - _handler.AddTempo(currentTick, (int)masterBar.TempoAutomation.Value); - _currentTempo = (int)(masterBar.TempoAutomation.Value); - } - - var masterBarLookup = new MasterBarTickLookup(); - masterBarLookup.MasterBar = masterBar; - masterBarLookup.Start = currentTick; - masterBarLookup.Tempo = _currentTempo; - masterBarLookup.End = masterBarLookup.Start + masterBar.CalculateDuration(); - TickLookup.AddMasterBar(masterBarLookup); - } - - #endregion - - #region Bar -> Voice -> Beat -> Automations/Rests/Notes - - private void GenerateBar(Bar bar, int barStartTick) - { - var playbackBar = GetPlaybackBar(bar); - _currentBarRepeatLookup = null; - - for (int i = 0, j = playbackBar.Voices.Count; i < j; i++) - { - GenerateVoice(playbackBar.Voices[i], barStartTick, bar); - } - } - - private Bar GetPlaybackBar(Bar bar) - { - switch (bar.SimileMark) - { - case SimileMark.Simple: - if (bar.PreviousBar != null) - { - bar = GetPlaybackBar(bar.PreviousBar); - } - break; - case SimileMark.FirstOfDouble: - if (bar.PreviousBar != null && bar.PreviousBar.PreviousBar != null) - { - bar = GetPlaybackBar(bar.PreviousBar.PreviousBar); - } - break; - case SimileMark.SecondOfDouble: - if (bar.PreviousBar != null && bar.PreviousBar.PreviousBar != null) - { - bar = GetPlaybackBar(bar.PreviousBar.PreviousBar); - } - break; - } - - return bar; - } - - private void GenerateVoice(Voice voice, int barStartTick, Bar realBar) - { - if (voice.IsEmpty && (!voice.Bar.IsEmpty || voice.Index != 0)) return; - - for (int i = 0, j = voice.Beats.Count; i < j; i++) - { - GenerateBeat(voice.Beats[i], barStartTick, realBar); - } - } - - private void GenerateBeat(Beat beat, int barStartTick, Bar realBar) - { - // TODO: take care of tripletfeel - var beatStart = beat.PlaybackStart; - var audioDuration = beat.Voice.Bar.IsEmpty - ? beat.Voice.Bar.MasterBar.CalculateDuration() - : beat.PlaybackDuration; - - var beatLookup = new BeatTickLookup(); - beatLookup.Start = barStartTick + beatStart; - var realTickOffset = beat.NextBeat == null ? audioDuration : beat.NextBeat.AbsolutePlaybackStart - beat.AbsolutePlaybackStart; - beatLookup.End = barStartTick + beatStart; - - beatLookup.End += (realTickOffset > audioDuration ? realTickOffset : audioDuration); - - // in case of normal playback register playback - if (realBar == beat.Voice.Bar) - { - beatLookup.Beat = beat; - TickLookup.AddBeat(beatLookup); - } - // in case of bar repeats register empty beat - else - { - beatLookup.IsEmptyBar = true; - beatLookup.Beat = realBar.Voices[0].Beats[0]; - if (_currentBarRepeatLookup == null) - { - _currentBarRepeatLookup = beatLookup; - TickLookup.AddBeat(_currentBarRepeatLookup); - } - else - { - _currentBarRepeatLookup.End = beatLookup.End; - } - } - - var track = beat.Voice.Bar.Staff.Track; - - for (int i = 0, j = beat.Automations.Count; i < j; i++) - { - GenerateAutomation(beat, beat.Automations[i], barStartTick); - } - - if (beat.IsRest) - { - _handler.AddRest(track.Index, barStartTick + beatStart, track.PlaybackInfo.PrimaryChannel); - } - else - { - var brushInfo = GetBrushInfo(beat); - - for (int i = 0, j = beat.Notes.Count; i < j; i++) - { - var n = beat.Notes[i]; - - GenerateNote(n, barStartTick + beatStart, audioDuration, brushInfo); - } - } - - if (beat.Vibrato != VibratoType.None) - { - int phaseLength = 240; - int bendAmplitude = 3; - switch (beat.Vibrato) - { - case VibratoType.Slight: - phaseLength = _settings.Vibrato.BeatSlightLength; - bendAmplitude = _settings.Vibrato.BeatSlightAmplitude; - break; - case VibratoType.Wide: - phaseLength = _settings.Vibrato.BeatWideLength; - bendAmplitude = _settings.Vibrato.BeatWideAmplitude; - break; - } - - GenerateVibratorWithParams(beat.Voice.Bar.Staff.Track, barStartTick + beatStart, beat.PlaybackDuration, phaseLength, bendAmplitude, track.PlaybackInfo.SecondaryChannel); - } - } - - private void GenerateNote(Note note, int beatStart, int beatDuration, int[] brushInfo) - { - var track = note.Beat.Voice.Bar.Staff.Track; - var staff = note.Beat.Voice.Bar.Staff; - var noteKey = note.RealValue; - var brushOffset = note.IsStringed && note.String <= brushInfo.Length ? brushInfo[note.String - 1] : 0; - var noteStart = beatStart + brushOffset; - var noteDuration = GetNoteDuration(note, beatDuration); - noteDuration.UntilTieEnd -= brushOffset; - noteDuration.NoteOnly -= brushOffset; - noteDuration.LetRingEnd -= brushOffset; - var dynamicValue = GetDynamicValue(note); - - var channel = note.HasBend || note.Beat.HasWhammyBar || note.Beat.Vibrato != VibratoType.None - ? track.PlaybackInfo.SecondaryChannel - : track.PlaybackInfo.PrimaryChannel; - - var initialBend = DefaultBend; - if (note.HasBend) - { - initialBend += (int)Math.Round(note.BendPoints[0].Value * DefaultBendSemitone); - } - else if (note.Beat.HasWhammyBar) - { - initialBend += (int)Math.Round(note.Beat.WhammyBarPoints[0].Value * DefaultBendSemitone); - } - else if (note.IsTieDestination) - { - initialBend = 0; - } - - if (initialBend > 0) - { - _handler.AddBend(track.Index, noteStart, (byte)channel, initialBend); - } - - // - // Fade in - if (note.Beat.FadeIn) - { - GenerateFadeIn(note, noteStart, noteDuration, noteKey, dynamicValue); - } - - // TODO: grace notes? - - // - // Trill - if (note.IsTrill && staff.StaffKind != StaffKind.Percussion) - { - GenerateTrill(note, noteStart, noteDuration, noteKey, dynamicValue, channel); - // no further generation needed - return; - } - - // - // Tremolo Picking - if (note.Beat.IsTremolo) - { - GenerateTremoloPicking(note, noteStart, noteDuration, noteKey, dynamicValue, channel); - // no further generation needed - return; - } - - // - // All String Bending/Variation effects - if (note.HasBend) - { - GenerateBend(note, noteStart, noteDuration, noteKey, dynamicValue, channel); - } - else if (note.Beat.HasWhammyBar && note.Index == 0) - { - GenerateWhammy(note.Beat, noteStart, noteDuration, noteKey, dynamicValue, channel); - } - else if (note.SlideType != SlideType.None) - { - GenerateSlide(note, noteStart, noteDuration, noteKey, dynamicValue, channel); - } - else if (note.Vibrato != VibratoType.None) - { - GenerateVibrato(note, noteStart, noteDuration, noteKey, dynamicValue, channel); - } - - if (!note.IsTieDestination) - { - var noteSoundDuration = Math.Max(noteDuration.UntilTieEnd, noteDuration.LetRingEnd); - _handler.AddNote(track.Index, noteStart, noteSoundDuration, (byte)noteKey, dynamicValue, (byte)channel); - } - } - - private MidiNoteDuration GetNoteDuration(Note note, int duration) - { - var durationWithEffects = new MidiNoteDuration(); - durationWithEffects.NoteOnly = duration; - durationWithEffects.UntilTieEnd = duration; - durationWithEffects.LetRingEnd = duration; - - if (note.IsDead) - { - durationWithEffects.NoteOnly = ApplyStaticDuration(DefaultDurationDead, duration); - durationWithEffects.UntilTieEnd = durationWithEffects.NoteOnly; - durationWithEffects.LetRingEnd = durationWithEffects.NoteOnly; - return durationWithEffects; - } - if (note.IsPalmMute) - { - durationWithEffects.NoteOnly = ApplyStaticDuration(DefaultDurationPalmMute, duration); - durationWithEffects.UntilTieEnd = durationWithEffects.NoteOnly; - durationWithEffects.LetRingEnd = durationWithEffects.NoteOnly; - return durationWithEffects; - } - if (note.IsStaccato) - { - durationWithEffects.NoteOnly = (duration / 2); - durationWithEffects.UntilTieEnd = durationWithEffects.NoteOnly; - durationWithEffects.LetRingEnd = durationWithEffects.NoteOnly; - return durationWithEffects; - } - - if (note.IsTieOrigin) - { - var endNote = note.TieDestination; - - // for the initial start of the tie calculate absolute duration from start to end note - if (endNote != null) - { - if (!note.IsTieDestination) - { - var startTick = note.Beat.AbsolutePlaybackStart; - var tieDestinationDuration = GetNoteDuration(endNote, endNote.Beat.PlaybackDuration); - var endTick = endNote.Beat.AbsolutePlaybackStart + tieDestinationDuration.UntilTieEnd; - durationWithEffects.UntilTieEnd = endTick - startTick; - } - else - { - // for continuing ties, take the current duration + the one from the destination - // this branch will be entered as part of the recusion of the if branch - var tieDestinationDuration = GetNoteDuration(endNote, endNote.Beat.PlaybackDuration); - durationWithEffects.UntilTieEnd = duration + tieDestinationDuration.UntilTieEnd; - } - } - } - - if (note.IsLetRing && _settings.DisplayMode == DisplayMode.GuitarPro) - { - // LetRing ends when: - // - rest - Beat lastLetRingBeat = note.Beat; - var letRingEnd = 0; - var maxDuration = note.Beat.Voice.Bar.MasterBar.CalculateDuration(); - while (lastLetRingBeat.NextBeat != null) - { - var next = lastLetRingBeat.NextBeat; - if (next.IsRest) - { - break; - } - - // note on the same string - if (note.IsStringed && next.HasNoteOnString(note.String)) - { - break; - } - - lastLetRingBeat = lastLetRingBeat.NextBeat; - - letRingEnd = (lastLetRingBeat.AbsolutePlaybackStart - note.Beat.AbsolutePlaybackStart) + - lastLetRingBeat.PlaybackDuration; - if (letRingEnd > maxDuration) - { - letRingEnd = maxDuration; - break; - } - } - - if (lastLetRingBeat == note.Beat) - { - durationWithEffects.LetRingEnd = duration; - } - else - { - durationWithEffects.LetRingEnd = letRingEnd; - } - } - else - { - durationWithEffects.LetRingEnd = durationWithEffects.UntilTieEnd; - } - return durationWithEffects; - } - - private int ApplyStaticDuration(int duration, int maximum) - { - var value = (_currentTempo * duration) / 60; - return Math.Min(value, maximum); - } - - private DynamicValue GetDynamicValue(Note note) - { - var dynamicValue = note.Dynamic; - - // more silent on hammer destination - if (note.Beat.Voice.Bar.Staff.StaffKind != StaffKind.Percussion && note.HammerPullOrigin != null) - { - dynamicValue--; - } - - // more silent on ghost notes - if (note.IsGhost) - { - dynamicValue--; - } - - // louder on accent - switch (note.Accentuated) - { - case AccentuationType.Normal: - dynamicValue++; - break; - case AccentuationType.Heavy: - dynamicValue += 2; - break; - } - - return dynamicValue; - } - - #endregion - - #region Effect Generation - - private void GenerateFadeIn(Note note, int noteStart, MidiNoteDuration noteDuration, int noteKey, DynamicValue dynamicValue) - { - var track = note.Beat.Voice.Bar.Staff.Track; - var endVolume = ToChannelShort(track.PlaybackInfo.Volume); - var volumeFactor = (float)endVolume / noteDuration.NoteOnly; - - var tickStep = 120; - int steps = (noteDuration.NoteOnly / tickStep); - - var endTick = noteStart + noteDuration.NoteOnly; - for (int i = steps - 1; i >= 0; i--) - { - var tick = endTick - (i * tickStep); - var volume = (tick - noteStart) * volumeFactor; - if (i == steps - 1) - { - _handler.AddControlChange(track.Index, noteStart, (byte)track.PlaybackInfo.PrimaryChannel, (byte)ControllerType.VolumeCoarse, (byte)volume); - _handler.AddControlChange(track.Index, noteStart, (byte)track.PlaybackInfo.SecondaryChannel, (byte)ControllerType.VolumeCoarse, (byte)volume); - } - _handler.AddControlChange(track.Index, tick, (byte)track.PlaybackInfo.PrimaryChannel, (byte)ControllerType.VolumeCoarse, (byte)volume); - _handler.AddControlChange(track.Index, tick, (byte)track.PlaybackInfo.SecondaryChannel, (byte)ControllerType.VolumeCoarse, (byte)volume); - } - } - - private void GenerateVibrato(Note note, int noteStart, MidiNoteDuration noteDuration, int noteKey, DynamicValue dynamicValue, int channel) - { - int phaseLength; - int bendAmplitude; - - switch (note.Vibrato) - { - case VibratoType.Slight: - phaseLength = _settings.Vibrato.NoteSlightLength; - bendAmplitude = _settings.Vibrato.NoteSlightAmplitude; - break; - case VibratoType.Wide: - phaseLength = _settings.Vibrato.NoteWideLength; - bendAmplitude = _settings.Vibrato.NoteWideAmplitude; - break; - default: - return; - } - - var track = note.Beat.Voice.Bar.Staff.Track; - - GenerateVibratorWithParams(track, noteStart, noteDuration.NoteOnly, phaseLength, bendAmplitude, channel); - } - - private void GenerateVibratorWithParams(Track track, int noteStart, int noteDuration, int phaseLength, int bendAmplitude, int channel) - { - const int resolution = 16; - - int phaseHalf = phaseLength / 2; - // 1st Phase stays at bend 0, - // then we have a sine wave with the given amplitude and phase length - - noteStart += phaseLength; - var noteEnd = noteStart + noteDuration; - - while (noteStart < noteEnd) - { - var phase = 0; - var phaseDuration = noteStart + phaseLength < noteEnd ? phaseLength : noteEnd - noteStart; - while (phase < phaseDuration) - { - var bend = bendAmplitude * Math.Sin(phase * Math.PI / phaseHalf); - - _handler.AddBend(track.Index, noteStart + phase, (byte)channel, (int)(DefaultBend + bend)); - - phase += resolution; - } - - noteStart += phaseLength; - } - } - - private void GenerateSlide(Note note, int noteStart, MidiNoteDuration noteDuration, int noteKey, DynamicValue dynamicValue, int channel) - { - // TODO - } - - private const int DefaultBend = 0x40; - private const float DefaultBendSemitone = 2.75f; - - private void GenerateBend(Note note, int noteStart, MidiNoteDuration noteDuration, int noteKey, DynamicValue dynamicValue, int channel) - { - FastList bendPoints = note.BendPoints; - var track = note.Beat.Voice.Bar.Staff.Track; - - // if bend is extended on next tied note, we directly bend to the final bend value - int? finalBendValue = null; - - // Bends are spread across all tied notes unless they have a bend on their own. - double duration; - if (note.IsTieOrigin && (_settings == null || _settings.ExtendBendArrowsOnTiedNotes)) - { - var endNote = note; - while (endNote.IsTieOrigin && !endNote.TieDestination.HasBend) - { - endNote = endNote.TieDestination; - } - - duration = endNote.Beat.AbsolutePlaybackStart - note.Beat.AbsolutePlaybackStart + GetNoteDuration(endNote, endNote.Beat.PlaybackDuration).NoteOnly; - } - // if current note is a grace note with bend and tie, we can reach into next note - else if(note.IsTieOrigin && note.Beat.GraceType != GraceType.None) - { - switch (note.TieDestination.BendType) - { - case BendType.Bend: - case BendType.BendRelease: - case BendType.PrebendBend: - finalBendValue = note.TieDestination.BendPoints[1].Value; - break; - case BendType.Prebend: - case BendType.PrebendRelease: - finalBendValue = note.TieDestination.BendPoints[0].Value; - break; - } - - if (_settings == null) - { - duration = noteDuration.NoteOnly; - } - else - { - duration = Math.Max(noteDuration.NoteOnly, MidiUtils.MillisToTicks(_settings.SongBookBendDuration, _currentTempo)); - } - } - else - { - duration = noteDuration.NoteOnly; - } - - // ensure prebends are slightly before the actual note. - if (bendPoints[0].Value > 0 && !note.IsContinuedBend) - { - noteStart--; - } - var bendDuration = _settings == null ? duration : Math.Min(duration, MidiUtils.MillisToTicks(_settings.SongBookBendDuration, _currentTempo)); - - FastList playedBendPoints = new FastList(); - switch (note.BendType) - { - case BendType.Custom: - playedBendPoints = bendPoints; - break; - case BendType.Bend: - case BendType.Release: - switch (note.BendStyle) - { - case BendStyle.Default: - playedBendPoints = bendPoints; - break; - case BendStyle.Gradual: - playedBendPoints.Add(new BendPoint(0, note.BendPoints[0].Value)); - if (finalBendValue == null || finalBendValue.Value < note.BendPoints[1].Value) - { - finalBendValue = note.BendPoints[1].Value; - } - playedBendPoints.Add(new BendPoint(BendPoint.MaxPosition, finalBendValue.Value)); - break; - case BendStyle.Fast: - if (finalBendValue == null || finalBendValue.Value < note.BendPoints[1].Value) - { - finalBendValue = note.BendPoints[1].Value; - } - - if (note.Beat.GraceType == GraceType.BendGrace) - { - GenerateSongBookWhammyOrBend(noteStart, channel, duration, track, - true, new[] { note.BendPoints[0].Value, finalBendValue.Value }, bendDuration); - } - else - { - GenerateSongBookWhammyOrBend(noteStart, channel, duration, track, - false, new[] { note.BendPoints[0].Value, finalBendValue.Value }, bendDuration); - } - return; - } - - break; - case BendType.BendRelease: - switch (note.BendStyle) - { - case BendStyle.Default: - playedBendPoints = bendPoints; - break; - case BendStyle.Gradual: - playedBendPoints.Add(new BendPoint(0, note.BendPoints[0].Value)); - playedBendPoints.Add(new BendPoint(BendPoint.MaxPosition / 2, note.BendPoints[1].Value)); - playedBendPoints.Add(new BendPoint(BendPoint.MaxPosition, note.BendPoints[2].Value)); - break; - case BendStyle.Fast: - GenerateSongBookWhammyOrBend(noteStart, channel, duration, track, - false, new[] { note.BendPoints[0].Value, note.BendPoints[1].Value, note.BendPoints[2].Value }, bendDuration); - return; - } - - break; - case BendType.Hold: - playedBendPoints = bendPoints; - break; - case BendType.Prebend: - playedBendPoints = bendPoints; - break; - case BendType.PrebendBend: - switch (note.BendStyle) - { - case BendStyle.Default: - playedBendPoints = bendPoints; - break; - case BendStyle.Gradual: - playedBendPoints.Add(new BendPoint(0, note.BendPoints[0].Value)); - playedBendPoints.Add(new BendPoint(BendPoint.MaxPosition, note.BendPoints[1].Value)); - break; - case BendStyle.Fast: - var preBendValue = DefaultBend + (note.BendPoints[0].Value * DefaultBendSemitone); - _handler.AddBend(track.Index, noteStart, (byte)channel, (int)preBendValue); - - if (finalBendValue == null || finalBendValue.Value < note.BendPoints[1].Value) - { - finalBendValue = note.BendPoints[1].Value; - } - - GenerateSongBookWhammyOrBend(noteStart, channel, duration, track, - false, new[] { note.BendPoints[0].Value, finalBendValue.Value }, bendDuration); - return; - } - - break; - case BendType.PrebendRelease: - switch (note.BendStyle) - { - case BendStyle.Default: - playedBendPoints = bendPoints; - break; - case BendStyle.Gradual: - playedBendPoints.Add(new BendPoint(0, note.BendPoints[0].Value)); - playedBendPoints.Add(new BendPoint(BendPoint.MaxPosition, note.BendPoints[1].Value)); - break; - case BendStyle.Fast: - var preBendValue = DefaultBend + (note.BendPoints[0].Value * DefaultBendSemitone); - _handler.AddBend(track.Index, noteStart, (byte)channel, (int)preBendValue); - - GenerateSongBookWhammyOrBend(noteStart, channel, duration, track, - false, new[] { note.BendPoints[0].Value, note.BendPoints[1].Value }, bendDuration); - return; - } - - break; - } - - GenerateWhammyOrBend(noteStart, channel, duration, playedBendPoints, track); - } - - private void GenerateSongBookWhammyOrBend(int noteStart, int channel, double duration, Track track, - bool bendAtBeginning, int[] bendValues, double bendDuration) - { - var startTick = bendAtBeginning ? noteStart : (noteStart + duration) - bendDuration; - var ticksBetweenPoints = bendDuration / (bendValues.Length - 1); - - for (int i = 0; i < bendValues.Length - 1; i++) - { - var currentBendValue = DefaultBend + (bendValues[i] * DefaultBendSemitone); - var nextBendValue = DefaultBend + (bendValues[i + 1] * DefaultBendSemitone); - - var tick = startTick + (ticksBetweenPoints * i); - GenerateBendValues(tick, channel, track, ticksBetweenPoints, currentBendValue, nextBendValue); - } - } - - - private void GenerateWhammy(Beat beat, int noteStart, MidiNoteDuration noteDuration, int noteKey, DynamicValue dynamicValue, int channel) - { - FastList bendPoints = beat.WhammyBarPoints; - var track = beat.Voice.Bar.Staff.Track; - - double duration = noteDuration.NoteOnly; - - // ensure prebends are slightly before the actual note. - if (bendPoints[0].Value > 0 && !beat.IsContinuedWhammy) - { - noteStart--; - } - - FastList playedBendPoints = new FastList(); - switch (beat.WhammyBarType) - { - case WhammyType.Custom: - playedBendPoints = bendPoints; - break; - case WhammyType.Dive: - switch (beat.WhammyStyle) - { - case BendStyle.Default: - playedBendPoints = bendPoints; - break; - case BendStyle.Gradual: - playedBendPoints.Add(new BendPoint(0, bendPoints[0].Value)); - playedBendPoints.Add(new BendPoint(BendPoint.MaxPosition, bendPoints[1].Value)); - break; - case BendStyle.Fast: - var whammyDuration = _settings == null ? duration : Math.Min(duration, MidiUtils.MillisToTicks(_settings.SongBookBendDuration, _currentTempo)); - GenerateSongBookWhammyOrBend(noteStart, channel, duration, track, - false, new[] { bendPoints[0].Value, bendPoints[1].Value }, whammyDuration); - return; - } - - break; - case WhammyType.Dip: - switch (beat.WhammyStyle) - { - case BendStyle.Default: - playedBendPoints = bendPoints; - break; - case BendStyle.Gradual: - playedBendPoints.Add(new BendPoint(0, bendPoints[0].Value)); - playedBendPoints.Add(new BendPoint(BendPoint.MaxPosition / 2, bendPoints[1].Value)); - playedBendPoints.Add(new BendPoint(BendPoint.MaxPosition, bendPoints[2].Value)); - break; - case BendStyle.Fast: - var whammyDuration = _settings == null ? duration : Math.Min(duration, MidiUtils.MillisToTicks(_settings.SongBookDipDuration, _currentTempo)); - GenerateSongBookWhammyOrBend(noteStart, channel, duration, track, - true, new[] { bendPoints[0].Value, bendPoints[1].Value, bendPoints[2].Value }, whammyDuration); - return; - } - - break; - case WhammyType.Hold: - playedBendPoints = bendPoints; - break; - case WhammyType.Predive: - playedBendPoints = bendPoints; - break; - case WhammyType.PrediveDive: - switch (beat.WhammyStyle) - { - case BendStyle.Default: - playedBendPoints = bendPoints; - break; - case BendStyle.Gradual: - playedBendPoints.Add(new BendPoint(0, bendPoints[0].Value)); - playedBendPoints.Add(new BendPoint(BendPoint.MaxPosition / 2, bendPoints[0].Value)); - playedBendPoints.Add(new BendPoint(BendPoint.MaxPosition, bendPoints[1].Value)); - break; - case BendStyle.Fast: - var preDiveValue = DefaultBend + (bendPoints[0].Value * DefaultBendSemitone); - _handler.AddBend(track.Index, noteStart, (byte)channel, (int)preDiveValue); - - var whammyDuration = _settings == null ? duration : Math.Min(duration, MidiUtils.MillisToTicks(_settings.SongBookBendDuration, _currentTempo)); - GenerateSongBookWhammyOrBend(noteStart, channel, duration, track, - false, new[] { bendPoints[0].Value, bendPoints[1].Value }, whammyDuration); - return; - } - - break; - } - - GenerateWhammyOrBend(noteStart, channel, duration, playedBendPoints, track); - } - - private void GenerateWhammyOrBend(int noteStart, int channel, double duration, FastList playedBendPoints, Track track) - { - var ticksPerPosition = duration / BendPoint.MaxPosition; - for (int i = 0; i < playedBendPoints.Count - 1; i++) - { - var currentPoint = playedBendPoints[i]; - var nextPoint = playedBendPoints[i + 1]; - - // calculate the midi pitchbend values start and end values - var currentBendValue = DefaultBend + (currentPoint.Value * DefaultBendSemitone); - var nextBendValue = DefaultBend + (nextPoint.Value * DefaultBendSemitone); - - // how many midi ticks do we have to spend between this point and the next one? - var ticksBetweenPoints = ticksPerPosition * (nextPoint.Offset - currentPoint.Offset); - - // we will generate one pitchbend message for each value - // for this we need to calculate how many ticks to offset per value - var tick = noteStart + (ticksPerPosition * currentPoint.Offset); - - GenerateBendValues(tick, channel, track, ticksBetweenPoints, currentBendValue, nextBendValue); - } - } - - private void GenerateBendValues(double currentTick, int channel, Track track, double ticksBetweenPoints, - float currentBendValue, float nextBendValue) - { - var ticksPerValue = ticksBetweenPoints / Math.Abs(nextBendValue - currentBendValue); - // bend up - if (currentBendValue < nextBendValue) - { - while (currentBendValue <= nextBendValue) - { - _handler.AddBend(track.Index, (int) currentTick, (byte) channel, (int) Math.Round(currentBendValue)); - currentBendValue++; - currentTick += ticksPerValue; - } - } - // bend down - else if (currentBendValue > nextBendValue) - { - while (currentBendValue >= nextBendValue) - { - _handler.AddBend(track.Index, (int) currentTick, (byte) channel, (int) Math.Round(currentBendValue)); - currentBendValue--; - currentTick += ticksPerValue; - } - } - // hold - else - { - _handler.AddBend(track.Index, (int) currentTick, (byte) channel, (int) Math.Round(currentBendValue)); - } - } - - private void GenerateTrill(Note note, int noteStart, MidiNoteDuration noteDuration, int noteKey, DynamicValue dynamicValue, int channel) - { - var track = note.Beat.Voice.Bar.Staff.Track; - var trillKey = note.StringTuning + note.TrillFret; - var trillLength = note.TrillSpeed.ToTicks(); - var realKey = true; - var tick = noteStart; - var end = noteStart + noteDuration.UntilTieEnd; - while (tick + 10 < (end)) - { - // only the rest on last trill play - if ((tick + trillLength) >= (end)) - { - trillLength = (end) - tick; - } - _handler.AddNote(track.Index, tick, trillLength, (byte)(realKey ? trillKey : noteKey), dynamicValue, (byte)channel); - realKey = !realKey; - tick += trillLength; - } - } - - private void GenerateTremoloPicking(Note note, int noteStart, MidiNoteDuration noteDuration, int noteKey, DynamicValue dynamicValue, int channel) - { - var track = note.Beat.Voice.Bar.Staff.Track; - var tpLength = note.Beat.TremoloSpeed.Value.ToTicks(); - var tick = noteStart; - var end = noteStart + noteDuration.UntilTieEnd; - while (tick + 10 < (end)) - { - // only the rest on last trill play - if ((tick + tpLength) >= (end)) - { - tpLength = (end) - tick; - } - _handler.AddNote(track.Index, tick, tpLength, (byte)noteKey, dynamicValue, (byte)channel); - tick += tpLength; - } - } - - private int[] GetBrushInfo(Beat beat) - { - var brushInfo = new int[beat.Voice.Bar.Staff.Tuning.Length]; - - if (beat.BrushType != BrushType.None) - { - // - // calculate the number of - - // a mask where the single bits indicate the strings used - var stringUsed = 0; - var stringCount = 0; - - for (int i = 0, j = beat.Notes.Count; i < j; i++) - { - var n = beat.Notes[i]; - if (n.IsTieDestination) continue; - stringUsed |= 0x01 << (n.String - 1); - stringCount++; - } - - // - // calculate time offset for all strings - if (beat.Notes.Count > 0) - { - int brushMove = 0; - var brushIncrement = beat.BrushDuration / (stringCount - 1); - for (int i = 0, j = beat.Voice.Bar.Staff.Tuning.Length; i < j; i++) - { - var index = (beat.BrushType == BrushType.ArpeggioDown || beat.BrushType == BrushType.BrushDown) - ? i - : ((brushInfo.Length - 1) - i); - if ((stringUsed & (0x01 << index)) != 0) - { - brushInfo[index] = brushMove; - brushMove += brushIncrement; - } - } - } - } - - return brushInfo; - } - - private int GetBrushIncrement(Beat beat) - { - if (beat.BrushDuration == 0) return 0; - var duration = beat.PlaybackDuration; - if (duration == 0) return 0; - return (int)((duration / 8.0) * (4.0 / beat.BrushDuration)); - } - - #endregion - - #region Automations - - private void GenerateAutomation(Beat beat, Automation automation, int startMove) - { - switch (automation.Type) - { - case AutomationType.Instrument: - _handler.AddProgramChange(beat.Voice.Bar.Staff.Track.Index, beat.PlaybackStart + startMove, - (byte)beat.Voice.Bar.Staff.Track.PlaybackInfo.PrimaryChannel, - (byte)(automation.Value)); - _handler.AddProgramChange(beat.Voice.Bar.Staff.Track.Index, beat.PlaybackStart + startMove, - (byte)beat.Voice.Bar.Staff.Track.PlaybackInfo.SecondaryChannel, - (byte)(automation.Value)); - break; - case AutomationType.Balance: - var balance = ToChannelShort((int)automation.Value); - _handler.AddControlChange(beat.Voice.Bar.Staff.Track.Index, beat.PlaybackStart + startMove, - (byte)beat.Voice.Bar.Staff.Track.PlaybackInfo.PrimaryChannel, - (byte)ControllerType.PanCoarse, - (byte)balance); - _handler.AddControlChange(beat.Voice.Bar.Staff.Track.Index, beat.PlaybackStart + startMove, - (byte)beat.Voice.Bar.Staff.Track.PlaybackInfo.SecondaryChannel, - (byte)ControllerType.PanCoarse, - (byte)balance); - break; - case AutomationType.Volume: - var volume = ToChannelShort((int)automation.Value); - - _handler.AddControlChange(beat.Voice.Bar.Staff.Track.Index, beat.PlaybackStart + startMove, - (byte)beat.Voice.Bar.Staff.Track.PlaybackInfo.PrimaryChannel, - (byte)ControllerType.VolumeCoarse, - (byte)volume); - _handler.AddControlChange(beat.Voice.Bar.Staff.Track.Index, beat.PlaybackStart + startMove, - (byte)beat.Voice.Bar.Staff.Track.PlaybackInfo.SecondaryChannel, - (byte)ControllerType.VolumeCoarse, - (byte)volume); - break; - } - } - - #endregion - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Audio/Generator/MidiPlaybackController.cs b/Source/AlphaTab/Audio/Generator/MidiPlaybackController.cs deleted file mode 100644 index 82ffabd9f..000000000 --- a/Source/AlphaTab/Audio/Generator/MidiPlaybackController.cs +++ /dev/null @@ -1,121 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Model; - -namespace AlphaTab.Audio.Generator -{ - class MidiPlaybackController - { - private readonly Score _score; - - private int _repeatStartIndex; - private int _repeatNumber; - private bool _repeatOpen; - - public bool ShouldPlay { get; set; } - public int Index { get; set; } - public int CurrentTick { get; set; } - - public bool Finished => Index >= _score.MasterBars.Count; - - - public MidiPlaybackController(Score score) - { - _score = score; - - ShouldPlay = true; - Index = 0; - CurrentTick = 0; - } - - public void ProcessCurrent() - { - var masterBar = _score.MasterBars[Index]; - var masterBarAlternateEndings = masterBar.AlternateEndings; - - // if the repeat group wasn't closed we reset the repeating - // on the last group opening - if (!masterBar.RepeatGroup.IsClosed && masterBar.RepeatGroup.Openings[masterBar.RepeatGroup.Openings.Count - 1] == masterBar) - { - _repeatNumber = 0; - _repeatOpen = false; - } - - if ((masterBar.IsRepeatStart || masterBar.Index == 0) && _repeatNumber == 0) - { - _repeatStartIndex = Index; - _repeatOpen = true; - } - else if (masterBar.IsRepeatStart) - { - ShouldPlay = true; - } - - // if we encounter an alternate ending - if (_repeatOpen && masterBarAlternateEndings > 0) - { - // do we need to skip this section? - if ((masterBarAlternateEndings & (1 << _repeatNumber)) == 0) - { - ShouldPlay = false; - } - else - { - ShouldPlay = true; - } - } - - if (ShouldPlay) - { - CurrentTick += masterBar.CalculateDuration(); - } - } - - public void MoveNext() - { - var masterBar = _score.MasterBars[Index]; - var masterBarRepeatCount = masterBar.RepeatCount - 1; - - // if we encounter a repeat end - if (_repeatOpen && (masterBarRepeatCount > 0)) - { - // more repeats required? - if (_repeatNumber < masterBarRepeatCount) - { - // jump to start - Index = _repeatStartIndex; - _repeatNumber++; - } - else - { - // no repeats anymore, jump after repeat end - _repeatNumber = 0; - _repeatOpen = false; - ShouldPlay = true; - Index++; - } - } - // no repeat end, normal next - else - { - Index++; - } - } - } -} diff --git a/Source/AlphaTab/Audio/MidiTickLookup.cs b/Source/AlphaTab/Audio/MidiTickLookup.cs deleted file mode 100644 index 551d38ce8..000000000 --- a/Source/AlphaTab/Audio/MidiTickLookup.cs +++ /dev/null @@ -1,352 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Collections; -using AlphaTab.Model; - -namespace AlphaTab.Audio -{ - /// - /// Represents the time period, for which a is played. - /// - public class BeatTickLookup - { - /// - /// Gets or sets the start time in midi ticks at which the given beat is played. - /// - public int Start { get; set; } - /// - /// Gets or sets the end time in midi ticks at which the given beat is played. - /// - public int End { get; set; } - /// - /// Gets or sets the beat which is played. - /// - public Beat Beat { get; set; } - /// - /// Gets or sets whether the beat is the placeholder beat for an empty bar. - /// - public bool IsEmptyBar { get; set; } - } - - /// - /// Represents the time period, for which all bars of a are played. - /// - public class MasterBarTickLookup - { - /// - /// Gets or sets the start time in midi ticks at which the MasterBar is played. - /// - public int Start { get; set; } - /// - /// Gets or sets the end time in midi ticks at which the MasterBar is played. - /// - public int End { get; set; } - /// - /// Gets or sets the current tempo when the MasterBar is played. - /// - public int Tempo { get; set; } - /// - /// Gets or sets the MasterBar which is played. - /// - public MasterBar MasterBar { get; set; } - - /// - /// Gets or sets the list of object which define the durations - /// for all played within the period of this MasterBar. - /// - public FastList Beats { get; set; } - - /// - /// Gets or sets the of the next masterbar in the - /// - public MasterBarTickLookup NextMasterBar { get; set; } - - - /// - /// Initializes a new instance of the class. - /// - public MasterBarTickLookup() - { - Beats = new FastList(); - } - - /// - /// Performs the neccessary finalization steps after all information was written. - /// - public void Finish() - { - Beats.Sort((a, b) => a.Start - b.Start); - } - - /// - /// Adds a new to the list of played beats during this MasterBar period. - /// - /// - public void AddBeat(BeatTickLookup beat) - { - Beats.Add(beat); - } - } - - /// - /// Represents the results of searching the currently played beat. - /// - /// - public class MidiTickLookupFindBeatResult - { - /// - /// Gets or sets the beat that is currently played. - /// - public Beat CurrentBeat { get; set; } - /// - /// Gets or sets the beat that will be played next. - /// - public Beat NextBeat { get; set; } - /// - /// Gets or sets the duration in milliseconds how long this beat is playing. - /// - public int Duration { get; set; } - } - - /// - /// This class holds all information about when s and s are played. - /// - public class MidiTickLookup - { - private MasterBarTickLookup _currentMasterBar; - - /// - /// Gets a dictionary of all master bars played. The index is the index equals to . - /// - /// - /// This lookup only contains the first time a MasterBar is played. For a whole sequence of the song refer to . - /// - public FastDictionary MasterBarLookup { get; } - - /// - /// Gets a list of all sorted by time. - /// - public FastList MasterBars { get; } - - /// - /// Initializes a new instance of the class. - /// - public MidiTickLookup() - { - MasterBars = new FastList(); - MasterBarLookup = new FastDictionary(); - } - - /// - /// Performs the neccessary finalization steps after all information was written. - /// - public void Finish() - { - MasterBarTickLookup previous = null; - foreach (var bar in MasterBars) - { - bar.Finish(); - if (previous != null) - { - previous.NextMasterBar = bar; - } - previous = bar; - } - } - - /// - /// Finds the currently played beat given a list of tracks and the current time. - /// - /// The tracks in which to search the played beat for. - /// The current time in midi ticks. - /// The information about the current beat or null if no beat could be found. - public MidiTickLookupFindBeatResult FindBeat(Track[] tracks, int tick) - { - // get all beats within the masterbar - var masterBar = FindMasterBar(tick); - if (masterBar == null) - { - return null; - } - - var trackLookup = new FastDictionary(); - foreach (var track in tracks) - { - trackLookup[track.Index] = true; - } - - BeatTickLookup beat = null; - int index = 0; - var beats = masterBar.Beats; - for (int b = 0; b < beats.Count; b++) - { - // is the current beat played on the given tick? - var currentBeat = beats[b]; - // skip non relevant beats - if (!trackLookup.ContainsKey(currentBeat.Beat.Voice.Bar.Staff.Track.Index)) - { - continue; - } - if (currentBeat.Start <= tick && tick < currentBeat.End) - { - // take the latest played beat we can find. (most right) - if (beat == null || (beat.Start < currentBeat.Start)) - { - beat = beats[b]; - index = b; - } - } - // if we are already past the tick, we can stop searching - else if (currentBeat.End > tick) - { - break; - } - } - - if (beat == null) - { - return null; - } - - // search for next relevant beat in masterbar - BeatTickLookup nextBeat = null; - for (int b = index + 1; b < beats.Count; b++) - { - var currentBeat = beats[b]; - if (currentBeat.Start > beat.Start && trackLookup.ContainsKey(currentBeat.Beat.Voice.Bar.Staff.Track.Index)) - { - nextBeat = currentBeat; - break; - } - } - - // first relevant beat in next bar - if (nextBeat == null && masterBar.NextMasterBar != null) - { - var nextBar = masterBar.NextMasterBar; - beats = nextBar.Beats; - for (int b = 0; b < beats.Count; b++) - { - var currentBeat = beats[b]; - if (trackLookup.ContainsKey(currentBeat.Beat.Voice.Bar.Staff.Track.Index)) - { - nextBeat = currentBeat; - break; - } - } - } - - var result = new MidiTickLookupFindBeatResult(); - result.CurrentBeat = beat.Beat; - result.NextBeat = nextBeat == null ? null : nextBeat.Beat; - result.Duration = MidiUtils.TicksToMillis(beat.End - beat.Start, masterBar.Tempo); - return result; - } - - private MasterBarTickLookup FindMasterBar(int tick) - { - var bars = MasterBars; - var bottom = 0; - var top = bars.Count - 1; - - while (bottom <= top) - { - var middle = (top + bottom) / 2; - var bar = bars[middle]; - - // found? - if (tick >= bar.Start && tick < bar.End) - { - return bar; - } - // search in lower half - if (tick < bar.Start) - { - top = middle - 1; - } - // search in upper half - else - { - bottom = middle + 1; - } - } - - return null; - } - - /// - /// Gets the for a given masterbar at which the masterbar is played the first time. - /// - /// The masterbar to find the time period for. - /// A containing the details about the first time the is played. - // ReSharper disable once UnusedMember.Global - public MasterBarTickLookup GetMasterBar(MasterBar bar) - { - if (!MasterBarLookup.ContainsKey(bar.Index)) - { - return new MasterBarTickLookup - { - Start = 0, - End = 0, - Beats = new FastList(), - MasterBar = bar - }; - } - return MasterBarLookup[bar.Index]; - } - - /// - /// Gets the start time in midi ticks for a given masterbar at which the masterbar is played the first time. - /// - /// The masterbar to find the time period for. - /// The time in midi ticks at which the masterbar is played the first time or 0 if the masterbar is not contained - // ReSharper disable once UnusedMember.Global - public int GetMasterBarStart(MasterBar bar) - { - if (!MasterBarLookup.ContainsKey(bar.Index)) - { - return 0; - } - return MasterBarLookup[bar.Index].Start; - } - - /// - /// Adds a new to the lookup table. - /// - /// The item to add. - public void AddMasterBar(MasterBarTickLookup masterBar) - { - MasterBars.Add(masterBar); - _currentMasterBar = masterBar; - if (!MasterBarLookup.ContainsKey(masterBar.MasterBar.Index)) - { - MasterBarLookup[masterBar.MasterBar.Index] = masterBar; - } - } - - /// - /// Adds the given to the current . - /// - /// The lookup to add. - public void AddBeat(BeatTickLookup beat) - { - _currentMasterBar.AddBeat(beat); - } - } -} diff --git a/Source/AlphaTab/Audio/MidiUtils.cs b/Source/AlphaTab/Audio/MidiUtils.cs deleted file mode 100644 index 9f50093c2..000000000 --- a/Source/AlphaTab/Audio/MidiUtils.cs +++ /dev/null @@ -1,111 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Model; - -namespace AlphaTab.Audio -{ - static class MidiUtils - { - /// - /// The amount of ticks per quarter note used within this midi system. - /// (Pulses Per Quarter Note) - /// - public const int QuarterTime = 960; - - private const int MinVelocity = 15; - private const int VelocityIncrement = 16; - - /// - /// Converts the given midi tick duration into milliseconds. - /// - /// The duration in midi ticks - /// The current tempo in BPM. - /// The converted duration in milliseconds. - public static int TicksToMillis(int ticks, int tempo) - { - return (int)(ticks * (60000.0 / (tempo * QuarterTime))); - } - - /// - /// Converts the given midi tick duration into milliseconds. - /// - /// The duration in milliseconds - /// The current tempo in BPM. - /// The converted duration in midi ticks. - public static int MillisToTicks(int millis, int tempo) - { - return (int)(millis / (60000.0 / (tempo * QuarterTime))); - } - - /// - /// Converts a duration value to its ticks equivalent. - /// - /// - /// - public static int ToTicks(this Duration duration) - { - return ValueToTicks((int)duration); - } - - /// - /// Converts a numerical value to its ticks equivalent. - /// - /// the numerical proportion to convert. (i.E. timesignature denominator, note duration,...) - /// - public static int ValueToTicks(int duration) - { - float denomninator = duration; - if (denomninator < 0) - { - denomninator = 1 / -denomninator; - } - return (int)(QuarterTime * (4.0 / denomninator)); - } - - public static int ApplyDot(int ticks, bool doubleDotted) - { - if (doubleDotted) - { - return ticks + (ticks / 4) * 3; - } - return ticks + ticks / 2; - } - - public static int ApplyTuplet(int ticks, int numerator, int denominator) - { - return ticks * denominator / numerator; - } - - public static int DynamicToVelocity(DynamicValue dyn) - { - return (MinVelocity + ((int)(dyn) * VelocityIncrement)); - // switch(dynamicValue) - // { - // case PPP: return (MinVelocity + (0 * VelocityIncrement)); - // case PP: return (MinVelocity + (1 * VelocityIncrement)); - // case P: return (MinVelocity + (2 * VelocityIncrement)); - // case MP: return (MinVelocity + (3 * VelocityIncrement)); - // case MF: return (MinVelocity + (4 * VelocityIncrement)); - // case F: return (MinVelocity + (5 * VelocityIncrement)); - // case FF: return (MinVelocity + (6 * VelocityIncrement)); - // case FFF: return (MinVelocity + (7 * VelocityIncrement)); - // } - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/AlphaSynth.cs b/Source/AlphaTab/Audio/Synth/AlphaSynth.cs deleted file mode 100644 index 589f46d4b..000000000 --- a/Source/AlphaTab/Audio/Synth/AlphaSynth.cs +++ /dev/null @@ -1,492 +0,0 @@ -using System; -using AlphaTab.Audio.Synth.Bank; -using AlphaTab.Audio.Synth.Midi; -using AlphaTab.Audio.Synth.Synthesis; -using AlphaTab.Audio.Synth.Util; -using AlphaTab.IO; -using AlphaTab.Util; - -namespace AlphaTab.Audio.Synth -{ - /// - /// This is the main synthesizer component which can be used to - /// play a via a . - /// - public class AlphaSynth : IAlphaSynth - { - private readonly MidiFileSequencer _sequencer; - private readonly Synthesizer _synthesizer; - - private bool _isSoundFontLoaded; - private bool _isMidiLoaded; - private int _tickPosition; - private double _timePosition; - - /// - /// Gets the used for playing the generated samples. - /// - public ISynthOutput Output { get; } - - /// - public bool IsReady { get; private set; } - - /// - public bool IsReadyForPlayback => IsReady && _isSoundFontLoaded && _isMidiLoaded; - - /// - public PlayerState State { get; private set; } - - /// - public LogLevel LogLevel - { - get => Logger.LogLevel; - set => Logger.LogLevel = value; - } - - /// - public float MasterVolume - { - get => _synthesizer.MasterVolume; - set - { - value = SynthHelper.ClampF(value, SynthConstants.MinVolume, SynthConstants.MaxVolume); - _synthesizer.MasterVolume = value; - } - } - - /// - public float MetronomeVolume - { - get => _synthesizer.MetronomeVolume; - set - { - value = SynthHelper.ClampF(value, SynthConstants.MinVolume, SynthConstants.MaxVolume); - _synthesizer.MetronomeVolume = value; - } - } - - /// - public double PlaybackSpeed - { - get => _sequencer.PlaybackSpeed; - set - { - value = SynthHelper.ClampD(value, SynthConstants.MinPlaybackSpeed, SynthConstants.MaxPlaybackSpeed); - var oldSpeed = _sequencer.PlaybackSpeed; - _sequencer.PlaybackSpeed = value; - UpdateTimePosition(_timePosition * (oldSpeed / value)); - } - } - - /// - public int TickPosition - { - get => _tickPosition; - set - { - TimePosition = _sequencer.TickPositionToTimePosition(value); - } - } - - /// - public double TimePosition - { - get => _timePosition; - set - { - Logger.Debug("AlphaSynth", "Seeking to position " + value + "ms"); - - // tell the sequencer to jump to the given position - _sequencer.Seek(value); - - // update the internal position - UpdateTimePosition(value); - - // tell the output to reset the already synthesized buffers and request data again - Output.ResetSamples(); - } - } - - /// - public PlaybackRange PlaybackRange - { - get => _sequencer.PlaybackRange; - set - { - _sequencer.PlaybackRange = value; - if (value != null) - { - TickPosition = value.StartTick; - } - } - } - - /// - public bool IsLooping - { - get => _sequencer.IsLooping; - set => _sequencer.IsLooping = value; - } - - /// - /// Initializes a new instance of the class. - /// - /// The output to use for playing the generated samples. - public AlphaSynth(ISynthOutput output) - { - Logger.Debug("AlphaSynth", "Initializing player"); - State = PlayerState.Paused; - - Logger.Debug("AlphaSynth", "Creating output"); - Output = output; - Output.Ready += () => - { - IsReady = true; - CheckReadyForPlayback(); - }; - Output.Finished += () => - { - // stop everything - Stop(); - Logger.Debug("AlphaSynth", "Finished playback"); - OnFinished(_sequencer.IsLooping); - if (_sequencer.IsLooping) - { - Play(); - } - }; - Output.SampleRequest += () => - { - // synthesize buffer - _sequencer.FillMidiEventQueue(); - _synthesizer.Synthesize(); - // send it to output - Output.AddSamples(_synthesizer.SampleBuffer); - // tell sequencer to check whether its work is done - _sequencer.CheckForStop(); - }; - Output.SamplesPlayed += OnSamplesPlayed; - - Logger.Debug("AlphaSynth", "Creating synthesizer"); - _synthesizer = new Synthesizer(Output.SampleRate, SynthConstants.AudioChannels, 441, 3, 100); - - _sequencer = new MidiFileSequencer(_synthesizer); - _sequencer.Finished += Output.SequencerFinished; - - Logger.Debug("AlphaSynth", "Opening output"); - Output.Open(); - } - - /// - public void Play() - { - if (State == PlayerState.Playing || !IsReadyForPlayback) return; - - Logger.Debug("AlphaSynth", "Starting playback"); - State = PlayerState.Playing; - OnPlayerStateChanged(new PlayerStateChangedEventArgs(State)); - - Output.Play(); - } - - /// - public void Pause() - { - if (State == PlayerState.Paused || !IsReadyForPlayback) return; - - Logger.Debug("AlphaSynth", "Pausing playback"); - State = PlayerState.Paused; - OnPlayerStateChanged(new PlayerStateChangedEventArgs(State)); - - Output.Pause(); - _synthesizer.NoteOffAll(false); - } - - /// - public void PlayPause() - { - if (State == PlayerState.Playing || !IsReadyForPlayback) - { - Pause(); - } - else - { - Play(); - } - } - - /// - public void Stop() - { - if (!IsReadyForPlayback) return; - - Logger.Debug("AlphaSynth", "Stopping playback"); - Pause(); - _synthesizer.NoteOffAll(true); - TickPosition = _sequencer.PlaybackRange != null ? _sequencer.PlaybackRange.StartTick : 0; - } - - /// - public void LoadSoundFont(byte[] data) - { - Pause(); - - var input = ByteBuffer.FromBuffer(data); - try - { - Logger.Info("AlphaSynth", "Loading soundfont from bytes"); - var bank = new PatchBank(); - bank.LoadSf2(input); - _synthesizer.LoadBank(bank); - - _isSoundFontLoaded = true; - OnSoundFontLoaded(); - - Logger.Info("AlphaSynth", "soundFont successfully loaded"); - CheckReadyForPlayback(); - } - catch (Exception e) - { - Logger.Error("AlphaSynth", "Could not load soundfont from bytes " + e); - OnSoundFontLoadFailed(e); - } - } - - private void CheckReadyForPlayback() - { - if (IsReadyForPlayback) - { - OnReadyForPlayback(); - } - } - - /// - /// Loads the given midi file for playback. - /// - /// The midi file to load - // ReSharper disable once UnusedMember.Global - public void LoadMidi(MidiFile midiFile) - { - Stop(); - - try - { - Logger.Info("AlphaSynth", "Loading midi from model"); - - _sequencer.LoadMidi(midiFile); - _isMidiLoaded = true; - OnMidiLoaded(); - Logger.Info("AlphaSynth", "Midi successfully loaded"); - CheckReadyForPlayback(); - - TickPosition = 0; - } - catch (Exception e) - { - Logger.Error("AlphaSynth", "Could not load midi from model " + e); - OnMidiLoadFailed(e); - } - } - - /// - public void SetChannelMute(int channel, bool mute) - { - _synthesizer.SetChannelMute(channel, mute); - } - - /// - public void ResetChannelStates() - { - _synthesizer.ResetChannelStates(); - } - - /// - public void SetChannelSolo(int channel, bool solo) - { - _synthesizer.SetChannelSolo(channel, solo); - } - - /// - public void SetChannelVolume(int channel, double volume) - { - volume = SynthHelper.ClampD(volume, SynthConstants.MinVolume, SynthConstants.MaxVolume); - _synthesizer.SetChannelVolume(channel, volume); - } - - /// - public void SetChannelProgram(int channel, byte program) - { - program = SynthHelper.ClampB(program, SynthConstants.MinProgram, SynthConstants.MaxProgram); - _sequencer.SetChannelProgram(channel, program); - _synthesizer.SetChannelProgram(channel, program); - } - - private void OnSamplesPlayed(int sampleCount) - { - var playedMillis = (sampleCount / (double)_synthesizer.SampleRate) * 1000; - UpdateTimePosition(_timePosition + playedMillis); - } - - private void UpdateTimePosition(double timePosition) - { - // update the real positions - var currentTime = _timePosition = timePosition; - var currentTick = _tickPosition = _sequencer.TimePositionToTickPosition(currentTime); - - var endTime = _sequencer.EndTime; - var endTick = _sequencer.EndTick; - - Logger.Debug("AlphaSynth", "Position changed: (time: " + currentTime + "/" + endTime + ", tick: " + currentTick + "/" + endTime + ", Active Voices: " + _synthesizer.ActiveVoices + ", Free Voices: " + _synthesizer.FreeVoices + ")"); - OnPositionChanged(new PositionChangedEventArgs(currentTime, endTime, currentTick, endTick)); - } - - #region Events - - - /// - /// Occurs when the playback of the whole midi file finished. - /// - public event Action Finished; - private void OnFinished(bool isLooping) - { - var handler = Finished; - if (handler != null) handler(isLooping); - } - - /// - /// Occurs when the playback state changes. - /// - public event Action PlayerStateChanged; - private void OnPlayerStateChanged(PlayerStateChangedEventArgs e) - { - var handler = PlayerStateChanged; - if (handler != null) handler(e); - } - - /// - /// Occurs when the soundfont was successfully loaded. - /// - public event Action SoundFontLoaded; - private void OnSoundFontLoaded() - { - var handler = SoundFontLoaded; - if (handler != null) handler(); - } - - /// - /// Occurs when AlphaSynth is ready to start the playback. - /// This is the case once the is ready, a SoundFont was loaded and also a MidiFle is loaded. - /// - public event Action ReadyForPlayback; - private void OnReadyForPlayback() - { - var handler = ReadyForPlayback; - if (handler != null) handler(); - } - - /// - /// Occurs when the soundfont failed to be loaded. - /// - public event Action SoundFontLoadFailed; - private void OnSoundFontLoadFailed(Exception e) - { - var handler = SoundFontLoadFailed; - if (handler != null) handler(e); - } - - /// - /// Occurs when the midi file was successfully loaded. - /// - public event Action MidiLoaded; - private void OnMidiLoaded() - { - var handler = MidiLoaded; - if (handler != null) handler(); - } - - /// - /// Occurs when the midi failed to be loaded. - /// - public event Action MidiLoadFailed; - private void OnMidiLoadFailed(Exception e) - { - var handler = MidiLoadFailed; - if (handler != null) handler(e); - } - - /// - /// Occurs whenever the current time of the played audio changes. - /// - public event Action PositionChanged; - private void OnPositionChanged(PositionChangedEventArgs e) - { - var handler = PositionChanged; - if (handler != null) handler(e); - } - - #endregion - } - - #region EventArgs - - /// - /// Represents the info when the player state changes. - /// - public class PlayerStateChangedEventArgs - { - /// - /// The new state of the player. - /// - public PlayerState State { get; } - - /// - /// Initializes a new instance of the class. - /// - /// The state. - public PlayerStateChangedEventArgs(PlayerState state) - { - State = state; - } - } - - /// - /// Represents the info when the time in the synthesizer changes. - /// - public class PositionChangedEventArgs - { - /// - /// Gets the current time in milliseconds. - /// - public double CurrentTime { get; } - /// - /// Gets the length of the played song in milliseconds. - /// - public double EndTime { get; } - - /// - /// Gets the current time in midi ticks. - /// - public int CurrentTick { get; } - /// - /// Gets the length of the played song in midi ticks. - /// - public int EndTick { get; } - - /// - /// Initializes a new instance of the class. - /// - /// The current time. - /// The end time. - /// The current tick. - /// The end tick. - public PositionChangedEventArgs(double currentTime, double endTime, int currentTick, int endTick) - { - CurrentTime = currentTime; - EndTime = endTime; - CurrentTick = currentTick; - EndTick = endTick; - } - } - - #endregion -} \ No newline at end of file diff --git a/Source/AlphaTab/Audio/Synth/Bank/AssetManager.cs b/Source/AlphaTab/Audio/Synth/Bank/AssetManager.cs deleted file mode 100644 index b0b562010..000000000 --- a/Source/AlphaTab/Audio/Synth/Bank/AssetManager.cs +++ /dev/null @@ -1,58 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Collections; - -namespace AlphaTab.Audio.Synth.Bank -{ - class AssetManager - { - public FastList PatchAssets { get; } - public FastList SampleAssets { get; } - - public AssetManager() - { - PatchAssets = new FastList(); - SampleAssets = new FastList(); - } - - public PatchAsset FindPatch(string name) - { - foreach (var patchAsset in PatchAssets) - { - if (patchAsset.Name == name) - { - return patchAsset; - } - } - return null; - } - - public SampleDataAsset FindSample(string name) - { - foreach (var sampleDataAsset in SampleAssets) - { - if (sampleDataAsset.Name == name) - { - return sampleDataAsset; - } - } - return null; - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Bank/Components/Envelope.cs b/Source/AlphaTab/Audio/Synth/Bank/Components/Envelope.cs deleted file mode 100644 index 13df7d682..000000000 --- a/Source/AlphaTab/Audio/Synth/Bank/Components/Envelope.cs +++ /dev/null @@ -1,203 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using AlphaTab.Audio.Synth.Bank.Descriptors; -using AlphaTab.Audio.Synth.Ds; -using AlphaTab.Audio.Synth.Util; - -namespace AlphaTab.Audio.Synth.Bank.Components -{ - enum EnvelopeState - { - Delay = 0, - Attack = 1, - Hold = 2, - Decay = 3, - Sustain = 4, - Release = 5, - None = 6 - } - - class Envelope - { - private readonly EnvelopeStage[] _stages; - private int _index; - private EnvelopeStage _stage; - - public float Value { get; set; } - public EnvelopeState CurrentStage { get; private set; } - public float Depth { get; set; } - - public Envelope() - { - Value = 0; - Depth = 0; - _stages = new EnvelopeStage[7]; - for (int x = 0; x < _stages.Length; x++) - { - _stages[x] = new EnvelopeStage(); - _stages[x].Graph = Tables.EnvelopeTables(0); - } - _stages[3].Reverse = true; - _stages[5].Reverse = true; - _stages[6].Time = 100000000; - - CurrentStage = EnvelopeState.Delay; - _stage = _stages[(int)CurrentStage]; - } - - public void QuickSetupSf2(int sampleRate, int note, short keyNumToHold, short keyNumToDecay, bool isVolumeEnvelope, EnvelopeDescriptor envelopeInfo) - { - Depth = envelopeInfo.Depth; - // Delay - _stages[0].Offset = 0; - _stages[0].Scale = 0; - _stages[0].Time = Math.Max(0, (sampleRate * (envelopeInfo.DelayTime))); - // Attack - _stages[1].Offset = envelopeInfo.StartLevel; - _stages[1].Scale = envelopeInfo.PeakLevel - envelopeInfo.StartLevel; - _stages[1].Time = Math.Max(0, (sampleRate * (envelopeInfo.AttackTime))); - _stages[1].Graph = Tables.EnvelopeTables(envelopeInfo.AttackGraph); - // Hold - _stages[2].Offset = 0; - _stages[2].Scale = envelopeInfo.PeakLevel; - _stages[2].Time = Math.Max(0, sampleRate * (envelopeInfo.HoldTime) * Math.Pow(2, ((60 - note) * keyNumToHold) / 1200.0)); - // Decay - _stages[3].Offset = envelopeInfo.SustainLevel; - _stages[3].Scale = envelopeInfo.PeakLevel - envelopeInfo.SustainLevel; - if (envelopeInfo.SustainLevel == envelopeInfo.PeakLevel) - _stages[3].Time = 0; - else - _stages[3].Time = Math.Max(0, (sampleRate * (envelopeInfo.DecayTime) * Math.Pow(2, ((60 - note) * keyNumToDecay) / 1200.0))); - _stages[3].Graph = Tables.EnvelopeTables(envelopeInfo.DecayGraph); - // Sustain - _stages[4].Offset = 0; - _stages[4].Scale = envelopeInfo.SustainLevel; - _stages[4].Time = (sampleRate * envelopeInfo.SustainTime); - // Release - _stages[5].Scale = _stages[3].Time == 0 && _stages[4].Time == 0 ? envelopeInfo.PeakLevel : _stages[4].Scale; - if (isVolumeEnvelope) - { - _stages[5].Offset = -100; - _stages[5].Scale += 100; - _stages[6].Scale = -100; - } - else - { - _stages[5].Offset = 0; - _stages[6].Scale = 0; - } - _stages[5].Time = Math.Max(0, (int)(sampleRate * (envelopeInfo.ReleaseTime))); - _stages[5].Graph = Tables.EnvelopeTables(envelopeInfo.ReleaseGraph); - - _index = 0; - Value = 0; - CurrentStage = EnvelopeState.Delay; - while (Math.Abs(_stages[(int)CurrentStage].Time) < 0.01) - { - CurrentStage++; - } - _stage = _stages[(int)CurrentStage]; - } - - public void Increment(int samples) - { - do - { - var neededSamples = (int)_stage.Time - _index; - if (neededSamples > samples) - { - _index += samples; - samples = 0; - } - else - { - _index = 0; - if (CurrentStage != EnvelopeState.None) - { - do - { - _stage = _stages[(int)++CurrentStage]; - } while (_stage.Time == 0); - } - samples -= neededSamples; - } - } while (samples > 0); - - var i = (int)(_stage.Graph.Length * (_index / (double)_stage.Time)); - if (_stage.Reverse) - Value = (1f - _stage.Graph[i]) * _stage.Scale + _stage.Offset; - else - Value = _stage.Graph[i] * _stage.Scale + _stage.Offset; - } - - public void Release(double lowerLimit) - { - if (Value <= lowerLimit) - { - _index = 0; - CurrentStage = EnvelopeState.None; - _stage = _stages[(int)CurrentStage]; - } - else if (CurrentStage < EnvelopeState.Release) - { - _index = 0; - CurrentStage = EnvelopeState.Release; - _stage = _stages[(int)CurrentStage]; - _stage.Scale = Value; - } - } - - public void ReleaseSf2VolumeEnvelope() - { - if (Value <= -100) - { - _index = 0; - CurrentStage = EnvelopeState.None; - _stage = _stages[(int)CurrentStage]; - } - else if (CurrentStage < EnvelopeState.Release) - { - _index = 0; - CurrentStage = EnvelopeState.Release; - _stage = _stages[(int)CurrentStage]; - _stage.Offset = -100; - _stage.Scale = 100 + Value; - } - } - } - - class EnvelopeStage - { - public double Time { get; set; } - public SampleArray Graph { get; set; } - public float Scale { get; set; } - public float Offset { get; set; } - public bool Reverse { get; set; } - - public EnvelopeStage() - { - Time = 0; - Graph = null; - Scale = 0; - Offset = 0; - Reverse = false; - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Bank/Components/Filter.cs b/Source/AlphaTab/Audio/Synth/Bank/Components/Filter.cs deleted file mode 100644 index b8340f7cf..000000000 --- a/Source/AlphaTab/Audio/Synth/Bank/Components/Filter.cs +++ /dev/null @@ -1,253 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using AlphaTab.Audio.Synth.Synthesis; -using AlphaTab.Audio.Synth.Bank.Descriptors; -using AlphaTab.Audio.Synth.Ds; -using AlphaTab.Audio.Synth.Util; - -namespace AlphaTab.Audio.Synth.Bank.Components -{ - enum FilterType - { - None = 0, - BiquadLowpass = 1, - BiquadHighpass = 2, - OnePoleLowpass = 3 - } - - class Filter - { - private float _a1; - private float _a2; - - private float _b1; - private float _b2; - - private float _m1; - private float _m2; - private float _m3; - private double _cutOff; - private double _resonance; - - public FilterType FilterMethod { get; private set; } - - public double CutOff - { - get { return _cutOff; } - set - { - _cutOff = value; - CoeffNeedsUpdating = true; - } - } - - public double Resonance - { - get { return _resonance; } - set - { - _resonance = value; - CoeffNeedsUpdating = true; - } - } - - public bool Enabled - { - get { return FilterMethod != FilterType.None; } - } - - public bool CoeffNeedsUpdating { get; private set; } - - public Filter() - { - _a1 = 0; - _a2 = 0; - _b1 = 0; - _b2 = 0; - _m1 = 0; - _m2 = 0; - _m3 = 0; - FilterMethod = FilterType.None; - CutOff = 0; - Resonance = 0; - } - - public void Disable() - { - FilterMethod = FilterType.None; - } - - public void QuickSetup(int sampleRate, int note, float velocity, FilterDescriptor filterInfo) - { - CoeffNeedsUpdating = false; - CutOff = filterInfo.CutOff; - Resonance = filterInfo.Resonance; - FilterMethod = filterInfo.FilterMethod; - _a1 = 0; - _a2 = 0; - _b1 = 0; - _b2 = 0; - _m1 = 0; - _m2 = 0; - _m3 = 0; - if (CutOff <= 0 || Resonance <= 0) - { - FilterMethod = FilterType.None; - } - if (FilterMethod != FilterType.None) - { - CutOff *= SynthHelper.CentsToPitch((note - filterInfo.RootKey) * filterInfo.KeyTrack + (int)(velocity * filterInfo.VelTrack)); - UpdateCoefficients(sampleRate); - } - } - - public float ApplyFilter(float sample) - { - switch (FilterMethod) - { - case FilterType.BiquadHighpass: - case FilterType.BiquadLowpass: - _m3 = sample - _a1 * _m1 - _a2 * _m2; - sample = _b2 * (_m3 + _m2) + _b1 * _m1; - _m2 = _m1; - _m1 = _m3; - return sample; - case FilterType.OnePoleLowpass: - _m1 += _a1 * (sample - _m1); - return _m1; - default: - return 0f; - } - } - - public void ApplyFilter(SampleArray data) - { - switch (FilterMethod) - { - case FilterType.BiquadHighpass: - case FilterType.BiquadLowpass: - for (int x = 0; x < data.Length; x++) - { - _m3 = data[x] - _a1 * _m1 - _a2 * _m2; - data[x] = _b2 * (_m3 + _m2) + _b1 * _m1; - _m2 = _m1; - _m1 = _m3; - } - break; - case FilterType.OnePoleLowpass: - for (int x = 0; x < data.Length; x++) - { - _m1 += _a1 * (data[x] - _m1); - data[x] = _m1; - } - break; - } - } - - public void ApplyFilterInterp(SampleArray data, int sampleRate) - { - float[] ic = GenerateFilterCoeff(CutOff / sampleRate, Resonance); - float a1_inc = (ic[0] - _a1) / data.Length; - float a2_inc = (ic[1] - _a2) / data.Length; - float b1_inc = (ic[2] - _b1) / data.Length; - float b2_inc = (ic[3] - _b2) / data.Length; - switch (FilterMethod) - { - case FilterType.BiquadHighpass: - case FilterType.BiquadLowpass: - for (int x = 0; x < data.Length; x++) - { - _a1 += a1_inc; - _a2 += a2_inc; - _b1 += b1_inc; - _b2 += b2_inc; - _m3 = data[x] - _a1 * _m1 - _a2 * _m2; - data[x] = _b2 * (_m3 + _m2) + _b1 * _m1; - _m2 = _m1; - _m1 = _m3; - } - _a1 = ic[0]; - _a2 = ic[1]; - _b1 = ic[2]; - _b2 = ic[3]; - break; - case FilterType.OnePoleLowpass: - for (int x = 0; x < data.Length; x++) - { - _a1 += a1_inc; - _m1 += _a1 * (data[x] - _m1); - data[x] = _m1; - } - _a1 = ic[0]; - break; - } - CoeffNeedsUpdating = false; - } - - public void UpdateCoefficients(int sampleRate) - { - float[] coeff = GenerateFilterCoeff(CutOff / sampleRate, Resonance); - _a1 = coeff[0]; - _a2 = coeff[1]; - _b1 = coeff[2]; - _b2 = coeff[3]; - CoeffNeedsUpdating = false; - } - - private float[] GenerateFilterCoeff(double fc, double q) - { - fc = SynthHelper.ClampD(fc, SynthConstants.DenormLimit, .49); - float[] coeff = new float[4]; - switch (FilterMethod) - { - case FilterType.BiquadLowpass: - { - double w0 = SynthConstants.TwoPi * fc; - double cosw0 = Math.Cos(w0); - double alpha = Math.Sin(w0) / (2.0 * q); - double a0inv = 1.0 / (1.0 + alpha); - coeff[0] = (float)(-2.0 * cosw0 * a0inv); - coeff[1] = (float)((1.0 - alpha) * a0inv); - coeff[2] = (float)((1.0 - cosw0) * a0inv * (1.0 / Math.Sqrt(q))); - coeff[3] = _b1 * 0.5f; - } - break; - case FilterType.BiquadHighpass: - { - double w0 = SynthConstants.TwoPi * fc; - double cosw0 = Math.Cos(w0); - double alpha = Math.Sin(w0) / (2.0 * q); - double a0inv = 1.0 / (1.0 + alpha); - double qinv = 1.0 / Math.Sqrt(q); - coeff[0] = (float)(-2.0 * cosw0 * a0inv); - coeff[1] = (float)((1.0 - alpha) * a0inv); - coeff[2] = (float)((-1.0 - cosw0) * a0inv * qinv); - coeff[3] = (float)((1.0 + cosw0) * a0inv * qinv * 0.5); - } - break; - case FilterType.OnePoleLowpass: - coeff[0] = 1.0f - (float)Math.Exp(-2.0 * Math.PI * fc); - break; - } - return coeff; - } - - } -} diff --git a/Source/AlphaTab/Audio/Synth/Bank/Components/Generators/DefaultGenerators.cs b/Source/AlphaTab/Audio/Synth/Bank/Components/Generators/DefaultGenerators.cs deleted file mode 100644 index 8cd30cb3b..000000000 --- a/Source/AlphaTab/Audio/Synth/Bank/Components/Generators/DefaultGenerators.cs +++ /dev/null @@ -1,30 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Audio.Synth.Bank.Descriptors; - -namespace AlphaTab.Audio.Synth.Bank.Components.Generators -{ - class DefaultGenerators - { - public static readonly Generator DefaultSine = new SineGenerator(new GeneratorDescriptor()); - public static readonly Generator DefaultSaw = new SawGenerator(new GeneratorDescriptor()); - public static readonly Generator DefaultSquare = new SquareGenerator(new GeneratorDescriptor()); - public static readonly Generator DefaultTriangle = new TriangleGenerator(new GeneratorDescriptor()); - } -} diff --git a/Source/AlphaTab/Audio/Synth/Bank/Components/Generators/Generator.cs b/Source/AlphaTab/Audio/Synth/Bank/Components/Generators/Generator.cs deleted file mode 100644 index 0cb469d29..000000000 --- a/Source/AlphaTab/Audio/Synth/Bank/Components/Generators/Generator.cs +++ /dev/null @@ -1,126 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using AlphaTab.Audio.Synth.Bank.Descriptors; -using AlphaTab.Audio.Synth.Ds; - -namespace AlphaTab.Audio.Synth.Bank.Components.Generators -{ - enum LoopMode - { - NoLoop = 0, - OneShot = 1, - Continuous = 2, - LoopUntilNoteOff = 3 - } - - enum GeneratorState - { - PreLoop = 0, - Loop = 1, - PostLoop = 2, - Finished = 3 - } - - abstract class Generator - { - public LoopMode LoopMode { get; set; } - public double LoopStartPhase { get; set; } - public double LoopEndPhase { get; set; } - public double StartPhase { get; set; } - public double EndPhase { get; set; } - public double Offset { get; set; } - public double Period { get; set; } - public double Frequency { get; set; } - public short RootKey { get; set; } - public short KeyTrack { get; set; } - public short VelocityTrack { get; set; } - public short Tune { get; set; } - - - protected Generator(GeneratorDescriptor description) - { - LoopMode = description.LoopMethod; - LoopStartPhase = description.LoopStartPhase; - LoopEndPhase = description.LoopEndPhase; - StartPhase = description.StartPhase; - EndPhase = description.EndPhase; - Offset = description.Offset; - Period = description.Period; - Frequency = 0; - RootKey = description.RootKey; - KeyTrack = description.KeyTrack; - VelocityTrack = description.VelTrack; - Tune = description.Tune; - } - - public void Release(GeneratorParameters generatorParams) - { - if (LoopMode == LoopMode.LoopUntilNoteOff) - { - generatorParams.CurrentState = GeneratorState.PostLoop; - generatorParams.CurrentStart = StartPhase; - generatorParams.CurrentEnd = EndPhase; - } - } - - public abstract float GetValue(double phase); - public virtual void GetValues(GeneratorParameters generatorParams, SampleArray blockBuffer, double increment) - { - var proccessed = 0; - do - { - var samplesAvailable = (int)(Math.Ceiling((generatorParams.CurrentEnd - generatorParams.Phase) / increment)); - if (samplesAvailable > blockBuffer.Length - proccessed) - { - while (proccessed < blockBuffer.Length) - { - blockBuffer[proccessed++] = GetValue(generatorParams.Phase); - generatorParams.Phase += increment; - } - } - else - { - var endProccessed = proccessed + samplesAvailable; - while (proccessed < endProccessed) - { - blockBuffer[proccessed++] = GetValue(generatorParams.Phase); - generatorParams.Phase += increment; - } - switch (generatorParams.CurrentState) - { - case GeneratorState.PreLoop: - generatorParams.CurrentStart = LoopStartPhase; - generatorParams.CurrentEnd = LoopEndPhase; - generatorParams.CurrentState = GeneratorState.Loop; - break; - case GeneratorState.Loop: - generatorParams.Phase += generatorParams.CurrentStart - generatorParams.CurrentEnd; - break; - case GeneratorState.PostLoop: - generatorParams.CurrentState = GeneratorState.Finished; - while (proccessed < blockBuffer.Length) - blockBuffer[proccessed++] = 0; - break; - } - } - } while (proccessed < blockBuffer.Length); - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Bank/Components/Generators/GeneratorParameters.cs b/Source/AlphaTab/Audio/Synth/Bank/Components/Generators/GeneratorParameters.cs deleted file mode 100644 index 849c60ec5..000000000 --- a/Source/AlphaTab/Audio/Synth/Bank/Components/Generators/GeneratorParameters.cs +++ /dev/null @@ -1,75 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -namespace AlphaTab.Audio.Synth.Bank.Components.Generators -{ - class GeneratorParameters - { - public double Phase { get; set; } - public double CurrentStart { get; set; } - public double CurrentEnd { get; set; } - public GeneratorState CurrentState { get; set; } - - public GeneratorParameters() - { - Phase = 0; - CurrentStart = 0; - CurrentEnd = 0; - CurrentState = 0; - } - - public void QuickSetup(Generator generator) - { - CurrentStart = generator.StartPhase; - Phase = CurrentStart + generator.Offset; - switch (generator.LoopMode) - { - case LoopMode.Continuous: - case LoopMode.LoopUntilNoteOff: - if (Phase >= generator.EndPhase) - {//phase is greater than the end index so generator is finished - CurrentState = GeneratorState.Finished; - } - else if (Phase >= generator.LoopEndPhase) - {//phase is greater than the loop end point so generator is in post loop - CurrentState = GeneratorState.PostLoop; - CurrentEnd = generator.EndPhase; - } - else if (Phase >= generator.LoopStartPhase) - {//phase is greater than loop start so we are inside the loop - CurrentState = GeneratorState.Loop; - CurrentEnd = generator.LoopEndPhase; - CurrentStart = generator.LoopStartPhase; - } - else - {//phase is less than the loop so generator is in pre loop - CurrentState = GeneratorState.PreLoop; - CurrentEnd = generator.LoopStartPhase; - } - break; - default: - CurrentEnd = generator.EndPhase; - if (Phase >= CurrentEnd) - CurrentState = GeneratorState.Finished; - else - CurrentState = GeneratorState.PostLoop; - break; - } - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Bank/Components/Generators/SampleGenerator.cs b/Source/AlphaTab/Audio/Synth/Bank/Components/Generators/SampleGenerator.cs deleted file mode 100644 index 6c0591359..000000000 --- a/Source/AlphaTab/Audio/Synth/Bank/Components/Generators/SampleGenerator.cs +++ /dev/null @@ -1,105 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using AlphaTab.Audio.Synth.Bank.Descriptors; -using AlphaTab.Audio.Synth.Ds; -using AlphaTab.Audio.Synth.Util; - -namespace AlphaTab.Audio.Synth.Bank.Components.Generators -{ - class SampleGenerator : Generator - { - public PcmData Samples { get; set; } - - public SampleGenerator() - : base(new GeneratorDescriptor()) - { - } - - public override float GetValue(double phase) - { - return Samples[(int)(phase)]; - } - - public override void GetValues(GeneratorParameters generatorParams, SampleArray blockBuffer, double increment) - { - int proccessed = 0; - do - { - int samplesAvailable = (int)Math.Ceiling((generatorParams.CurrentEnd - generatorParams.Phase) / increment); - if (samplesAvailable > blockBuffer.Length - proccessed) - { - Interpolate(generatorParams, blockBuffer, increment, proccessed, blockBuffer.Length); - return; //proccessed = blockBuffer.Length; - } - else - { - int endProccessed = proccessed + samplesAvailable; - Interpolate(generatorParams, blockBuffer, increment, proccessed, endProccessed); - proccessed = endProccessed; - switch (generatorParams.CurrentState) - { - case GeneratorState.PreLoop: - generatorParams.CurrentStart = LoopStartPhase; - generatorParams.CurrentEnd = LoopEndPhase; - generatorParams.CurrentState = GeneratorState.Loop; - break; - case GeneratorState.Loop: - generatorParams.Phase += generatorParams.CurrentStart - generatorParams.CurrentEnd; - break; - case GeneratorState.PostLoop: - generatorParams.CurrentState = GeneratorState.Finished; - while (proccessed < blockBuffer.Length) - blockBuffer[proccessed++] = 0f; - break; - } - } - } - while (proccessed < blockBuffer.Length); - } - - private void Interpolate(GeneratorParameters generatorParams, SampleArray blockBuffer, double increment, int start, int end) - { - double _end = generatorParams.CurrentState == GeneratorState.Loop ? this.LoopEndPhase - 1 : this.EndPhase - 1; - int index; - float s1, s2, mu; - while (start < end && generatorParams.Phase < _end)//do this until we reach an edge case or fill the buffer - { - index = (int)generatorParams.Phase; - s1 = Samples[index]; - s2 = Samples[index + 1]; - mu = (float)(generatorParams.Phase - index); - blockBuffer[start++] = s1 + mu * (s2 - s1); - generatorParams.Phase += increment; - } - while (start < end)//edge case, if in loop wrap to loop start else use duplicate sample - { - index = (int)generatorParams.Phase; - s1 = Samples[index]; - if (generatorParams.CurrentState == GeneratorState.Loop) - s2 = Samples[(int)generatorParams.CurrentStart]; - else - s2 = s1; - mu = (float)(generatorParams.Phase - index); - blockBuffer[start++] = s1 + mu * (s2 - s1); - generatorParams.Phase += increment; - } - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Bank/Components/Generators/SawGenerator.cs b/Source/AlphaTab/Audio/Synth/Bank/Components/Generators/SawGenerator.cs deleted file mode 100644 index 507759f32..000000000 --- a/Source/AlphaTab/Audio/Synth/Bank/Components/Generators/SawGenerator.cs +++ /dev/null @@ -1,49 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using AlphaTab.Audio.Synth.Bank.Descriptors; - -namespace AlphaTab.Audio.Synth.Bank.Components.Generators -{ - class SawGenerator : Generator - { - public SawGenerator(GeneratorDescriptor description) - : base(description) - { - if (EndPhase < 0) - EndPhase = 1; - if (StartPhase < 0) - StartPhase = 0; - if (LoopEndPhase < 0) - LoopEndPhase = EndPhase; - if (LoopStartPhase < 0) - LoopStartPhase = StartPhase; - if (Period < 0) - Period = 1; - if (RootKey < 0) - RootKey = 69; - Frequency = 440; - } - - public override float GetValue(double phase) - { - return (float) (2.0 * (phase - Math.Floor(phase + 0.5))); - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Bank/Components/Generators/SineGenerator.cs b/Source/AlphaTab/Audio/Synth/Bank/Components/Generators/SineGenerator.cs deleted file mode 100644 index a05fb8d49..000000000 --- a/Source/AlphaTab/Audio/Synth/Bank/Components/Generators/SineGenerator.cs +++ /dev/null @@ -1,50 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using AlphaTab.Audio.Synth.Bank.Descriptors; -using AlphaTab.Audio.Synth.Util; - -namespace AlphaTab.Audio.Synth.Bank.Components.Generators -{ - class SineGenerator : Generator - { - public SineGenerator(GeneratorDescriptor description) - : base(description) - { - if (EndPhase < 0) - EndPhase = SynthConstants.TwoPi; - if (StartPhase < 0) - StartPhase = 0; - if (LoopEndPhase < 0) - LoopEndPhase = EndPhase; - if (LoopStartPhase < 0) - LoopStartPhase = StartPhase; - if (Period < 0) - Period = SynthConstants.TwoPi; - if (RootKey < 0) - RootKey = 69; - Frequency = 440; - } - - public override float GetValue(double phase) - { - return (float)Math.Sin(phase); - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Bank/Components/Generators/SquareGenerator.cs b/Source/AlphaTab/Audio/Synth/Bank/Components/Generators/SquareGenerator.cs deleted file mode 100644 index 4a54d269b..000000000 --- a/Source/AlphaTab/Audio/Synth/Bank/Components/Generators/SquareGenerator.cs +++ /dev/null @@ -1,50 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using AlphaTab.Audio.Synth.Bank.Descriptors; -using AlphaTab.Audio.Synth.Util; - -namespace AlphaTab.Audio.Synth.Bank.Components.Generators -{ - class SquareGenerator : Generator - { - public SquareGenerator(GeneratorDescriptor description) - : base(description) - { - if (EndPhase < 0) - EndPhase = SynthConstants.TwoPi; - if (StartPhase < 0) - StartPhase = 0; - if (LoopEndPhase < 0) - LoopEndPhase = EndPhase; - if (LoopStartPhase < 0) - LoopStartPhase = StartPhase; - if (Period < 0) - Period = SynthConstants.TwoPi; - if (RootKey < 0) - RootKey = 69; - Frequency = 440; - } - - public override float GetValue(double phase) - { - return Math.Sign(Math.Sin(phase)); - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Bank/Components/Generators/TriangleGenerator.cs b/Source/AlphaTab/Audio/Synth/Bank/Components/Generators/TriangleGenerator.cs deleted file mode 100644 index 4d2529a26..000000000 --- a/Source/AlphaTab/Audio/Synth/Bank/Components/Generators/TriangleGenerator.cs +++ /dev/null @@ -1,49 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using AlphaTab.Audio.Synth.Bank.Descriptors; - -namespace AlphaTab.Audio.Synth.Bank.Components.Generators -{ - class TriangleGenerator : Generator - { - public TriangleGenerator(GeneratorDescriptor description) - : base(description) - { - if (EndPhase < 0) - EndPhase = 1.25; - if (StartPhase < 0) - StartPhase = 0.25; - if (LoopEndPhase < 0) - LoopEndPhase = EndPhase; - if (LoopStartPhase < 0) - LoopStartPhase = StartPhase; - if (Period < 0) - Period = 1; - if (RootKey < 0) - RootKey = 69; - Frequency = 440; - } - - public override float GetValue(double phase) - { - return (float) (Math.Abs(phase - Math.Floor(phase + 0.5)) * 4.0 - 1.0); - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Bank/Components/Generators/WhiteNoiseGenerator.cs b/Source/AlphaTab/Audio/Synth/Bank/Components/Generators/WhiteNoiseGenerator.cs deleted file mode 100644 index 88cc5cc18..000000000 --- a/Source/AlphaTab/Audio/Synth/Bank/Components/Generators/WhiteNoiseGenerator.cs +++ /dev/null @@ -1,50 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Audio.Synth.Bank.Descriptors; -using AlphaTab.Platform; - -namespace AlphaTab.Audio.Synth.Bank.Components.Generators -{ - class WhiteNoiseGenerator : Generator - { - - public WhiteNoiseGenerator(GeneratorDescriptor description) - : base(description) - { - if (EndPhase < 0) - EndPhase = 1; - if (StartPhase < 0) - StartPhase = 0; - if (LoopEndPhase < 0) - LoopEndPhase = EndPhase; - if (LoopStartPhase < 0) - LoopStartPhase = StartPhase; - if (Period < 0) - Period = 1; - if (RootKey < 0) - RootKey = 69; - Frequency = 440; - } - - public override float GetValue(double phase) - { - return (float) ((Platform.Platform.RandomDouble() * 2.0) - 1.0); - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Bank/Components/Lfo.cs b/Source/AlphaTab/Audio/Synth/Bank/Components/Lfo.cs deleted file mode 100644 index 255457dc6..000000000 --- a/Source/AlphaTab/Audio/Synth/Bank/Components/Lfo.cs +++ /dev/null @@ -1,103 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Audio.Synth.Bank.Components.Generators; -using AlphaTab.Audio.Synth.Bank.Descriptors; - -namespace AlphaTab.Audio.Synth.Bank.Components -{ - enum LfoState - { - Delay = 0, - Sustain = 1 - } - - class Lfo - { - private double _phase; - private double _increment; - private int _delayTime; - private Generators.Generator _generator; - - public float Frequency { get; private set; } - public LfoState CurrentState { get; private set; } - public double Value { get; set; } - public double Depth { get; set; } - - public Lfo() - { - CurrentState = LfoState.Delay; - _generator = DefaultGenerators.DefaultSine; - _delayTime = 0; - _increment = 0; - _phase = 0; - Frequency = 0; - CurrentState = 0; - Value = 0; - Depth = 0; - } - - public void QuickSetup(int sampleRate, LfoDescriptor lfoInfo) - { - _generator = lfoInfo.Generator; - _delayTime = (int)(sampleRate * lfoInfo.DelayTime); - Frequency = lfoInfo.Frequency; - _increment = _generator.Period * Frequency / sampleRate; - Depth = lfoInfo.Depth; - Reset(); - } - - public void Increment(int amount) - { - if (CurrentState == LfoState.Delay) - { - _phase -= amount; - while (_phase <= 0.0) - { - _phase = _generator.LoopStartPhase + _increment * -_phase; - Value = _generator.GetValue(_phase); - CurrentState = LfoState.Sustain; - } - } - else - { - _phase += _increment * amount; - while (_phase >= _generator.LoopEndPhase) - { - _phase = _generator.LoopStartPhase + (_phase - _generator.LoopEndPhase) % (_generator.LoopEndPhase - _generator.LoopStartPhase); - } - Value = _generator.GetValue(_phase); - } - } - - public void Reset() - { - Value = 0; - if (_delayTime > 0) - { - _phase = _delayTime; - CurrentState = LfoState.Delay; - } - else - { - _phase = 0.0f; - CurrentState = LfoState.Sustain; - } - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Bank/Components/PanComponent.cs b/Source/AlphaTab/Audio/Synth/Bank/Components/PanComponent.cs deleted file mode 100644 index d24d062ec..000000000 --- a/Source/AlphaTab/Audio/Synth/Bank/Components/PanComponent.cs +++ /dev/null @@ -1,62 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using AlphaTab.Audio.Synth.Synthesis; -using AlphaTab.Audio.Synth.Util; - -namespace AlphaTab.Audio.Synth.Bank.Components -{ - enum PanFormulaEnum - { - Neg3dBCenter = 0, - Neg6dBCenter = 1, - ZeroCenter = 2 - } - - class PanComponent - { - public float Left { get; set; } - public float Right { get; set; } - - public void SetValue(float value, PanFormulaEnum formula) - { - value = SynthHelper.ClampF(value, -1, 1); - double dvalue; - switch (formula) - { - case PanFormulaEnum.Neg3dBCenter: - dvalue = SynthConstants.HalfPi * (value + 1) / 2.0; - Left = (float)Math.Cos(dvalue); - Right = (float)Math.Sin(dvalue); - break; - case PanFormulaEnum.Neg6dBCenter: - Left = (float)(.5 + value * -.5); - Right = (float)(.5 + value * .5); - break; - case PanFormulaEnum.ZeroCenter: - dvalue = SynthConstants.HalfPi * (value + 1.0) / 2.0; - Left = (float)(Math.Cos(dvalue) / SynthConstants.InverseSqrtOfTwo); - Right = (float)(Math.Sin(dvalue) / SynthConstants.InverseSqrtOfTwo); - break; - default: - throw new Exception("Invalid pan law selected."); - } - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Bank/Descriptors/EnvelopeDescriptor.cs b/Source/AlphaTab/Audio/Synth/Bank/Descriptors/EnvelopeDescriptor.cs deleted file mode 100644 index 6103ea641..000000000 --- a/Source/AlphaTab/Audio/Synth/Bank/Descriptors/EnvelopeDescriptor.cs +++ /dev/null @@ -1,68 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -namespace AlphaTab.Audio.Synth.Bank.Descriptors -{ - class EnvelopeDescriptor - { - public float DelayTime { get; set; } - public float AttackTime { get; set; } - public short AttackGraph { get; set; } - public float HoldTime { get; set; } - public float DecayTime { get; set; } - public short DecayGraph { get; set; } - public float SustainTime { get; set; } - public float ReleaseTime { get; set; } - public short ReleaseGraph { get; set; } - public float SustainLevel { get; set; } - public float PeakLevel { get; set; } - public float StartLevel { get; set; } - public float Depth { get; set; } - public float Vel2Delay { get; set; } - public float Vel2Attack { get; set; } - public float Vel2Hold { get; set; } - public float Vel2Decay { get; set; } - public float Vel2Sustain { get; set; } - public float Vel2Release { get; set; } - public float Vel2Depth { get; set; } - - public EnvelopeDescriptor() - { - DelayTime = 0; - AttackTime = 0; - AttackGraph = 1; - HoldTime = 0; - DecayTime = 0; - DecayGraph = 1; - SustainTime = 3600; - ReleaseTime = 0; - ReleaseGraph = 1; - SustainLevel = 0; - PeakLevel = 1; - StartLevel = 0; - Depth = 1; - Vel2Delay = 0; - Vel2Attack = 0; - Vel2Hold = 0; - Vel2Decay = 0; - Vel2Sustain = 0; - Vel2Release = 0; - Vel2Depth = 0; - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Bank/Descriptors/FilterDescriptor.cs b/Source/AlphaTab/Audio/Synth/Bank/Descriptors/FilterDescriptor.cs deleted file mode 100644 index cbfe21c5f..000000000 --- a/Source/AlphaTab/Audio/Synth/Bank/Descriptors/FilterDescriptor.cs +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Audio.Synth.Bank.Components; - -namespace AlphaTab.Audio.Synth.Bank.Descriptors -{ - class FilterDescriptor - { - public FilterType FilterMethod { get; set; } - public float CutOff { get; set; } - public float Resonance { get; set; } - public short RootKey { get; set; } - public short KeyTrack { get; set; } - public short VelTrack { get; set; } - - public FilterDescriptor() - { - FilterMethod = FilterType.None; - CutOff = -1; - Resonance = 1; - RootKey = 60; - KeyTrack = 0; - VelTrack = 0; - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Bank/Descriptors/GeneratorDescriptor.cs b/Source/AlphaTab/Audio/Synth/Bank/Descriptors/GeneratorDescriptor.cs deleted file mode 100644 index 1973fedea..000000000 --- a/Source/AlphaTab/Audio/Synth/Bank/Descriptors/GeneratorDescriptor.cs +++ /dev/null @@ -1,66 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Audio.Synth.Bank.Components.Generators; - -namespace AlphaTab.Audio.Synth.Bank.Descriptors -{ - enum Waveform - { - Sine = 0, - Square = 1, - Saw = 2, - Triangle = 3, - SampleData = 4, - WhiteNoise = 5 - } - - class GeneratorDescriptor - { - public LoopMode LoopMethod { get; set; } - public Waveform SamplerType { get; set; } - public string AssetName { get; set; } - public double EndPhase { get; set; } - public double StartPhase { get; set; } - public double LoopEndPhase { get; set; } - public double LoopStartPhase { get; set; } - public double Offset { get; set; } - public double Period { get; set; } - public short RootKey { get; set; } - public short KeyTrack { get; set; } - public short VelTrack { get; set; } - public short Tune { get; set; } - - public GeneratorDescriptor() - { - LoopMethod = LoopMode.NoLoop; - SamplerType = Waveform.Sine; - AssetName = "null"; - EndPhase = -1; - StartPhase = -1; - LoopEndPhase = -1; - LoopStartPhase = -1; - Offset = 0; - Period = -1; - RootKey = -1; - KeyTrack = 100; - VelTrack = 0; - Tune = 0; - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Bank/Descriptors/LfoDescriptor.cs b/Source/AlphaTab/Audio/Synth/Bank/Descriptors/LfoDescriptor.cs deleted file mode 100644 index e3b0b246a..000000000 --- a/Source/AlphaTab/Audio/Synth/Bank/Descriptors/LfoDescriptor.cs +++ /dev/null @@ -1,39 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Audio.Synth.Bank.Components.Generators; -using AlphaTab.Audio.Synth.Util; - -namespace AlphaTab.Audio.Synth.Bank.Descriptors -{ - class LfoDescriptor - { - public float DelayTime { get; set; } - public float Frequency { get; set; } - public float Depth { get; set; } - public Components.Generators.Generator Generator { get; set; } - - public LfoDescriptor() - { - DelayTime = 0; - Frequency = SynthConstants.DefaultLfoFrequency; - Depth = 1; - Generator = DefaultGenerators.DefaultSine; - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Bank/Patch/MultiPatch.cs b/Source/AlphaTab/Audio/Synth/Bank/Patch/MultiPatch.cs deleted file mode 100644 index 581c7f2c0..000000000 --- a/Source/AlphaTab/Audio/Synth/Bank/Patch/MultiPatch.cs +++ /dev/null @@ -1,207 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Audio.Synth.Sf2; -using AlphaTab.Audio.Synth.Synthesis; -using AlphaTab.Platform; - -namespace AlphaTab.Audio.Synth.Bank.Patch -{ - class MultiPatch : Patch - { - private IntervalType _intervalType; - private PatchInterval[] _intervalList; - - public MultiPatch(string name) - : base(name) - { - _intervalType = IntervalType.ChannelKeyVelocity; - } - - public int FindPatches(int channel, int key, int velocity, Patch[] layers) - { - int count = 0; - switch (_intervalType) - { - case IntervalType.ChannelKeyVelocity: - for (int x = 0; x < _intervalList.Length; x++) - { - if (_intervalList[x].CheckAllIntervals(channel, key, velocity)) - { - layers[count++] = _intervalList[x].Patch; - if (count == layers.Length) - break; - } - } - break; - case IntervalType.ChannelKey: - for (int x = 0; x < _intervalList.Length; x++) - { - if (_intervalList[x].CheckChannelAndKey(channel, key)) - { - layers[count++] = _intervalList[x].Patch; - if (count == layers.Length) - break; - } - } - break; - case IntervalType.KeyVelocity: - for (int x = 0; x < _intervalList.Length; x++) - { - if (_intervalList[x].CheckKeyAndVelocity(key, velocity)) - { - layers[count++] = _intervalList[x].Patch; - if (count == layers.Length) - break; - } - } - break; - case IntervalType.Key: - for (int x = 0; x < _intervalList.Length; x++) - { - if (_intervalList[x].CheckKey(key)) - { - layers[count++] = _intervalList[x].Patch; - if (count == layers.Length) - break; - } - } - break; - } - return count; - } - - public override bool Start(VoiceParameters voiceparams) - { - return false; - } - - public override void Process(VoiceParameters voiceparams, int startIndex, int endIndex, bool isMuted, bool isSilentProcess) - { - - } - - public override void Stop(VoiceParameters voiceparams) - { - } - - public void LoadSf2(Sf2Region[] regions, AssetManager assets) - { - _intervalList = new PatchInterval[regions.Length]; - for (int x = 0; x < regions.Length; x++) - { - byte loKey; - byte hiKey; - byte loVel; - byte hiVel; - loKey = Platform.Platform.ToUInt8(regions[x].Generators[(int)GeneratorEnum.KeyRange] & 0xFF); - hiKey = Platform.Platform.ToUInt8((regions[x].Generators[(int)GeneratorEnum.KeyRange] >> 8) & 0xFF); - loVel = Platform.Platform.ToUInt8(regions[x].Generators[(int)GeneratorEnum.VelocityRange] & 0xFF); - hiVel = Platform.Platform.ToUInt8((regions[x].Generators[(int)GeneratorEnum.VelocityRange] >> 8) & 0xFF); - - var sf2 = new Sf2Patch(Name + "_" + x); - sf2.Load(regions[x], assets); - _intervalList[x] = new PatchInterval(sf2, 0, 15, loKey, hiKey, loVel, hiVel); - } - DetermineIntervalType(); - } - - private void DetermineIntervalType() - { - bool checkChannel = false; - bool checkVelocity = false; - for (int x = 0; x < _intervalList.Length; x++) - { - if (_intervalList[x].StartChannel != 0 || _intervalList[x].EndChannel != 15) - { - checkChannel = true; - if (checkChannel && checkVelocity) - break; - } - if (_intervalList[x].StartVelocity != 0 || _intervalList[x].EndVelocity != 127) - { - checkVelocity = true; - if (checkChannel && checkVelocity) - break; - } - } - if (checkChannel & checkVelocity) - _intervalType = IntervalType.ChannelKeyVelocity; - else if (checkChannel) - _intervalType = IntervalType.ChannelKey; - else if (checkVelocity) - _intervalType = IntervalType.KeyVelocity; - else - _intervalType = IntervalType.Key; - } - } - - enum IntervalType - { - ChannelKeyVelocity = 0, - ChannelKey = 1, - KeyVelocity = 2, - Key = 3 - } - - class PatchInterval - { - public Patch Patch { get; set; } - public byte StartChannel { get; set; } - public byte StartKey { get; set; } - public byte StartVelocity { get; set; } - public byte EndChannel { get; set; } - public byte EndKey { get; set; } - public byte EndVelocity { get; set; } - - public PatchInterval(Patch patch, byte startChannel, byte endChannel, byte startKey, byte endKey, byte startVelocity, byte endVelocity) - { - Patch = patch; - StartChannel = startChannel; - EndChannel = endChannel; - StartKey = startKey; - EndKey = endKey; - StartVelocity = startVelocity; - EndVelocity = endVelocity; - } - - public bool CheckAllIntervals(int channel, int key, int velocity) - { - return (channel >= StartChannel && channel <= EndChannel) && - (key >= StartKey && key <= EndKey) && - (velocity >= StartVelocity && velocity <= EndVelocity); - } - - public bool CheckChannelAndKey(int channel, int key) - { - return (channel >= StartChannel && channel <= EndChannel) && - (key >= StartKey && key <= EndKey); - } - - public bool CheckKeyAndVelocity(int key, int velocity) - { - return (key >= StartKey && key <= EndKey) && - (velocity >= StartVelocity && velocity <= EndVelocity); - } - - public bool CheckKey(int key) - { - return (key >= StartKey && key <= EndKey); - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Bank/Patch/Patch.cs b/Source/AlphaTab/Audio/Synth/Bank/Patch/Patch.cs deleted file mode 100644 index 00c1449b8..000000000 --- a/Source/AlphaTab/Audio/Synth/Bank/Patch/Patch.cs +++ /dev/null @@ -1,40 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Audio.Synth.Synthesis; - -namespace AlphaTab.Audio.Synth.Bank.Patch -{ - abstract class Patch - { - public int ExclusiveGroupTarget { get; set; } - public int ExclusiveGroup { get; set; } - public string Name { get; private set; } - - protected Patch(string name) - { - Name = name; - ExclusiveGroup = 0; - ExclusiveGroupTarget = 0; - } - - public abstract bool Start(VoiceParameters voiceparams); - public abstract void Process(VoiceParameters voiceparams, int startIndex, int endIndex, bool isMuted, bool isSilentProcess); - public abstract void Stop(VoiceParameters voiceparams); - } -} diff --git a/Source/AlphaTab/Audio/Synth/Bank/Patch/Sf2Patch.cs b/Source/AlphaTab/Audio/Synth/Bank/Patch/Sf2Patch.cs deleted file mode 100644 index 5b0f003b9..000000000 --- a/Source/AlphaTab/Audio/Synth/Bank/Patch/Sf2Patch.cs +++ /dev/null @@ -1,350 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using AlphaTab.Audio.Synth.Bank.Components; -using AlphaTab.Audio.Synth.Bank.Components.Generators; -using AlphaTab.Audio.Synth.Bank.Descriptors; -using AlphaTab.Audio.Synth.Sf2; -using AlphaTab.Audio.Synth.Synthesis; -using AlphaTab.Audio.Synth.Util; -using Generator = AlphaTab.Audio.Synth.Bank.Components.Generators.Generator; - -namespace AlphaTab.Audio.Synth.Bank.Patch -{ - class Sf2Patch : Patch - { - private int iniFilterFc; - private double filterQ; - private float initialAttn; - private short keyOverride; - private short velOverride; - private short keynumToModEnvHold; - private short keynumToModEnvDecay; - private short keynumToVolEnvHold; - private short keynumToVolEnvDecay; - private PanComponent pan; - private short modLfoToPitch; - private short vibLfoToPitch; - private short modEnvToPitch; - private short modLfoToFilterFc; - private short modEnvToFilterFc; - private float modLfoToVolume; - private SampleGenerator gen; - private EnvelopeDescriptor mod_env; - private EnvelopeDescriptor vel_env; - private LfoDescriptor mod_lfo; - private LfoDescriptor vib_lfo; - private FilterDescriptor fltr; - - public Sf2Patch(string name) - : base(name) - { - } - - public override bool Start(VoiceParameters voiceparams) - { - int note = keyOverride > -1 ? keyOverride : voiceparams.Note; - int vel = velOverride > -1 ? velOverride : voiceparams.Velocity; - //setup generator - voiceparams.GeneratorParams[0].QuickSetup(gen); - //setup envelopes - voiceparams.Envelopes[0].QuickSetupSf2(voiceparams.SynthParams.Synth.SampleRate, note, keynumToModEnvHold, keynumToModEnvDecay, false, mod_env); - voiceparams.Envelopes[1].QuickSetupSf2(voiceparams.SynthParams.Synth.SampleRate, note, keynumToVolEnvHold, keynumToVolEnvDecay, true, vel_env); - //setup filter - //voiceparams.pData[0].int1 = iniFilterFc - (int)(2400 * CalculateModulator(SourceTypeEnum.Linear, TransformEnum.Linear, DirectionEnum.MaxToMin, PolarityEnum.Unipolar, voiceparams.velocity, 0, 127)); - //if (iniFilterFc >= 13500 && fltr.Resonance <= 1) - voiceparams.Filters[0].Disable(); - //else - // voiceparams.filters[0].QuickSetup(voiceparams.synthParams.synth.SampleRate, note, 1f, fltr); - //setup lfos - voiceparams.Lfos[0].QuickSetup(voiceparams.SynthParams.Synth.SampleRate, mod_lfo); - voiceparams.Lfos[1].QuickSetup(voiceparams.SynthParams.Synth.SampleRate, vib_lfo); - //calculate initial pitch - voiceparams.PitchOffset = (note - gen.RootKey) * gen.KeyTrack + gen.Tune; - voiceparams.PitchOffset += (int)(100.0 * (voiceparams.SynthParams.MasterCoarseTune + (voiceparams.SynthParams.MasterFineTune.Combined - 8192.0) / 8192.0)); - //calculate initial volume - voiceparams.VolOffset = initialAttn; - voiceparams.VolOffset -= 96.0f * (float)CalculateModulator(SourceTypeEnum.Concave, TransformEnum.Linear, DirectionEnum.MaxToMin, PolarityEnum.Unipolar, voiceparams.Velocity, 0, 127); - voiceparams.VolOffset -= 96.0f * (float)CalculateModulator(SourceTypeEnum.Concave, TransformEnum.Linear, DirectionEnum.MaxToMin, PolarityEnum.Unipolar, voiceparams.SynthParams.Volume.Coarse, 0, 127); - //check if we have finished before we have begun - return voiceparams.GeneratorParams[0].CurrentState != GeneratorState.Finished && voiceparams.Envelopes[1].CurrentStage != EnvelopeState.None; - } - - public override void Stop(VoiceParameters voiceparams) - { - gen.Release(voiceparams.GeneratorParams[0]); - if (gen.LoopMode != LoopMode.OneShot) - { - voiceparams.Envelopes[0].Release(SynthConstants.DenormLimit); - voiceparams.Envelopes[1].ReleaseSf2VolumeEnvelope(); - } - } - - public override void Process(VoiceParameters voiceparams, int startIndex, int endIndex, bool isMuted, bool isSilentProcess) - { - //--Base pitch calculation - var basePitchFrequency = SynthHelper.CentsToPitch(voiceparams.SynthParams.CurrentPitch) * gen.Frequency; - var pitchWithBend = basePitchFrequency * SynthHelper.CentsToPitch(voiceparams.PitchOffset); - var basePitch = pitchWithBend / voiceparams.SynthParams.Synth.SampleRate; - - float baseVolume = voiceparams.SynthParams.Synth.MasterVolume * voiceparams.SynthParams.CurrentVolume * SynthConstants.DefaultMixGain * voiceparams.SynthParams.MixVolume; - - if (isSilentProcess) - { - voiceparams.State = VoiceStateEnum.Stopped; - } - else - { - //--Main Loop - for (int x = startIndex; x < endIndex; x += SynthConstants.DefaultBlockSize * SynthConstants.AudioChannels) - { - voiceparams.Envelopes[0].Increment(SynthConstants.DefaultBlockSize); - voiceparams.Envelopes[1].Increment(SynthConstants.DefaultBlockSize); - voiceparams.Lfos[0].Increment(SynthConstants.DefaultBlockSize); - voiceparams.Lfos[1].Increment(SynthConstants.DefaultBlockSize); - - //--Calculate pitch and get next block of samples - gen.GetValues(voiceparams.GeneratorParams[0], voiceparams.BlockBuffer, basePitch * - SynthHelper.CentsToPitch((int)(voiceparams.Envelopes[0].Value * modEnvToPitch + - voiceparams.Lfos[0].Value * modLfoToPitch + voiceparams.Lfos[1].Value * vibLfoToPitch))); - //--Filter - if (voiceparams.Filters[0].Enabled) - { - double centsFc = voiceparams.PData[0].Int1 + voiceparams.Lfos[0].Value * modLfoToFilterFc + voiceparams.Envelopes[0].Value * modEnvToFilterFc; - if (centsFc > 13500) - centsFc = 13500; - voiceparams.Filters[0].CutOff = SynthHelper.KeyToFrequency(centsFc / 100.0, 69); - if (voiceparams.Filters[0].CoeffNeedsUpdating) - voiceparams.Filters[0].ApplyFilterInterp(voiceparams.BlockBuffer, voiceparams.SynthParams.Synth.SampleRate); - else - voiceparams.Filters[0].ApplyFilter(voiceparams.BlockBuffer); - } - //--Volume calculation - float volume = (float)SynthHelper.DBtoLinear(voiceparams.VolOffset + voiceparams.Envelopes[1].Value + voiceparams.Lfos[0].Value * modLfoToVolume) * baseVolume; - - // only mix if needed - if (!isMuted) - { - //--Mix block based on number of channels - voiceparams.MixMonoToStereoInterp(x, volume * pan.Left * voiceparams.SynthParams.CurrentPan.Left, volume * pan.Right * voiceparams.SynthParams.CurrentPan.Right); - } - - //--Check and end early if necessary - if ((voiceparams.Envelopes[1].CurrentStage > EnvelopeState.Hold && volume <= SynthConstants.NonAudible) || voiceparams.GeneratorParams[0].CurrentState == GeneratorState.Finished) - { - voiceparams.State = VoiceStateEnum.Stopped; - return; - } - } - } - - } - - - public void Load(Sf2Region region, AssetManager assets) - { - ExclusiveGroup = region.Generators[(int)GeneratorEnum.ExclusiveClass]; - ExclusiveGroupTarget = ExclusiveGroup; - - iniFilterFc = region.Generators[(int)GeneratorEnum.InitialFilterCutoffFrequency]; - filterQ = SynthHelper.DBtoLinear(region.Generators[(int)GeneratorEnum.InitialFilterQ] / 10.0); - initialAttn = -region.Generators[(int)GeneratorEnum.InitialAttenuation] / 10f; - keyOverride = region.Generators[(int)GeneratorEnum.KeyNumber]; - velOverride = region.Generators[(int)GeneratorEnum.Velocity]; - keynumToModEnvHold = region.Generators[(int)GeneratorEnum.KeyNumberToModulationEnvelopeHold]; - keynumToModEnvDecay = region.Generators[(int)GeneratorEnum.KeyNumberToModulationEnvelopeDecay]; - keynumToVolEnvHold = region.Generators[(int)GeneratorEnum.KeyNumberToVolumeEnvelopeHold]; - keynumToVolEnvDecay = region.Generators[(int)GeneratorEnum.KeyNumberToVolumeEnvelopeDecay]; - pan = new PanComponent(); - pan.SetValue(region.Generators[(int)GeneratorEnum.Pan] / 500f, PanFormulaEnum.Neg3dBCenter); - modLfoToPitch = region.Generators[(int)GeneratorEnum.ModulationLFOToPitch]; - vibLfoToPitch = region.Generators[(int)GeneratorEnum.VibratoLFOToPitch]; - modEnvToPitch = region.Generators[(int)GeneratorEnum.ModulationEnvelopeToPitch]; - modLfoToFilterFc = region.Generators[(int)GeneratorEnum.ModulationLFOToFilterCutoffFrequency]; - modEnvToFilterFc = region.Generators[(int)GeneratorEnum.ModulationEnvelopeToFilterCutoffFrequency]; - modLfoToVolume = region.Generators[(int)GeneratorEnum.ModulationLFOToVolume] / 10f; - - LoadGen(region, assets); - LoadEnvelopes(region); - LoadLfos(region); - LoadFilter(region); - } - - private void LoadGen(Sf2Region region, AssetManager assets) - { - SampleDataAsset sda = assets.SampleAssets[region.Generators[(int)GeneratorEnum.SampleID]]; - gen = new SampleGenerator(); - gen.EndPhase = sda.End + region.Generators[(int)GeneratorEnum.EndAddressOffset] + 32768 * region.Generators[(int)GeneratorEnum.EndAddressCoarseOffset]; - gen.Frequency = sda.SampleRate; - gen.KeyTrack = region.Generators[(int)GeneratorEnum.ScaleTuning]; - gen.LoopEndPhase = sda.LoopEnd + region.Generators[(int)GeneratorEnum.EndLoopAddressOffset] + 32768 * region.Generators[(int)GeneratorEnum.EndLoopAddressCoarseOffset]; - switch (region.Generators[(int)GeneratorEnum.SampleModes] & 0x3) - { - case 0x0: - case 0x2: - gen.LoopMode = LoopMode.NoLoop; - break; - case 0x1: - gen.LoopMode = LoopMode.Continuous; - break; - case 0x3: - gen.LoopMode = LoopMode.LoopUntilNoteOff; - break; - } - gen.LoopStartPhase = sda.LoopStart + region.Generators[(int)GeneratorEnum.StartLoopAddressOffset] + 32768 * region.Generators[(int)GeneratorEnum.StartLoopAddressCoarseOffset]; - gen.Offset = 0; - gen.Period = 1.0; - if (region.Generators[(int)GeneratorEnum.OverridingRootKey] > -1) - gen.RootKey = region.Generators[(int)GeneratorEnum.OverridingRootKey]; - else - gen.RootKey = sda.RootKey; - gen.StartPhase = sda.Start + region.Generators[(int)GeneratorEnum.StartAddressOffset] + 32768 * region.Generators[(int)GeneratorEnum.StartAddressCoarseOffset]; - gen.Tune = (short)(sda.Tune + region.Generators[(int)GeneratorEnum.FineTune] + 100 * region.Generators[(int)GeneratorEnum.CoarseTune]); - gen.VelocityTrack = 0; - ((SampleGenerator)gen).Samples = sda.SampleData; - } - - private void LoadEnvelopes(Sf2Region region) - { - // - //mod env - mod_env = new EnvelopeDescriptor(); - mod_env.AttackTime = (float)Math.Pow(2, region.Generators[(int)GeneratorEnum.AttackModulationEnvelope] / 1200.0); - mod_env.AttackGraph = 3; - mod_env.DecayTime = (float)Math.Pow(2, region.Generators[(int)GeneratorEnum.DecayModulationEnvelope] / 1200.0); - mod_env.DelayTime = (float)Math.Pow(2, region.Generators[(int)GeneratorEnum.DelayModulationEnvelope] / 1200.0); - mod_env.HoldTime = (float)Math.Pow(2, region.Generators[(int)GeneratorEnum.HoldModulationEnvelope] / 1200.0); - mod_env.PeakLevel = 1; - mod_env.ReleaseTime = (float)Math.Pow(2, region.Generators[(int)GeneratorEnum.ReleaseModulationEnvelope] / 1200.0); - mod_env.StartLevel = 0; - mod_env.SustainLevel = 1f - SynthHelper.ClampS(region.Generators[(int)GeneratorEnum.SustainModulationEnvelope], (short)0, (short)1000) / 1000f; - //checks - if (mod_env.AttackTime < 0.001f) - mod_env.AttackTime = 0.001f; - else if (mod_env.AttackTime > 100f) - mod_env.AttackTime = 100f; - if (mod_env.DecayTime < 0.001f) - mod_env.DecayTime = 0; - else if (mod_env.DecayTime > 100f) - mod_env.DecayTime = 100f; - if (mod_env.DelayTime < 0.001f) - mod_env.DelayTime = 0; - else if (mod_env.DelayTime > 20f) - mod_env.DelayTime = 20f; - if (mod_env.HoldTime < 0.001f) - mod_env.HoldTime = 0; - else if (mod_env.HoldTime > 20f) - mod_env.HoldTime = 20f; - if (mod_env.ReleaseTime < 0.001f) - mod_env.ReleaseTime = 0.001f; - else if (mod_env.ReleaseTime > 100f) - mod_env.ReleaseTime = 100f; - - // - // volume env - vel_env = new EnvelopeDescriptor(); - vel_env.AttackTime = (float)Math.Pow(2, region.Generators[(int)GeneratorEnum.AttackVolumeEnvelope] / 1200.0); - vel_env.AttackGraph = 3; - vel_env.DecayTime = (float)Math.Pow(2, region.Generators[(int)GeneratorEnum.DecayVolumeEnvelope] / 1200.0); - vel_env.DelayTime = (float)Math.Pow(2, region.Generators[(int)GeneratorEnum.DelayVolumeEnvelope] / 1200.0); - vel_env.HoldTime = (float)Math.Pow(2, region.Generators[(int)GeneratorEnum.HoldVolumeEnvelope] / 1200.0); - vel_env.PeakLevel = 0; - vel_env.ReleaseTime = (float)Math.Pow(2, region.Generators[(int)GeneratorEnum.ReleaseVolumeEnvelope] / 1200.0); - vel_env.StartLevel = -100; - vel_env.SustainLevel = SynthHelper.ClampS(region.Generators[(int)GeneratorEnum.SustainVolumeEnvelope], (short)0, (short)1000) / -10f; - // checks - if (vel_env.AttackTime < 0.001f) - vel_env.AttackTime = 0.001f; - else if (vel_env.AttackTime > 100f) - vel_env.AttackTime = 100f; - if (vel_env.DecayTime < 0.001f) - vel_env.DecayTime = 0; - else if (vel_env.DecayTime > 100f) - vel_env.DecayTime = 100f; - if (vel_env.DelayTime < 0.001f) - vel_env.DelayTime = 0; - else if (vel_env.DelayTime > 20f) - vel_env.DelayTime = 20f; - if (vel_env.HoldTime < 0.001f) - vel_env.HoldTime = 0; - else if (vel_env.HoldTime > 20f) - vel_env.HoldTime = 20f; - if (vel_env.ReleaseTime < 0.001f) - vel_env.ReleaseTime = 0.001f; - else if (vel_env.ReleaseTime > 100f) - vel_env.ReleaseTime = 100f; - - } - - private void LoadLfos(Sf2Region region) - { - mod_lfo = new LfoDescriptor(); - mod_lfo.DelayTime = (float)Math.Pow(2, region.Generators[(int)GeneratorEnum.DelayModulationLFO] / 1200.0); - mod_lfo.Frequency = (float)(Math.Pow(2, region.Generators[(int)GeneratorEnum.FrequencyModulationLFO] / 1200.0) * 8.176); - mod_lfo.Generator = DefaultGenerators.DefaultSine; - vib_lfo = new LfoDescriptor(); - vib_lfo.DelayTime = (float)Math.Pow(2, region.Generators[(int)GeneratorEnum.DelayVibratoLFO] / 1200.0); - vib_lfo.Frequency = (float)(Math.Pow(2, region.Generators[(int)GeneratorEnum.FrequencyVibratoLFO] / 1200.0) * 8.176); - vib_lfo.Generator = DefaultGenerators.DefaultSine; - } - - private void LoadFilter(Sf2Region region) - { - fltr = new FilterDescriptor(); - fltr.FilterMethod = FilterType.BiquadLowpass; - fltr.CutOff = (float)SynthHelper.KeyToFrequency(region.Generators[(int)GeneratorEnum.InitialFilterCutoffFrequency] / 100.0, 69); - fltr.Resonance = (float)SynthHelper.DBtoLinear(region.Generators[(int)GeneratorEnum.InitialFilterQ] / 10.0); - } - - private static double CalculateModulator(SourceTypeEnum s, TransformEnum t, DirectionEnum d, PolarityEnum p, int value, int min, int max) - { - double output = 0; - int i; - value = value - min; - max = max - min; - if (d == DirectionEnum.MaxToMin) - value = max - value; - switch (s) - { - case SourceTypeEnum.Linear: - output = value / max; - break; - case SourceTypeEnum.Concave: - i = 127 - value; - output = -(20.0 / 96.0) * Math.Log10((i * i) / (double)(max * max)); - break; - case SourceTypeEnum.Convex: - i = value; - output = 1 + (20.0 / 96.0) * Math.Log10((i * i) / (double)(max * max)); - break; - case SourceTypeEnum.Switch: - if (value <= (max / 2)) - output = 0; - else - output = 1; - break; - } - if (p == PolarityEnum.Bipolar) - output = (output * 2) - 1; - if (t == TransformEnum.AbsoluteValue) - output = Math.Abs(output); - return output; - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Bank/PatchAsset.cs b/Source/AlphaTab/Audio/Synth/Bank/PatchAsset.cs deleted file mode 100644 index 1dc52c93f..000000000 --- a/Source/AlphaTab/Audio/Synth/Bank/PatchAsset.cs +++ /dev/null @@ -1,32 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -namespace AlphaTab.Audio.Synth.Bank -{ - class PatchAsset - { - public string Name { get; } - public Patch.Patch Patch { get; } - - public PatchAsset(string name, Patch.Patch patch) - { - Name = name; - Patch = patch; - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Bank/PatchBank.cs b/Source/AlphaTab/Audio/Synth/Bank/PatchBank.cs deleted file mode 100644 index d70b3d5ae..000000000 --- a/Source/AlphaTab/Audio/Synth/Bank/PatchBank.cs +++ /dev/null @@ -1,329 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using AlphaTab.Audio.Synth.Bank.Patch; -using AlphaTab.Audio.Synth.Sf2; -using AlphaTab.Collections; -using AlphaTab.IO; -using AlphaTab.Platform; -using AlphaTab.Util; - -namespace AlphaTab.Audio.Synth.Bank -{ - class PatchBank - { - public const int DrumBank = 128; - public const int BankSize = 128; - - private FastDictionary _bank; - private AssetManager _assets; - - public string Name { get; set; } - public string Comments { get; set; } - - public PatchBank() - { - Reset(); - } - - public void Reset() - { - _bank = new FastDictionary(); - _assets = new AssetManager(); - Name = ""; - Comments = ""; - } - - public int[] LoadedBanks - { - get - { - var banks = new FastList(); - foreach (var bank in _bank) - { - banks.Add(bank); - } - banks.Sort((a, b) => a - b); - return banks.ToArray(); - } - } - - public Patch.Patch[] GetBank(int bankNumber) - { - return _bank.ContainsKey(bankNumber) ? _bank[bankNumber] : null; - } - - public Patch.Patch GetPatchByNumber(int bankNumber, int patchNumber) - { - return _bank.ContainsKey(bankNumber) ? _bank[bankNumber][patchNumber] : null; - } - - public Patch.Patch GetPatchByName(int bankNumber, string name) - { - if (_bank.ContainsKey(bankNumber)) - { - var patches = _bank[bankNumber]; - foreach (var patch in patches) - { - if (patch != null && patch.Name == name) - { - return patch; - } - } - } - return null; - } - - public bool IsBankLoaded(int bankNumber) - { - return _bank.ContainsKey(bankNumber); - } - - public void LoadSf2(IReadable input) - { - Reset(); - - Logger.Debug("PatchBank", "Reading SF2"); - var sf = new SoundFont(); - sf.Load(input); - - Logger.Debug("PatchBank", "Building patchbank"); - Name = sf.Info.BankName; - Comments = sf.Info.Comments; - - //load samples - foreach (var sampleHeader in sf.Presets.SampleHeaders) - { - _assets.SampleAssets.Add(new SampleDataAsset(sampleHeader, sf.SampleData)); - } - - //create instrument regions first - var sfinsts = ReadSf2Instruments(sf.Presets.Instruments); - //load each patch - foreach (var p in sf.Presets.PresetHeaders) - { - Sf2.Generator[] globalGens = null; - int i; - if (p.Zones[0].Generators.Length == 0 || - p.Zones[0].Generators[p.Zones[0].Generators.Length - 1].GeneratorType != GeneratorEnum.Instrument) - { - globalGens = p.Zones[0].Generators; - i = 1; - } - else - { - i = 0; - } - - var regionList = new FastList(); - while (i < p.Zones.Length) - { - byte presetLoKey = 0; - byte presetHiKey = 127; - byte presetLoVel = 0; - byte presetHiVel = 127; - - if (p.Zones[i].Generators[0].GeneratorType == GeneratorEnum.KeyRange) - { - presetLoKey = Platform.Platform.ToUInt8(p.Zones[i].Generators[0].AmountInt16 & 0xFF); - presetHiKey = Platform.Platform.ToUInt8((p.Zones[i].Generators[0].AmountInt16 >> 8) & 0xFF); - - if (p.Zones[i].Generators.Length > 1 && p.Zones[i].Generators[1].GeneratorType == GeneratorEnum.VelocityRange) - { - presetLoVel = Platform.Platform.ToUInt8(p.Zones[i].Generators[1].AmountInt16 & 0xFF); - presetHiVel = Platform.Platform.ToUInt8((p.Zones[i].Generators[1].AmountInt16 >> 8) & 0xFF); - } - } - else if (p.Zones[i].Generators[0].GeneratorType == GeneratorEnum.VelocityRange) - { - presetLoVel = Platform.Platform.ToUInt8(p.Zones[i].Generators[0].AmountInt16 & 0xFF); - presetHiVel = Platform.Platform.ToUInt8((p.Zones[i].Generators[0].AmountInt16 >> 8) & 0xFF); - } - if (p.Zones[i].Generators[p.Zones[i].Generators.Length - 1].GeneratorType == GeneratorEnum.Instrument) - { - var insts = sfinsts[p.Zones[i].Generators[p.Zones[i].Generators.Length - 1].AmountInt16]; - foreach (var inst in insts) - { - byte instLoKey; - byte instHiKey; - byte instLoVel; - byte instHiVel; - - instLoKey = Platform.Platform.ToUInt8(inst.Generators[(int)GeneratorEnum.KeyRange] & 0xFF); - instHiKey = Platform.Platform.ToUInt8((inst.Generators[(int)GeneratorEnum.KeyRange] >> 8) & 0xFF); - instLoVel = Platform.Platform.ToUInt8(inst.Generators[(int)GeneratorEnum.VelocityRange] & 0xFF); - instHiVel = Platform.Platform.ToUInt8((inst.Generators[(int)GeneratorEnum.VelocityRange] >> 8) & 0xFF); - - if ((instLoKey <= presetHiKey && presetLoKey <= instHiKey) && (instLoVel <= presetHiVel && presetLoVel <= instHiVel)) - { - var r = new Sf2Region(); - Platform.Platform.ArrayCopy(inst.Generators, 0, r.Generators, 0, r.Generators.Length); - ReadSf2Region(r, globalGens, p.Zones[i].Generators, true); - regionList.Add(r); - } - } - } - i++; - } - var mp = new MultiPatch(p.Name); - mp.LoadSf2(regionList.ToArray(), _assets); - _assets.PatchAssets.Add(new PatchAsset(mp.Name, mp)); - AssignPatchToBank(mp, p.BankNumber, p.PatchNumber, p.PatchNumber); - } - } - - private Sf2Region[][] ReadSf2Instruments(Instrument[] instruments) - { - var regions = new Sf2Region[instruments.Length][]; - for (int x = 0; x < instruments.Length; x++) - { - Sf2.Generator[] globalGens = null; - int i; - if (instruments[x].Zones[0].Generators.Length == 0 || - instruments[x].Zones[0].Generators[instruments[x].Zones[0].Generators.Length - 1].GeneratorType != GeneratorEnum.SampleID) - { - globalGens = instruments[x].Zones[0].Generators; - i = 1; - } - else - i = 0; - - regions[x] = new Sf2Region[instruments[x].Zones.Length - i]; - for (int j = 0; j < regions[x].Length; j++) - { - var r = new Sf2Region(); - r.ApplyDefaultValues(); - ReadSf2Region(r, globalGens, instruments[x].Zones[j + i].Generators, false); - regions[x][j] = r; - } - } - return regions; - } - - private void ReadSf2Region(Sf2Region region, Sf2.Generator[] globals, Sf2.Generator[] gens, bool isRelative) - { - if (!isRelative) - { - if (globals != null) - { - for (int x = 0; x < globals.Length; x++) - { - region.Generators[(int)globals[x].GeneratorType] = globals[x].AmountInt16; - } - } - for (int x = 0; x < gens.Length; x++) - { - region.Generators[(int)gens[x].GeneratorType] = gens[x].AmountInt16; - } - } - else - { - var genList = new FastList(); - foreach (var generator in gens) - { - genList.Add(generator); - } - if (globals != null) - { - for (int x = 0; x < globals.Length; x++) - { - var found = false; - for (int i = 0; i < genList.Count; i++) - { - if (genList[i].GeneratorType == globals[x].GeneratorType) - { - found = true; - break; - } - } - if (!found) - { - genList.Add(globals[x]); - } - } - } - for (int x = 0; x < genList.Count; x++) - { - var value = (int)genList[x].GeneratorType; - if (value < 5 || value == 12 || value == 45 || value == 46 || value == 47 || value == 50 || - value == 54 || value == 57 || value == 58) - { - continue; - } - else if (value == 43 || value == 44) - { - byte lo_a; - byte hi_a; - byte lo_b; - byte hi_b; - lo_a = Platform.Platform.ToUInt8(region.Generators[value] & 0xFF); - hi_a = Platform.Platform.ToUInt8((region.Generators[value] >> 8) & 0xFF); - lo_b = Platform.Platform.ToUInt8(genList[x].AmountInt16 & 0xFF); - hi_b = Platform.Platform.ToUInt8((genList[x].AmountInt16 >> 8) & 0xFF); - - lo_a = (byte)Math.Max(lo_a, lo_b); - hi_a = Math.Min(hi_a, hi_b); - - if (lo_a > hi_a) - { - throw new Exception("Invalid sf2 region. The range generators do not intersect."); - } - region.Generators[value] = Platform.Platform.ToInt16((lo_a | (hi_a << 8))); - } - else - { - region.Generators[value] = Platform.Platform.ToInt16(region.Generators[value] + genList[x].AmountInt16); - } - } - } - } - - private void AssignPatchToBank(Patch.Patch patch, int bankNumber, int startRange, int endRange) - { - if (bankNumber < 0) - return; - - if (startRange > endRange) - { - var range = startRange; - startRange = endRange; - endRange = range; - } - if (startRange < 0 || startRange >= BankSize) - throw new Exception("startRange out of range"); - if (endRange < 0 || endRange >= BankSize) - throw new Exception("endRange out of range"); - - Patch.Patch[] patches; - if (_bank.ContainsKey(bankNumber)) - { - patches = _bank[bankNumber]; - } - else - { - patches = new Patch.Patch[BankSize]; - _bank[bankNumber] = patches; - } - for (int x = startRange; x <= endRange; x++) - { - patches[x] = patch; - } - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Bank/PcmData.cs b/Source/AlphaTab/Audio/Synth/Bank/PcmData.cs deleted file mode 100644 index f105cecc7..000000000 --- a/Source/AlphaTab/Audio/Synth/Bank/PcmData.cs +++ /dev/null @@ -1,100 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.Platform; -using AlphaTab.Audio.Synth.Util; - -namespace AlphaTab.Audio.Synth.Bank -{ - abstract class PcmData - { - protected byte[] Data; - - public int Length { get; protected set; } - public int BytesPerSample { get; protected set; } - public int BitsPerSample { get { return BytesPerSample * 8; } } - - protected PcmData(int bits, byte[] pcmData, bool isDataInLittleEndianFormat) - { - BytesPerSample = (byte)(bits / 8); - //if (pcmData.Length % BytesPerSample != 0) - // throw new Exception("Invalid PCM format. The PCM data was an invalid size."); - Data = pcmData; - Length = Data.Length / BytesPerSample; - if (!isDataInLittleEndianFormat) - { - SynthHelper.SwapEndianess(Data, bits); - } - } - - public abstract float this[int index] { get; } - - public static PcmData Create(int bits, byte[] pcmData, bool isDataInLittleEndianFormat) - { - switch (bits) - { - case 8: - return new PcmData8Bit(bits, pcmData, isDataInLittleEndianFormat); - case 16: - return new PcmData16Bit(bits, pcmData, isDataInLittleEndianFormat); - case 24: - return new PcmData24Bit(bits, pcmData, isDataInLittleEndianFormat); - case 32: - return new PcmData32Bit(bits, pcmData, isDataInLittleEndianFormat); - default: - throw new Exception("Invalid PCM format. " + bits + "bit pcm data is not supported."); - } - } - } - - class PcmData8Bit : PcmData - { - public PcmData8Bit(int bits, byte[] pcmData, bool isDataInLittleEndianFormat) : base(bits, pcmData, isDataInLittleEndianFormat) { } - public override float this[int index] - { - get { return ((Data[index] / 255f) * 2f) - 1f; } - } - } - - class PcmData16Bit : PcmData - { - public PcmData16Bit(int bits, byte[] pcmData, bool isDataInLittleEndianFormat) : base(bits, pcmData, isDataInLittleEndianFormat) { } - public override float this[int index] - { - get { index *= 2; return (((Data[index] | (Data[index + 1] << 8)) << 16) >> 16) / 32768f; } - } - } - - class PcmData24Bit : PcmData - { - public PcmData24Bit(int bits, byte[] pcmData, bool isDataInLittleEndianFormat) : base(bits, pcmData, isDataInLittleEndianFormat) { } - public override float this[int index] - { - get { index *= 3; return (((Data[index] | (Data[index + 1] << 8) | (Data[index + 2] << 16)) << 12) >> 12) / 8388608f; } - } - } - - class PcmData32Bit : PcmData - { - public PcmData32Bit(int bits, byte[] pcmData, bool isDataInLittleEndianFormat) : base(bits, pcmData, isDataInLittleEndianFormat) { } - public override float this[int index] - { - get { index *= 4; return (Data[index] | (Data[index + 1] << 8) | (Data[index + 2] << 16) | (Data[index + 3] << 24)) / 2147483648f; } - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Bank/SampleDataAsset.cs b/Source/AlphaTab/Audio/Synth/Bank/SampleDataAsset.cs deleted file mode 100644 index d5aa779c5..000000000 --- a/Source/AlphaTab/Audio/Synth/Bank/SampleDataAsset.cs +++ /dev/null @@ -1,59 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using AlphaTab.Audio.Synth.Sf2; - -namespace AlphaTab.Audio.Synth.Bank -{ - class SampleDataAsset - { - public string Name { get; set; } - public int Channels { get; set; } - public int SampleRate { get; set; } - public short RootKey { get; set; } - public short Tune { get; set; } - public float Start { get; set; } - public float End { get; set; } - public float LoopStart { get; set; } - public float LoopEnd { get; set; } - public PcmData SampleData { get; set; } - - public SampleDataAsset(SampleHeader sample, SoundFontSampleData sampleData) - { - Channels = 1; - - Name = sample.Name; - SampleRate = sample.SampleRate; - RootKey = sample.RootKey; - Tune = sample.Tune; - Start = sample.Start; - End = sample.End; - LoopStart = sample.StartLoop; - LoopEnd = sample.EndLoop; - if ((sample.SoundFontSampleLink & SFSampleLink.OggVobis) != 0) - { - throw new Exception("Ogg Vobis encoded soundfonts not supported"); - } - else - { - SampleData = PcmData.Create(sampleData.BitsPerSample, sampleData.SampleData, true); - } - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Ds/CircularSampleBuffer.cs b/Source/AlphaTab/Audio/Synth/Ds/CircularSampleBuffer.cs deleted file mode 100644 index bba19715d..000000000 --- a/Source/AlphaTab/Audio/Synth/Ds/CircularSampleBuffer.cs +++ /dev/null @@ -1,113 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; - -namespace AlphaTab.Audio.Synth.Ds -{ - /// - /// Represents a fixed size circular sample buffer that can be written to and read from. - /// - public class CircularSampleBuffer - { - private SampleArray _buffer; - private int _writePosition; - private int _readPosition; - - /// - /// Initializes a new instance of the class. - /// - /// The size. - public CircularSampleBuffer(int size) - { - _buffer = new SampleArray(size); - _writePosition = 0; - _readPosition = 0; - Count = 0; - } - - /// - /// Gets the number of samples written to the buffer. - /// - public int Count { get; private set; } - - /// - /// Clears all samples written to this buffer. - /// - public void Clear() - { - _readPosition = 0; - _writePosition = 0; - Count = 0; - _buffer = new SampleArray(_buffer.Length); - } - - /// - /// Writes the given samples to this buffer. - /// - /// The sample array to read from. - /// - /// - /// - public int Write(SampleArray data, int offset, int count) - { - var samplesWritten = 0; - if (count > _buffer.Length - Count) - { - count = _buffer.Length - Count; - } - - var writeToEnd = Math.Min(_buffer.Length - _writePosition, count); - SampleArray.Blit(data, offset, _buffer, _writePosition, writeToEnd); - _writePosition += writeToEnd; - _writePosition %= _buffer.Length; - samplesWritten += writeToEnd; - if (samplesWritten < count) - { - SampleArray.Blit(data, offset + samplesWritten, _buffer, _writePosition, count - samplesWritten); - _writePosition += (count - samplesWritten); - samplesWritten = count; - } - Count += samplesWritten; - return samplesWritten; - } - - public int Read(SampleArray data, int offset, int count) - { - if (count > Count) - { - count = Count; - } - var samplesRead = 0; - var readToEnd = Math.Min(_buffer.Length - _readPosition, count); - SampleArray.Blit(_buffer, _readPosition, data, offset, readToEnd); - samplesRead += readToEnd; - _readPosition += readToEnd; - _readPosition %= _buffer.Length; - - if (samplesRead < count) - { - SampleArray.Blit(_buffer, _readPosition, data, offset + samplesRead, count - samplesRead); - _readPosition += (count - samplesRead); - samplesRead = count; - } - - Count -= samplesRead; - return samplesRead; - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Audio/Synth/Ds/LinkedList.cs b/Source/AlphaTab/Audio/Synth/Ds/LinkedList.cs deleted file mode 100644 index b1e86de89..000000000 --- a/Source/AlphaTab/Audio/Synth/Ds/LinkedList.cs +++ /dev/null @@ -1,148 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -namespace AlphaTab.Audio.Synth.Ds -{ - class LinkedList where T : class - { - public LinkedListNode First { get; set; } - - public int Length { get; private set; } - - public LinkedList() - { - Length = 0; - } - - public void AddFirst(T value) - { - var node = new LinkedListNode(); - node.Value = value; - if (First == null) - { - InsertNodeToEmptyList(node); - } - else - { - InsertNodeBefore(First, node); - First = node; - } - } - - public void AddLast(T value) - { - var node = new LinkedListNode(); - node.Value = value; - if (First == null) - { - InsertNodeToEmptyList(node); - } - else - { - InsertNodeBefore(First, node); - } - } - - public T RemoveFirst() - { - if (First == null) return null; - var v = First.Value; - Remove(First); - return v; - } - - public T RemoveLast() - { - if (First == null) return null; - var v = First._prev != null ? First._prev.Value : null; - Remove(First._prev); - return v; - } - - public void Remove(LinkedListNode n) - { - if (n._next == n) - { - First = null; - } - else - { - n._next._prev = n._prev; - n._prev._next = n._next; - if (First == n) - { - First = n._next; - } - } - n.Invalidate(); - Length--; - } - - private void InsertNodeBefore(LinkedListNode node, LinkedListNode newNode) - { - newNode._next = node; - newNode._prev = node._prev; - node._prev._next = newNode; - node._prev = newNode; - newNode._list = this; - Length++; - } - - private void InsertNodeToEmptyList(LinkedListNode node) - { - node._next = node; - node._prev = node; - node._list = this; - First = node; - Length++; - } - } - - class LinkedListNode where T : class - { - internal LinkedList _list; - internal LinkedListNode _next; - internal LinkedListNode _prev; - - public T Value { get; set; } - - public LinkedListNode Next - { - get - { - return _next == null || _list.First == _next ? null : _next; - } - } - - public LinkedListNode Prev - { - get - { - return _prev == null || this == _list.First ? null : _prev; - } - } - - public void Invalidate() - { - _list = null; - _next = null; - _prev = null; - } - } - -} diff --git a/Source/AlphaTab/Audio/Synth/IAlphaSynth.cs b/Source/AlphaTab/Audio/Synth/IAlphaSynth.cs deleted file mode 100644 index ca9b57832..000000000 --- a/Source/AlphaTab/Audio/Synth/IAlphaSynth.cs +++ /dev/null @@ -1,161 +0,0 @@ -using AlphaTab.Audio.Synth.Synthesis; - -namespace AlphaTab.Audio.Synth -{ - /// - /// The public API interface for interacting with the synthesizer. - /// - public interface IAlphaSynth - { - /// - /// Gets or sets whether the synthesizer is ready for interaction. (output and worker are initialized) - /// - bool IsReady - { - get; - } - - /// - /// Gets or sets whether the synthesizer is ready for playback. (output, worker are initialized, soundfont and midi are loaded) - /// p - bool IsReadyForPlayback - { - get; - } - - /// - /// Gets the current player state. - /// - PlayerState State - { - get; - } - - /// - /// Gets or sets the loging level. - /// - AlphaTab.Util.LogLevel LogLevel - { - get; set; - } - - /// - /// Gets or sets the current master volume as percentage. (range: 0.0-3.0, default 1.0) - /// - float MasterVolume - { - get; - set; - } - - /// - /// Gets or sets the metronome volume. (range: 0.0-3.0, default 0.0) - /// - float MetronomeVolume - { - get; - set; - } - - /// - /// Gets or sets the current playback speed as percentage. (range: 0.125-8.0, default: 1.0) - /// - double PlaybackSpeed - { - get; set; - } - - /// - /// Gets or sets the position within the song in midi ticks. - /// - int TickPosition - { - get; set; - } - - /// - /// Gets or sets the position within the song in milliseconds. - /// - double TimePosition - { - get; set; - } - - /// - /// Gets or sets the range of the song that should be played. Set this to null - /// to play the whole song. - /// - PlaybackRange PlaybackRange - { - get; set; - } - - /// - /// Gets or sets whether the playback should automatically restart after it finished. - /// - bool IsLooping - { - get; set; - } - - /// - /// Starts the playback if possible - /// - void Play(); - - /// - /// Pauses the playback if was running - /// - void Pause(); - - /// - /// Starts the playback if possible, pauses the playback if was running - /// - void PlayPause(); - - /// - /// Stopps the playback - /// - void Stop(); - - /// - ///Loads a soundfont from the given data - /// - /// a byte array to load the data from - void LoadSoundFont(byte[] data); - - /// - /// Gets the mute state of a channel. - /// - /// The channel number - /// true if the channel should be muted, otherwise false. - void SetChannelMute(int channel, bool mute); - - /// - /// Resets the mute/solo state of all channels - /// - void ResetChannelStates(); - - /// - /// Gets the solo state of a channel. - /// - /// The channel number - /// true if the channel should be played solo, otherwise false. - void SetChannelSolo(int channel, bool solo); - - - /// - /// Gets or sets the current and initial volume of the given channel. - /// - /// The channel number. - /// The volume of of the channel (0.0-3.0) - void SetChannelVolume(int channel, double volume); - - /// - /// Gets or sets the current and initial program of the given channel. - /// - /// The channel number. - /// The midi program. - void SetChannelProgram(int channel, byte program); - } -} diff --git a/Source/AlphaTab/Audio/Synth/ISynthOutput.cs b/Source/AlphaTab/Audio/Synth/ISynthOutput.cs deleted file mode 100644 index ac2e6f90e..000000000 --- a/Source/AlphaTab/Audio/Synth/ISynthOutput.cs +++ /dev/null @@ -1,86 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.Audio.Synth.Ds; - -namespace AlphaTab.Audio.Synth -{ - /// - /// This is the base interface for output devices which can - /// request and playback audio samples. - /// - public interface ISynthOutput - { - /// - /// Gets the sample rate required by the output. - /// - int SampleRate { get; } - - /// - /// Called when the output should be opened. - /// - void Open(); - - /// - /// Called when the sequencer finished the playback. - /// This tells the output not to request any samples anymore after the existing buffers are finished. - /// - void SequencerFinished(); - - /// - /// Called when the output should start the playback. - /// - void Play(); - - /// - /// Called when the output should stop the playback. - /// - void Pause(); - - /// - /// Called when samples have been synthesized and should be added to the playback buffer. - /// - /// - void AddSamples(SampleArray samples); - - /// - /// Called when the samples in the output buffer should be reset. This is neeed for instance when seeking to another position. - /// - void ResetSamples(); - - /// - /// Fired when the output has been successfully opened and is ready to play samples. - /// - event Action Ready; - - /// - /// Fired when a certain number of samples have been played. - /// - event Action SamplesPlayed; - - /// - /// Fired when the output needs more samples to be played. - /// - event Action SampleRequest; - - /// - /// Fired when the last samples after calling SequencerFinished have been played. - /// - event Action Finished; - } -} diff --git a/Source/AlphaTab/Audio/Synth/Midi/Event/MetaDataEvent.cs b/Source/AlphaTab/Audio/Synth/Midi/Event/MetaDataEvent.cs deleted file mode 100644 index 03fad5b3e..000000000 --- a/Source/AlphaTab/Audio/Synth/Midi/Event/MetaDataEvent.cs +++ /dev/null @@ -1,43 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.IO; - -namespace AlphaTab.Audio.Synth.Midi.Event -{ - class MetaDataEvent : MetaEvent - { - public byte[] Data { get; private set; } - - public MetaDataEvent(int delta, byte status, byte metaId, byte[] data) - : base(delta, status, metaId, 0) - { - Data = data; - } - - public override void WriteTo(IWriteable s) - { - s.WriteByte(0xFF); - s.WriteByte((byte)MetaStatus); - - var l = Data.Length; - MidiFile.WriteVariableInt(s, l); - s.Write(Data, 0, Data.Length); - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Midi/Event/MetaEvent.cs b/Source/AlphaTab/Audio/Synth/Midi/Event/MetaEvent.cs deleted file mode 100644 index 3e157e624..000000000 --- a/Source/AlphaTab/Audio/Synth/Midi/Event/MetaEvent.cs +++ /dev/null @@ -1,64 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Audio.Synth.Midi.Event -{ - enum MetaEventTypeEnum - { - SequenceNumber = 0x00, - TextEvent = 0x01, - CopyrightNotice = 0x02, - SequenceOrTrackName = 0x03, - InstrumentName = 0x04, - LyricText = 0x05, - MarkerText = 0x06, - CuePoint = 0x07, - PatchName = 0x08, - PortName = 0x09, - MidiChannel = 0x20, - MidiPort = 0x21, - EndOfTrack = 0x2F, - Tempo = 0x51, - SmpteOffset = 0x54, - TimeSignature = 0x58, - KeySignature = 0x59, - SequencerSpecific = 0x7F - } - - abstract class MetaEvent : MidiEvent - { - public override int Channel - { - get { return -1; } - } - - public override MidiEventType Command - { - get { return (MidiEventType) (Message & 0x00000FF); } - } - - public int MetaStatus - { - get { return Data1; } - } - - protected MetaEvent(int delta, byte status, byte data1, byte data2) - : base(delta, status, data1, data2) - { - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Midi/Event/MetaNumberEvent.cs b/Source/AlphaTab/Audio/Synth/Midi/Event/MetaNumberEvent.cs deleted file mode 100644 index 316826b1d..000000000 --- a/Source/AlphaTab/Audio/Synth/Midi/Event/MetaNumberEvent.cs +++ /dev/null @@ -1,47 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.IO; - -namespace AlphaTab.Audio.Synth.Midi.Event -{ - class MetaNumberEvent : MetaEvent - { - public int Value { get; private set; } - - public MetaNumberEvent(int delta, byte status, byte metaId, int number) - : base(delta, status, metaId, 0) - { - Value = number; - } - - - public override void WriteTo(IWriteable s) - { - s.WriteByte(0xFF); - s.WriteByte((byte)MetaStatus); - - MidiFile.WriteVariableInt(s, 3); - - var b = new[]{ - (byte)((Value >> 16) & 0xFF), (byte)((Value >> 8) & 0xFF), (byte)((Value >> 0) & 0xFF) - }; - s.Write(b, 0, b.Length); - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Midi/Event/MidiEvent.cs b/Source/AlphaTab/Audio/Synth/Midi/Event/MidiEvent.cs deleted file mode 100644 index 0dfd2a176..000000000 --- a/Source/AlphaTab/Audio/Synth/Midi/Event/MidiEvent.cs +++ /dev/null @@ -1,271 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.IO; - -namespace AlphaTab.Audio.Synth.Midi.Event -{ - /// - /// Lists all midi events. - /// - public enum MidiEventType - { - /// - /// A note is released. - /// - NoteOff = 0x80, - /// - /// A note is started. - /// - NoteOn = 0x90, - /// - /// The pressure that was used to play the note. - /// - NoteAftertouch = 0xA0, - /// - /// Change of a midi controller - /// - Controller = 0xB0, - /// - /// Change of a midi program - /// - ProgramChange = 0xC0, - /// - /// The pressure that should be applied to the whole channel. - /// - ChannelAftertouch = 0xD0, - /// - /// A change of the audio pitch. - /// - PitchBend = 0xE0, - /// - /// A meta event. See for details. - /// - Meta = 0xFF - } - - /// - /// Lists all midi controllers. - /// - public enum ControllerType - { - /// - /// Bank Select. MSB - /// - BankSelectCoarse = 0x00, - /// - /// Modulation wheel or lever MSB - /// - ModulationCoarse = 0x01, - //BreathControllerCoarse = 0x02, - //FootControllerCoarse = 0x04, - //PortamentoTimeCoarse = 0x05, - /// - /// Data entry MSB - /// - DataEntryCoarse = 0x06, - /// - /// Channel Volume MSB - /// - VolumeCoarse = 0x07, - //BalanceCoarse = 0x08, - /// - /// Pan MSB - /// - PanCoarse = 0x0A, - /// - /// Expression Controller MSB - /// - ExpressionControllerCoarse = 0x0B, - //EffectControl1Coarse = 0x0C, - //EffectControl2Coarse = 0x0D, - //GeneralPurposeSlider1 = 0x10, - //GeneralPurposeSlider2 = 0x11, - //GeneralPurposeSlider3 = 0x12, - //GeneralPurposeSlider4 = 0x13, - //BankSelectFine = 0x20, - /// - /// Modulation wheel or level LSB - /// - ModulationFine = 0x21, - //BreathControllerFine = 0x22, - //FootControllerFine = 0x24, - //PortamentoTimeFine = 0x25, - /// - /// Data Entry LSB - /// - DataEntryFine = 0x26, - /// - /// Channel Volume LSB - /// - VolumeFine = 0x27, - //BalanceFine = 0x28, - /// - /// Pan LSB - /// - PanFine = 0x2A, - /// - /// Expression controller LSB - /// - ExpressionControllerFine = 0x2B, - //EffectControl1Fine = 0x2C, - //EffectControl2Fine = 0x2D, - /// - /// Damper pedal (sustain) - /// - HoldPedal = 0x40, - //Portamento = 0x41, - //SostenutoPedal = 0x42, - //SoftPedal = 0x43, - /// - /// Legato Footswitch - /// - LegatoPedal = 0x44, - //Hold2Pedal = 0x45, - //SoundVariation = 0x46, - //SoundTimbre = 0x47, - //SoundReleaseTime = 0x48, - //SoundAttackTime = 0x49, - //SoundBrightness = 0x4A, - //SoundControl6 = 0x4B, - //SoundControl7 = 0x4C, - //SoundControl8 = 0x4D, - //SoundControl9 = 0x4E, - //SoundControl10 = 0x4F, - //GeneralPurposeButton1 = 0x50, - //GeneralPurposeButton2 = 0x51, - //GeneralPurposeButton3 = 0x52, - //GeneralPurposeButton4 = 0x53, - //EffectsLevel = 0x5B, - //TremuloLevel = 0x5C, - //ChorusLevel = 0x5D, - //CelesteLevel = 0x5E, - //PhaseLevel = 0x5F, - //DataButtonIncrement = 0x60, - //DataButtonDecrement = 0x61, - /// - /// Non-Registered Parameter Number LSB - /// - NonRegisteredParameterFine = 0x62, - /// - /// Non-Registered Parameter Number MSB - /// - NonRegisteredParameterCourse = 0x63, - /// - /// Registered Parameter Number LSB - /// - RegisteredParameterFine = 0x64, - /// - /// Registered Parameter Number MSB - /// - RegisteredParameterCourse = 0x65, - //AllSoundOff = 0x78, - /// - /// Reset all controllers - /// - ResetControllers = 0x79, - //LocalKeyboard = 0x7A, - /// - /// All notes of. - /// - AllNotesOff = 0x7B, - //OmniModeOff = 0x7C, - //OmniModeOn = 0x7D, - //MonoMode = 0x7E, - //PolyMode = 0x7F - } - - /// - /// Represents a midi event. - /// - public class MidiEvent - { - /// - /// Gets or sets the raw midi message. - /// - public int Message { get; set; } - - /// - /// Gets or sets the absolute tick of this midi event. - /// - public int Tick { get; set; } - - /// - /// Gets or sets the channel of this midi event. - /// - public virtual int Channel => Message & 0x000000F; - - /// - /// Gets or sets the command of this midi event. - /// - public virtual MidiEventType Command => (MidiEventType)(Message & 0x00000F0); - - /// - /// Gets or sets the first data component of this midi event. - /// - public int Data1 - { - get => (Message & 0x000FF00) >> 8; - set - { - Message &= ~0x000FF00; - Message |= value << 8; - } - } - - /// - /// Gets or sets the second data component of this midi event. - /// - public int Data2 - { - get => (Message & 0x0FF0000) >> 16; - set - { - Message &= ~0x0FF0000; - Message |= value << 16; - } - } - - /// - /// Initializes a new instance of the class. - /// - /// The absolute midi ticks of this event.. - /// The status information of this event. - /// The first data component of this midi event. - /// The second data component of this midi event. - public MidiEvent(int tick, int status, byte data1, byte data2) - { - Tick = tick; - Message = status | (data1 << 8) | (data2 << 16); - } - - /// - /// Writes the midi event as binary into the given stream. - /// - /// The stream to write to. - public virtual void WriteTo(IWriteable s) - { - var b = new[] - { - (byte) ((Message >> 24) & 0xFF), (byte) ((Message >> 16) & 0xFF), - (byte) ((Message >> 8) & 0xFF), (byte) ((Message >> 0) & 0xFF) - }; - s.Write(b, 0, b.Length); - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Midi/Event/SystemCommonEvent.cs b/Source/AlphaTab/Audio/Synth/Midi/Event/SystemCommonEvent.cs deleted file mode 100644 index c91e5ce94..000000000 --- a/Source/AlphaTab/Audio/Synth/Midi/Event/SystemCommonEvent.cs +++ /dev/null @@ -1,47 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Audio.Synth.Midi.Event -{ - enum SystemCommonTypeEnum - { - SystemExclusive = 0xF0, - MtcQuarterFrame = 0xF1, - SongPosition = 0xF2, - SongSelect = 0xF3, - TuneRequest = 0xF6, - SystemExclusive2 = 0xF7 - } - - abstract class SystemCommonEvent : MidiEvent - { - public override int Channel - { - get { return -1; } - } - - public override MidiEventType Command - { - get { return (MidiEventType) (Message & 0x00000FF); } - } - - protected SystemCommonEvent(int delta, byte status, byte data1, byte data2) - : base(delta, status, data1, data2) - { - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Midi/Event/SystemExclusiveEvent.cs b/Source/AlphaTab/Audio/Synth/Midi/Event/SystemExclusiveEvent.cs deleted file mode 100644 index e5b11e4eb..000000000 --- a/Source/AlphaTab/Audio/Synth/Midi/Event/SystemExclusiveEvent.cs +++ /dev/null @@ -1,55 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.IO; - -namespace AlphaTab.Audio.Synth.Midi.Event -{ - class SystemExclusiveEvent : SystemCommonEvent - { - public byte[] Data { get; private set; } - - - public int ManufacturerId - { - get - { - return Message >> 8; - } - } - - public SystemExclusiveEvent(int delta, byte status, short id, byte[] data) - : base(delta, status, (byte)(id & 0x00FF), (byte)(id >> 8)) - { - Data = data; - } - - public override void WriteTo(IWriteable s) - { - s.WriteByte(0xF0); - var l = Data.Length + 2; - s.WriteByte((byte)ManufacturerId); - var b = new[]{ - (byte)((l >> 24) & 0xFF), (byte)((l >> 16) & 0xFF), - (byte)((l >> 8) & 0xFF), (byte)((l >> 0) & 0xFF) - }; - s.Write(b, 0, b.Length); - s.WriteByte(0xF7); - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Midi/MidiFile.cs b/Source/AlphaTab/Audio/Synth/Midi/MidiFile.cs deleted file mode 100644 index 0a131b6ba..000000000 --- a/Source/AlphaTab/Audio/Synth/Midi/MidiFile.cs +++ /dev/null @@ -1,166 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Audio.Synth.Midi.Event; -using AlphaTab.Collections; -using AlphaTab.IO; - -namespace AlphaTab.Audio.Synth.Midi -{ - /// - /// Represents a midi file with a single track that can be played via - /// - public class MidiFile - { - /// - /// Gets or sets the division per quarter notes. - /// - public int Division { get; set; } - - /// - /// Gets a list of midi events sorted by time. - /// - public FastList Events { get; } - - /// - /// Initializes a new instance of the class. - /// - public MidiFile() - { - Division = MidiUtils.QuarterTime; - Events = new FastList(); - } - - /// - /// Adds the given midi event a the correct time position into the file. - /// - /// - public void AddEvent(MidiEvent e) - { - if (Events.Count == 0) - { - Events.Add(e); - } - else - { - var insertPos = Events.Count; - while (insertPos > 0) - { - var prevItem = Events[insertPos - 1]; - if (prevItem.Tick > e.Tick) - { - insertPos--; - } - else - { - break; - } - } - Events.InsertAt(insertPos, e); - } - } - - /// - /// Writes the midi file into a binary format. - /// - /// The binary midi file. - // ReSharper disable once UnusedMember.Global - public byte[] ToBinary() - { - var data = ByteBuffer.Empty(); - WriteTo(data); - return data.ToArray(); - } - - /// - /// Writes the midi file as binary into the given stream. - /// - /// The stream to write to. - public void WriteTo(IWriteable s) - { - // magic number "MThd" (0x4D546864) - var b = new byte[] { 0x4D, 0x54, 0x68, 0x64 }; - s.Write(b, 0, b.Length); - - // Header Length 6 (0x00000006) - b = new byte[] { 0x00, 0x00, 0x00, 0x06 }; - s.Write(b, 0, b.Length); - - // format - b = new byte[] { 0x00, 0x00 }; - s.Write(b, 0, b.Length); - - // number of tracks - short v = 1; - b = new[] { (byte)((v >> 8) & 0xFF), (byte)(v & 0xFF) }; - s.Write(b, 0, b.Length); - - v = MidiUtils.QuarterTime; - b = new[] { (byte)((v >> 8) & 0xFF), (byte)(v & 0xFF) }; - s.Write(b, 0, b.Length); - - // build track data first - var trackData = ByteBuffer.Empty(); - var previousTick = 0; - foreach (var midiEvent in Events) - { - var delta = midiEvent.Tick - previousTick; - WriteVariableInt(trackData, delta); - midiEvent.WriteTo(trackData); - previousTick = midiEvent.Tick; - } - - // end of track - - // magic number "MTrk" (0x4D54726B) - b = new byte[] { 0x4D, 0x54, 0x72, 0x6B }; - s.Write(b, 0, b.Length); - - // size as integer - var data = trackData.ToArray(); - var l = data.Length; - b = new[]{ - (byte)((l >> 24) & 0xFF), (byte)((l >> 16) & 0xFF), - (byte)((l >> 8) & 0xFF), (byte)((l >> 0) & 0xFF) - }; - s.Write(b, 0, b.Length); - s.Write(data, 0, data.Length); - } - - internal static void WriteVariableInt(IWriteable s, int value) - { - var array = new byte[4]; - - var n = 0; - do - { - array[n++] = (byte)((value & 0x7F) & 0xFF); - value >>= 7; - } while (value > 0); - - while (n > 0) - { - n--; - if (n > 0) - s.WriteByte((byte)(array[n] | 0x80)); - else - s.WriteByte(array[n]); - } - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Midi/MidiHelper.cs b/Source/AlphaTab/Audio/Synth/Midi/MidiHelper.cs deleted file mode 100644 index 242a6e03c..000000000 --- a/Source/AlphaTab/Audio/Synth/Midi/MidiHelper.cs +++ /dev/null @@ -1,27 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Audio.Synth.Midi -{ - static class MidiHelper - { - public const int MicroSecondsPerMinute = 60000000; - public const int MinChannel = 0; - public const int MaxChannel = 15; - public const int DrumChannel = 9; - } -} diff --git a/Source/AlphaTab/Audio/Synth/MidiFileSequencer.cs b/Source/AlphaTab/Audio/Synth/MidiFileSequencer.cs deleted file mode 100644 index 073d7c6a9..000000000 --- a/Source/AlphaTab/Audio/Synth/MidiFileSequencer.cs +++ /dev/null @@ -1,392 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.Audio.Synth.Midi; -using AlphaTab.Audio.Synth.Midi.Event; -using AlphaTab.Audio.Synth.Synthesis; -using AlphaTab.Collections; -using AlphaTab.Util; - -namespace AlphaTab.Audio.Synth -{ - /// - /// This sequencer dispatches midi events to the synthesizer based on the current - /// synthesize position. The sequencer does not consider the playback speed. - /// - class MidiFileSequencer - { - private readonly Synthesizer _synthesizer; - - private FastList _tempoChanges; - private FastDictionary _firstProgramEventPerChannel; - private FastList _synthData; - private int _division; - private int _eventIndex; - - /// - /// Note that this is not the actual playback position. It's the position where we are currently synthesizing at. - /// Depending on the buffer size of the output, this position is after the actual playback. - /// - private double _currentTime; - - - private PlaybackRange _playbackRange; - private double _playbackRangeStartTime; - private double _playbackRangeEndTime; - private double _endTime; - - public PlaybackRange PlaybackRange - { - get { return _playbackRange; } - set - { - _playbackRange = value; - if (value != null) - { - _playbackRangeStartTime = TickPositionToTimePositionWithSpeed(value.StartTick, 1); - _playbackRangeEndTime = TickPositionToTimePositionWithSpeed(value.EndTick, 1); - } - } - } - - public bool IsLooping { get; set; } - - /// - /// Gets the duration of the song in ticks. - /// - public int EndTick { get; private set; } - - /// - /// Gets the duration of the song in milliseconds. - /// - public double EndTime - { - get { return _endTime / PlaybackSpeed; } - } - - /// - /// Gets or sets the playback speed. - /// - public double PlaybackSpeed - { - get; set; - } - - public MidiFileSequencer(Synthesizer synthesizer) - { - _synthesizer = synthesizer; - _firstProgramEventPerChannel = new FastDictionary(); - _tempoChanges = new FastList(); - PlaybackSpeed = 1; - } - - public void Seek(double timePosition) - { - // map to speed=1 - timePosition *= PlaybackSpeed; - - // ensure playback range - if (PlaybackRange != null) - { - if (timePosition < _playbackRangeStartTime) - { - timePosition = _playbackRangeStartTime; - } - else if (timePosition > _playbackRangeEndTime) - { - timePosition = _playbackRangeEndTime; - } - } - - // move back some ticks to ensure the on-time events are played - timePosition -= 25; - if (timePosition < 0) - { - timePosition = 0; - } - - if (timePosition > _currentTime) - { - SilentProcess(timePosition - _currentTime); - } - else if (timePosition < _currentTime) - { - //we have to restart the midi to make sure we get the right state: instruments, volume, pan, etc - _currentTime = 0; - _eventIndex = 0; - _synthesizer.NoteOffAll(true); - _synthesizer.ResetPrograms(); - _synthesizer.ResetSynthControls(); - - SilentProcess(timePosition); - } - } - - private void SilentProcess(double milliseconds) - { - if (milliseconds <= 0) return; - - var start = Platform.Platform.GetCurrentMilliseconds(); - - var finalTime = _currentTime + milliseconds; - - while (_currentTime < finalTime) - { - if (FillMidiEventQueueLimited(finalTime - _currentTime)) - { - _synthesizer.SynthesizeSilent(); - } - } - - var duration = Platform.Platform.GetCurrentMilliseconds() - start; - Logger.Debug("Sequencer", "Silent seek finished in " + duration + "ms"); - } - - - public void LoadMidi(MidiFile midiFile) - { - _tempoChanges = new FastList(); - - _division = midiFile.Division; - _eventIndex = 0; - _currentTime = 0; - - // build synth events. - _synthData = new FastList(); - - // Converts midi to milliseconds for easy sequencing - double bpm = 120; - var absTick = 0; - var absTime = 0.0; - - var metronomeLength = 0; - var metronomeTick = 0; - var metronomeTime = 0.0; - - var previousTick = 0; - - foreach (var mEvent in midiFile.Events) - { - var synthData = new SynthEvent(_synthData.Count, mEvent); - _synthData.Add(synthData); - - var deltaTick = mEvent.Tick - previousTick; - absTick += deltaTick; - absTime += deltaTick * (60000.0 / (bpm * midiFile.Division)); - synthData.Time = absTime; - previousTick = mEvent.Tick; - - if (mEvent.Command == MidiEventType.Meta && mEvent.Data1 == (int)MetaEventTypeEnum.Tempo) - { - var meta = (MetaNumberEvent)mEvent; - bpm = MidiHelper.MicroSecondsPerMinute / (double)meta.Value; - _tempoChanges.Add(new MidiFileSequencerTempoChange(bpm, absTick, (int)(absTime))); - } - else if (mEvent.Command == MidiEventType.Meta && mEvent.Data1 == (int)MetaEventTypeEnum.TimeSignature) - { - var meta = (MetaDataEvent)mEvent; - var timeSignatureDenominator = (int)Math.Pow(2, meta.Data[1]); - metronomeLength = (int)(_division * (4.0 / timeSignatureDenominator)); - } - else if (mEvent.Command == MidiEventType.ProgramChange) - { - var channel = mEvent.Channel; - if (!_firstProgramEventPerChannel.ContainsKey(channel)) - { - _firstProgramEventPerChannel[channel] = synthData; - } - } - - if (metronomeLength > 0) - { - while (metronomeTick < absTick) - { - var metronome = SynthEvent.NewMetronomeEvent(_synthData.Count, metronomeLength); - _synthData.Add(metronome); - metronome.Time = metronomeTime; - - metronomeTick += metronomeLength; - metronomeTime += metronomeLength * (60000.0 / (bpm * midiFile.Division)); - } - } - } - - _synthData.Sort((a, b) => - { - if (a.Time > b.Time) - { - return 1; - } - else if (a.Time < b.Time) - { - return -1; - } - return a.EventIndex - b.EventIndex; - }); - _endTime = absTime; - EndTick = absTick; - } - - - public bool FillMidiEventQueue() - { - return FillMidiEventQueueLimited(-1); - } - - private bool FillMidiEventQueueLimited(double maxMilliseconds) - { - var millisecondsPerBuffer = (_synthesizer.MicroBufferSize / (double)_synthesizer.SampleRate) * 1000 * PlaybackSpeed; - if (maxMilliseconds > 0 && maxMilliseconds < millisecondsPerBuffer) - { - millisecondsPerBuffer = maxMilliseconds; - } - - bool anyEventsDispatched = false; - for (int i = 0; i < _synthesizer.MicroBufferCount; i++) - { - _currentTime += millisecondsPerBuffer; - while (_eventIndex < _synthData.Count && _synthData[_eventIndex].Time < _currentTime) - { - _synthesizer.DispatchEvent(i, _synthData[_eventIndex]); - _eventIndex++; - anyEventsDispatched = true; - } - } - return anyEventsDispatched; - } - - public double TickPositionToTimePosition(int tickPosition) - { - return TickPositionToTimePositionWithSpeed(tickPosition, PlaybackSpeed); - } - - public int TimePositionToTickPosition(double timePosition) - { - return TimePositionToTickPositionWithSpeed(timePosition, PlaybackSpeed); - } - - private double TickPositionToTimePositionWithSpeed(int tickPosition, double playbackSpeed) - { - var timePosition = 0.0; - var bpm = 120.0; - var lastChange = 0; - - // find start and bpm of last tempo change before time - for (int i = 0; i < _tempoChanges.Count; i++) - { - var c = _tempoChanges[i]; - if (tickPosition < c.Ticks) - { - break; - } - timePosition = c.Time; - bpm = c.Bpm; - lastChange = c.Ticks; - } - - // add the missing millis - tickPosition -= lastChange; - timePosition += (tickPosition * (60000.0 / (bpm * _division))); - - return timePosition / playbackSpeed; - } - - private int TimePositionToTickPositionWithSpeed(double timePosition, double playbackSpeed) - { - timePosition *= playbackSpeed; - - var ticks = 0; - var bpm = 120.0; - var lastChange = 0; - - // find start and bpm of last tempo change before time - for (int i = 0; i < _tempoChanges.Count; i++) - { - var c = _tempoChanges[i]; - if (timePosition < c.Time) - { - break; - } - ticks = c.Ticks; - bpm = c.Bpm; - lastChange = c.Time; - } - - // add the missing ticks - timePosition -= lastChange; - ticks += (int)(timePosition / (60000.0 / (bpm * _division))); - // we add 1 for possible rounding errors.(floating point issuses) - return ticks + 1; - } - - public event Action Finished; - protected virtual void OnFinished() - { - var finished = Finished; - if (finished != null) - { - finished(); - } - } - - - public void CheckForStop() - { - if (PlaybackRange == null && _currentTime >= _endTime) - { - _currentTime = 0; - _eventIndex = 0; - _synthesizer.NoteOffAll(true); - _synthesizer.ResetPrograms(); - _synthesizer.ResetSynthControls(); - OnFinished(); - } - else if (PlaybackRange != null && _currentTime >= _playbackRangeEndTime) - { - _currentTime = PlaybackRange.StartTick; - _eventIndex = 0; - _synthesizer.NoteOffAll(true); - _synthesizer.ResetPrograms(); - _synthesizer.ResetSynthControls(); - OnFinished(); - } - } - - public void SetChannelProgram(int channel, byte program) - { - if (_firstProgramEventPerChannel.ContainsKey(channel)) - { - _firstProgramEventPerChannel[channel].Event.Data1 = program; - } - } - } - - class MidiFileSequencerTempoChange - { - public double Bpm { get; set; } - public int Ticks { get; set; } - public int Time { get; set; } - - public MidiFileSequencerTempoChange(double bpm, int ticks, int time) - { - Bpm = bpm; - Ticks = ticks; - Time = time; - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/PlayerState.cs b/Source/AlphaTab/Audio/Synth/PlayerState.cs deleted file mode 100644 index 7b4370d89..000000000 --- a/Source/AlphaTab/Audio/Synth/PlayerState.cs +++ /dev/null @@ -1,34 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Audio.Synth -{ - /// - /// Lists the different states of the player - /// - public enum PlayerState - { - /// - /// Player is paused - /// - Paused, - /// - /// Player is playing - /// - Playing - } -} diff --git a/Source/AlphaTab/Audio/Synth/Sf2/Chunks/Chunk.cs b/Source/AlphaTab/Audio/Synth/Sf2/Chunks/Chunk.cs deleted file mode 100644 index 3c4cede92..000000000 --- a/Source/AlphaTab/Audio/Synth/Sf2/Chunks/Chunk.cs +++ /dev/null @@ -1,33 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System.Runtime.CompilerServices; - -namespace AlphaTab.Audio.Synth.Sf2.Chunks -{ - class Chunk - { - public string Id { get; private set; } - public int Size { get; private set; } - - public Chunk(string id, int size) - { - Id = id; - Size = size; - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Sf2/Chunks/GeneratorChunk.cs b/Source/AlphaTab/Audio/Synth/Sf2/Chunks/GeneratorChunk.cs deleted file mode 100644 index 138eaabe1..000000000 --- a/Source/AlphaTab/Audio/Synth/Sf2/Chunks/GeneratorChunk.cs +++ /dev/null @@ -1,39 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.IO; - -namespace AlphaTab.Audio.Synth.Sf2.Chunks -{ - class GeneratorChunk : Chunk - { - public Generator[] Generators { get; set; } - - public GeneratorChunk(string id, int size, IReadable input) : base(id, size) - { - if(size % 4 != 0) - throw new Exception("Invalid SoundFont. The presetzone chunk was invalid"); - Generators = new Generator[(int) ((size/4.0) - 1)]; - for (int x = 0; x < Generators.Length; x++) - { - Generators[x] = new Generator(input); - } - new Generator(input); // terminal record - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Sf2/Chunks/InstrumentChunk.cs b/Source/AlphaTab/Audio/Synth/Sf2/Chunks/InstrumentChunk.cs deleted file mode 100644 index ae1747b31..000000000 --- a/Source/AlphaTab/Audio/Synth/Sf2/Chunks/InstrumentChunk.cs +++ /dev/null @@ -1,70 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.IO; - -namespace AlphaTab.Audio.Synth.Sf2.Chunks -{ - class InstrumentChunk : Chunk - { - private RawInstrument[] _rawInstruments; - - public InstrumentChunk(string id, int size, IReadable input) - : base(id, size) - { - if (size % 22 != 0) - throw new Exception("Invalid SoundFont. The preset chunk was invalid."); - _rawInstruments = new RawInstrument[((int)(size / 22.0))]; - RawInstrument lastInstrument = null; - for (int x = 0; x < _rawInstruments.Length; x++) - { - var i = new RawInstrument(); - i.Name = input.Read8BitStringLength(20); - i.StartInstrumentZoneIndex = input.ReadUInt16LE(); - if (lastInstrument != null) - { - lastInstrument.EndInstrumentZoneIndex = Platform.Platform.ToUInt16((i.StartInstrumentZoneIndex - 1)); - } - _rawInstruments[x] = i; - lastInstrument = i; - } - } - - public Instrument[] ToInstruments(Zone[] zones) - { - var inst = new Instrument[_rawInstruments.Length - 1]; - for (int x = 0; x < inst.Length; x++) - { - var rawInst = _rawInstruments[x]; - var i = new Instrument(); - i.Name = rawInst.Name; - i.Zones = new Zone[rawInst.EndInstrumentZoneIndex - rawInst.StartInstrumentZoneIndex + 1]; - Platform.Platform.ArrayCopy(zones, rawInst.StartInstrumentZoneIndex, i.Zones, 0, i.Zones.Length); - inst[x] = i; - } - return inst; - } - } - - class RawInstrument - { - public string Name { get; set; } - public int StartInstrumentZoneIndex { get; set; } - public int EndInstrumentZoneIndex { get; set; } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Sf2/Chunks/ModulatorChunk.cs b/Source/AlphaTab/Audio/Synth/Sf2/Chunks/ModulatorChunk.cs deleted file mode 100644 index 35a137e94..000000000 --- a/Source/AlphaTab/Audio/Synth/Sf2/Chunks/ModulatorChunk.cs +++ /dev/null @@ -1,40 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.IO; - -namespace AlphaTab.Audio.Synth.Sf2.Chunks -{ - class ModulatorChunk : Chunk - { - public Modulator[] Modulators { get; set; } - - public ModulatorChunk(string id, int size, IReadable input) - : base(id, size) - { - if(size % 10 != 0) - throw new Exception("Invalid SoundFont. The presetzone chunk was invalid."); - Modulators = new Modulator[(size/10) - 1]; - for (int x = 0; x < Modulators.Length; x++) - { - Modulators[x] = new Modulator(input); - } - new Modulator(input); - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Sf2/Chunks/PresetHeaderChunk.cs b/Source/AlphaTab/Audio/Synth/Sf2/Chunks/PresetHeaderChunk.cs deleted file mode 100644 index 3416d3575..000000000 --- a/Source/AlphaTab/Audio/Synth/Sf2/Chunks/PresetHeaderChunk.cs +++ /dev/null @@ -1,86 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.IO; - -namespace AlphaTab.Audio.Synth.Sf2.Chunks -{ - class PresetHeaderChunk : Chunk - { - private readonly RawPreset[] _rawPresets; - - public PresetHeaderChunk(string id, int size, IReadable input) - : base(id, size) - { - if (size % 38 != 0) - throw new Exception("Invalid SoundFont. The preset chunk was invalid."); - - _rawPresets = new RawPreset[((int)(size / 38.0))]; - RawPreset lastPreset = null; - for (int x = 0; x < _rawPresets.Length; x++) - { - var p = new RawPreset(); - p.Name = input.Read8BitStringLength(20); - p.PatchNumber = input.ReadUInt16LE(); - p.BankNumber = input.ReadUInt16LE(); - p.StartPresetZoneIndex = input.ReadUInt16LE(); - p.Library = input.ReadInt32LE(); - p.Genre = input.ReadInt32LE(); - p.Morphology = input.ReadInt32LE(); - if (lastPreset != null) - { - lastPreset.EndPresetZoneIndex = Platform.Platform.ToUInt16((p.StartPresetZoneIndex - 1)); - } - _rawPresets[x] = p; - lastPreset = p; - } - } - - public PresetHeader[] ToPresets(Zone[] presetZones) - { - var presets = new PresetHeader[(_rawPresets.Length - 1)]; - for (int x = 0; x < presets.Length; x++) - { - var rawPreset = _rawPresets[x]; - var p = new PresetHeader(); - p.BankNumber = rawPreset.BankNumber; - p.Genre = rawPreset.Genre; - p.Library = rawPreset.Library; - p.Morphology = rawPreset.Morphology; - p.Name = rawPreset.Name; - p.PatchNumber = rawPreset.PatchNumber; - p.Zones = new Zone[(rawPreset.EndPresetZoneIndex - rawPreset.StartPresetZoneIndex + 1)]; - Platform.Platform.ArrayCopy(presetZones, rawPreset.StartPresetZoneIndex, p.Zones, 0, p.Zones.Length); - presets[x] = p; - } - return presets; - } - } - - class RawPreset - { - public string Name { get; set; } - public int PatchNumber { get; set; } - public int BankNumber { get; set; } - public int StartPresetZoneIndex { get; set; } - public int EndPresetZoneIndex { get; set; } - public int Library { get; set; } - public int Genre { get; set; } - public int Morphology { get; set; } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Sf2/Chunks/SampleHeaderChunk.cs b/Source/AlphaTab/Audio/Synth/Sf2/Chunks/SampleHeaderChunk.cs deleted file mode 100644 index 5508a1be9..000000000 --- a/Source/AlphaTab/Audio/Synth/Sf2/Chunks/SampleHeaderChunk.cs +++ /dev/null @@ -1,41 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.IO; - -namespace AlphaTab.Audio.Synth.Sf2.Chunks -{ - class SampleHeaderChunk : Chunk - { - public SampleHeader[] SampleHeaders { get; set; } - - public SampleHeaderChunk(string id, int size, IReadable input) - : base(id, size) - { - if (size % 46 != 0) - throw new Exception("Invalid SoundFont. The sample header chunk was invalid."); - SampleHeaders = new SampleHeader[(int)((size / 46.0) - 1)]; - - for (int x = 0; x < SampleHeaders.Length; x++) - { - SampleHeaders[x] = new SampleHeader(input); - } - new SampleHeader(input); //read terminal record - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Sf2/Chunks/ZoneChunk.cs b/Source/AlphaTab/Audio/Synth/Sf2/Chunks/ZoneChunk.cs deleted file mode 100644 index 5ae2aefb9..000000000 --- a/Source/AlphaTab/Audio/Synth/Sf2/Chunks/ZoneChunk.cs +++ /dev/null @@ -1,74 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Audio.Synth.Util; -using AlphaTab.IO; -using AlphaTab.Platform; - -namespace AlphaTab.Audio.Synth.Sf2.Chunks -{ - class ZoneChunk : Chunk - { - private RawZoneData[] _zoneData; - - public ZoneChunk(string id, int size, IReadable input) - : base(id, size) - { - _zoneData = new RawZoneData[((int)(size / 4.0))]; - - RawZoneData lastZone = null; - for (int x = 0; x < _zoneData.Length; x++) - { - var z = new RawZoneData(); - z.GeneratorIndex = input.ReadUInt16LE(); - z.ModulatorIndex = input.ReadUInt16LE(); - if (lastZone != null) - { - lastZone.GeneratorCount = Platform.Platform.ToUInt16(z.GeneratorIndex - lastZone.GeneratorIndex); - lastZone.ModulatorCount = Platform.Platform.ToUInt16(z.ModulatorIndex - lastZone.ModulatorIndex); - } - _zoneData[x] = z; - lastZone = z; - } - } - - public Zone[] ToZones(Modulator[] modulators, Generator[] generators) - { - var zones = new Zone[(_zoneData.Length- 1)]; - for (int x = 0; x < zones.Length; x++) - { - var rawZone = _zoneData[x]; - var zone = new Zone(); - zone.Generators = new Generator[rawZone.GeneratorCount]; - Platform.Platform.ArrayCopy(generators, rawZone.GeneratorIndex, zone.Generators, 0, rawZone.GeneratorCount); - zone.Modulators = new Modulator[rawZone.ModulatorCount]; - Platform.Platform.ArrayCopy(modulators, rawZone.ModulatorIndex, zone.Modulators, 0, rawZone.ModulatorCount); - zones[x] = zone; - } - return zones; - } - } - - class RawZoneData - { - public int GeneratorIndex { get; set; } - public int ModulatorIndex { get; set; } - public int GeneratorCount { get; set; } - public int ModulatorCount { get; set; } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Sf2/ControllerSourceEnum.cs b/Source/AlphaTab/Audio/Synth/Sf2/ControllerSourceEnum.cs deleted file mode 100644 index 0624b9dd1..000000000 --- a/Source/AlphaTab/Audio/Synth/Sf2/ControllerSourceEnum.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace AlphaTab.Audio.Synth.Sf2 -{ - enum ControllerSourceEnum - { - NoController = 0, - NoteOnVelocity = 2, - NoteOnKeyNumber = 3, - PolyPressure = 10, - ChannelPressure = 13, - PitchWheel = 14, - PitchWheelSensitivity = 16, - Link = 127 - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Audio/Synth/Sf2/DirectionEnum.cs b/Source/AlphaTab/Audio/Synth/Sf2/DirectionEnum.cs deleted file mode 100644 index 86db66b97..000000000 --- a/Source/AlphaTab/Audio/Synth/Sf2/DirectionEnum.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace AlphaTab.Audio.Synth.Sf2 -{ - enum DirectionEnum - { - MinToMax = 0, - MaxToMin = 1 - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Audio/Synth/Sf2/Generator.cs b/Source/AlphaTab/Audio/Synth/Sf2/Generator.cs deleted file mode 100644 index 5675a24bf..000000000 --- a/Source/AlphaTab/Audio/Synth/Sf2/Generator.cs +++ /dev/null @@ -1,55 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Audio.Synth.Util; -using AlphaTab.IO; -using AlphaTab.Platform; - -namespace AlphaTab.Audio.Synth.Sf2 -{ - class Generator - { - private ushort _rawAmount; - - public GeneratorEnum GeneratorType { get; set; } - - public short AmountInt16 - { - get { return Platform.Platform.ToInt16(_rawAmount); } - set { _rawAmount = Platform.Platform.ToUInt16(value); } - } - - public short LowByteAmount - { - get { return Platform.Platform.ToUInt8(_rawAmount & 0x00FF); } - set { _rawAmount = Platform.Platform.ToUInt16((_rawAmount & 0xFF00) + Platform.Platform.ToUInt8(value)); } - } - - public short HighByteAmount - { - get { return Platform.Platform.ToUInt8((_rawAmount & 0xFF00) >> 8); } - set { _rawAmount = Platform.Platform.ToUInt16((_rawAmount & 0x00FF) + (Platform.Platform.ToUInt8(value) << 8)); } - } - - public Generator(IReadable input) - { - GeneratorType = (GeneratorEnum)input.ReadUInt16LE(); - _rawAmount = input.ReadUInt16LE(); - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Sf2/GeneratorEnum.cs b/Source/AlphaTab/Audio/Synth/Sf2/GeneratorEnum.cs deleted file mode 100644 index 490ab48f8..000000000 --- a/Source/AlphaTab/Audio/Synth/Sf2/GeneratorEnum.cs +++ /dev/null @@ -1,67 +0,0 @@ -namespace AlphaTab.Audio.Synth.Sf2 -{ - enum GeneratorEnum - { - StartAddressOffset = 0, - EndAddressOffset = 1, - StartLoopAddressOffset = 2, - EndLoopAddressOffset = 3, - StartAddressCoarseOffset = 4, - ModulationLFOToPitch = 5, - VibratoLFOToPitch = 6, - ModulationEnvelopeToPitch = 7, - InitialFilterCutoffFrequency = 8, - InitialFilterQ = 9, - ModulationLFOToFilterCutoffFrequency = 10, - ModulationEnvelopeToFilterCutoffFrequency = 11, - EndAddressCoarseOffset = 12, - ModulationLFOToVolume = 13, - Unused1 = 14, - ChorusEffectsSend = 15, - ReverbEffectsSend = 16, - Pan = 17, - Unused2 = 18, - Unused3 = 19, - Unused4 = 20, - DelayModulationLFO = 21, - FrequencyModulationLFO = 22, - DelayVibratoLFO = 23, - FrequencyVibratoLFO = 24, - DelayModulationEnvelope = 25, - AttackModulationEnvelope = 26, - HoldModulationEnvelope = 27, - DecayModulationEnvelope = 28, - SustainModulationEnvelope = 29, - ReleaseModulationEnvelope = 30, - KeyNumberToModulationEnvelopeHold = 31, - KeyNumberToModulationEnvelopeDecay = 32, - DelayVolumeEnvelope = 33, - AttackVolumeEnvelope = 34, - HoldVolumeEnvelope = 35, - DecayVolumeEnvelope = 36, - SustainVolumeEnvelope = 37, - ReleaseVolumeEnvelope = 38, - KeyNumberToVolumeEnvelopeHold = 39, - KeyNumberToVolumeEnvelopeDecay = 40, - Instrument = 41, - Reserved1 = 42, - KeyRange = 43, - VelocityRange = 44, - StartLoopAddressCoarseOffset = 45, - KeyNumber = 46, - Velocity = 47, - InitialAttenuation = 48, - Reserved2 = 49, - EndLoopAddressCoarseOffset = 50, - CoarseTune = 51, - FineTune = 52, - SampleID = 53, - SampleModes = 54, - Reserved3 = 55, - ScaleTuning = 56, - ExclusiveClass = 57, - OverridingRootKey = 58, - Unused5 = 59, - UnusedEnd = 60 - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Audio/Synth/Sf2/Instrument.cs b/Source/AlphaTab/Audio/Synth/Sf2/Instrument.cs deleted file mode 100644 index 1051e92e8..000000000 --- a/Source/AlphaTab/Audio/Synth/Sf2/Instrument.cs +++ /dev/null @@ -1,25 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Audio.Synth.Sf2 -{ - class Instrument - { - public string Name { get; set; } - public Zone[] Zones { get; set; } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Sf2/Modulator.cs b/Source/AlphaTab/Audio/Synth/Sf2/Modulator.cs deleted file mode 100644 index 33c28ce31..000000000 --- a/Source/AlphaTab/Audio/Synth/Sf2/Modulator.cs +++ /dev/null @@ -1,41 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Audio.Synth.Util; -using AlphaTab.IO; - -namespace AlphaTab.Audio.Synth.Sf2 -{ - class Modulator - { - private ModulatorType _sourceModulationData; - private int _destinationGenerator; - private short _amount; - private ModulatorType _sourceModulationAmount; - private int _sourceTransform; - - public Modulator(IReadable input) - { - _sourceModulationData = new ModulatorType(input); - _destinationGenerator = input.ReadUInt16LE(); - _amount = input.ReadInt16LE(); - _sourceModulationAmount = new ModulatorType(input); - _sourceTransform = input.ReadUInt16LE(); - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Sf2/ModulatorType.cs b/Source/AlphaTab/Audio/Synth/Sf2/ModulatorType.cs deleted file mode 100644 index 477d89632..000000000 --- a/Source/AlphaTab/Audio/Synth/Sf2/ModulatorType.cs +++ /dev/null @@ -1,46 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Audio.Synth.Util; -using AlphaTab.IO; -using AlphaTab.Platform; - -namespace AlphaTab.Audio.Synth.Sf2 -{ - class ModulatorType - { - private ushort _controllerSource; - - public PolarityEnum Polarity { get; set; } - public DirectionEnum Direction { get; set; } - public int SourceType { get; set; } - public bool IsMidiContinuousController { get; private set; } - - public ModulatorType(IReadable input) - { - var raw = input.ReadUInt16LE(); - - Polarity = (raw & 0x0200) == 0x0200 ? PolarityEnum.Bipolar : PolarityEnum.Unipolar; - Direction = (raw & 0x0100) == 0x0100 ? DirectionEnum.MaxToMin : DirectionEnum.MinToMax; - - IsMidiContinuousController = ((raw & 0x0080) == 0x0080); - SourceType = ((raw & (0xFC00)) >> 10); - _controllerSource = Platform.Platform.ToUInt16((raw & 0x007F)); - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Sf2/PolarityEnum.cs b/Source/AlphaTab/Audio/Synth/Sf2/PolarityEnum.cs deleted file mode 100644 index d65e6e725..000000000 --- a/Source/AlphaTab/Audio/Synth/Sf2/PolarityEnum.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace AlphaTab.Audio.Synth.Sf2 -{ - enum PolarityEnum - { - Unipolar = 0, - Bipolar = 1 - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Audio/Synth/Sf2/PresetHeader.cs b/Source/AlphaTab/Audio/Synth/Sf2/PresetHeader.cs deleted file mode 100644 index 9e1b45b67..000000000 --- a/Source/AlphaTab/Audio/Synth/Sf2/PresetHeader.cs +++ /dev/null @@ -1,30 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Audio.Synth.Sf2 -{ - class PresetHeader - { - public string Name { get; set; } - public int PatchNumber { get; set; } - public int BankNumber { get; set; } - public int Library { get; set; } - public int Genre { get; set; } - public int Morphology { get; set; } - public Zone[] Zones { get; set; } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Sf2/SFSampleLink.cs b/Source/AlphaTab/Audio/Synth/Sf2/SFSampleLink.cs deleted file mode 100644 index dbac9e583..000000000 --- a/Source/AlphaTab/Audio/Synth/Sf2/SFSampleLink.cs +++ /dev/null @@ -1,32 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Audio.Synth.Sf2 -{ - enum SFSampleLink - { - MonoSample = 1, - RightSample = 2, - LeftSample = 4, - LinkedSample = 8, - OggVobis = 0x10, - RomMonoSample = 0x8001, - RomRightSample = 0x8002, - RomLeftSample = 0x8004, - RomLinkedSample = 0x8008 - } -} diff --git a/Source/AlphaTab/Audio/Synth/Sf2/SampleHeader.cs b/Source/AlphaTab/Audio/Synth/Sf2/SampleHeader.cs deleted file mode 100644 index 9c98c994c..000000000 --- a/Source/AlphaTab/Audio/Synth/Sf2/SampleHeader.cs +++ /dev/null @@ -1,52 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Audio.Synth.Util; -using AlphaTab.IO; -using AlphaTab.Platform; - -namespace AlphaTab.Audio.Synth.Sf2 -{ - class SampleHeader - { - public string Name { get; set; } - public int Start { get; private set; } - public int End { get; private set; } - public int StartLoop { get; private set; } - public int EndLoop { get; private set; } - public int SampleRate { get; private set; } - public byte RootKey { get; private set; } - public short Tune { get; private set; } - public ushort SampleLink { get; private set; } - public SFSampleLink SoundFontSampleLink { get; private set; } - - public SampleHeader(IReadable input) - { - Name = input.Read8BitStringLength(20); - Start = input.ReadInt32LE(); - End = input.ReadInt32LE(); - StartLoop = input.ReadInt32LE(); - EndLoop = input.ReadInt32LE(); - SampleRate = input.ReadInt32LE(); - RootKey = (byte) input.ReadByte(); - Tune = Platform.Platform.ToInt16(input.ReadByte()); - SampleLink = input.ReadUInt16LE(); - SoundFontSampleLink = (SFSampleLink) input.ReadUInt16LE(); - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Sf2/Sf2Region.cs b/Source/AlphaTab/Audio/Synth/Sf2/Sf2Region.cs deleted file mode 100644 index fc955c419..000000000 --- a/Source/AlphaTab/Audio/Synth/Sf2/Sf2Region.cs +++ /dev/null @@ -1,83 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Audio.Synth.Sf2 -{ - class Sf2Region - { - public short[] Generators { get; set; } - - public Sf2Region() - { - Generators = new short[61]; - } - - public void ApplyDefaultValues() - { - Generators[(int)GeneratorEnum.StartAddressOffset] = 0; - Generators[(int)GeneratorEnum.EndAddressOffset] = 0; - Generators[(int)GeneratorEnum.StartLoopAddressOffset] = 0; - Generators[(int)GeneratorEnum.EndLoopAddressOffset] = 0; - Generators[(int)GeneratorEnum.StartAddressCoarseOffset] = 0; - Generators[(int)GeneratorEnum.ModulationLFOToPitch] = 0; - Generators[(int)GeneratorEnum.VibratoLFOToPitch] = 0; - Generators[(int)GeneratorEnum.ModulationEnvelopeToPitch] = 0; - Generators[(int)GeneratorEnum.InitialFilterCutoffFrequency] = 13500; - Generators[(int)GeneratorEnum.InitialFilterQ] = 0; - Generators[(int)GeneratorEnum.ModulationLFOToFilterCutoffFrequency] = 0; - Generators[(int)GeneratorEnum.ModulationEnvelopeToFilterCutoffFrequency] = 0; - Generators[(int)GeneratorEnum.EndAddressCoarseOffset] = 0; - Generators[(int)GeneratorEnum.ModulationLFOToVolume] = 0; - Generators[(int)GeneratorEnum.ChorusEffectsSend] = 0; - Generators[(int)GeneratorEnum.ReverbEffectsSend] = 0; - Generators[(int)GeneratorEnum.Pan] = 0; - Generators[(int)GeneratorEnum.DelayModulationLFO] = -12000; - Generators[(int)GeneratorEnum.FrequencyModulationLFO] = 0; - Generators[(int)GeneratorEnum.DelayVibratoLFO] = -12000; - Generators[(int)GeneratorEnum.FrequencyVibratoLFO] = 0; - Generators[(int)GeneratorEnum.DelayModulationEnvelope] = -12000; - Generators[(int)GeneratorEnum.AttackModulationEnvelope] = -12000; - Generators[(int)GeneratorEnum.HoldModulationEnvelope] = -12000; - Generators[(int)GeneratorEnum.DecayModulationEnvelope] = -12000; - Generators[(int)GeneratorEnum.SustainModulationEnvelope] = 0; - Generators[(int)GeneratorEnum.ReleaseModulationEnvelope] = -12000; - Generators[(int)GeneratorEnum.KeyNumberToModulationEnvelopeHold] = 0; - Generators[(int)GeneratorEnum.KeyNumberToModulationEnvelopeDecay] = 0; - Generators[(int)GeneratorEnum.DelayVolumeEnvelope] = -12000; - Generators[(int)GeneratorEnum.AttackVolumeEnvelope] = -12000; - Generators[(int)GeneratorEnum.HoldVolumeEnvelope] = -12000; - Generators[(int)GeneratorEnum.DecayVolumeEnvelope] = -12000; - Generators[(int)GeneratorEnum.SustainVolumeEnvelope] = 0; - Generators[(int)GeneratorEnum.ReleaseVolumeEnvelope] = -12000; - Generators[(int)GeneratorEnum.KeyNumberToVolumeEnvelopeHold] = 0; - Generators[(int)GeneratorEnum.KeyNumberToVolumeEnvelopeDecay] = 0; - Generators[(int)GeneratorEnum.KeyRange] = 0x7F00; - Generators[(int)GeneratorEnum.VelocityRange] = 0x7F00; - Generators[(int)GeneratorEnum.StartLoopAddressCoarseOffset] = 0; - Generators[(int)GeneratorEnum.KeyNumber] = -1; - Generators[(int)GeneratorEnum.Velocity] = -1; - Generators[(int)GeneratorEnum.InitialAttenuation] = 0; - Generators[(int)GeneratorEnum.EndLoopAddressCoarseOffset] = 0; - Generators[(int)GeneratorEnum.CoarseTune] = 0; - Generators[(int)GeneratorEnum.FineTune] = 0; - Generators[(int)GeneratorEnum.SampleModes] = 0; - Generators[(int)GeneratorEnum.ScaleTuning] = 100; - Generators[(int)GeneratorEnum.ExclusiveClass] = 0; - Generators[(int)GeneratorEnum.OverridingRootKey] = -1; - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Sf2/SoundFont.cs b/Source/AlphaTab/Audio/Synth/Sf2/SoundFont.cs deleted file mode 100644 index 0f416d1e2..000000000 --- a/Source/AlphaTab/Audio/Synth/Sf2/SoundFont.cs +++ /dev/null @@ -1,49 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.Audio.Synth.Util; -using AlphaTab.IO; -using AlphaTab.Util; - -namespace AlphaTab.Audio.Synth.Sf2 -{ - class SoundFont - { - public SoundFontInfo Info { get; set; } - public SoundFontSampleData SampleData { get; set; } - public SoundFontPresets Presets { get; set; } - - public void Load(IReadable input) - { - var id = input.Read8BitChars(4); - var size = input.ReadInt32LE(); - if (id.ToLower() != "riff") - throw new Exception("Invalid soundfont. Could not find RIFF header."); - id = input.Read8BitChars(4); - if (id.ToLower() != "sfbk") - throw new Exception("Invalid soundfont. Riff type is invalid."); - - Logger.Debug("SF2", "Reading info chunk"); - Info = new SoundFontInfo(input); - Logger.Debug("SF2", "Reading sampledata chunk"); - SampleData = new SoundFontSampleData(input); - Logger.Debug("SF2", "Reading preset chunk"); - Presets = new SoundFontPresets(input); - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Sf2/SoundFontInfo.cs b/Source/AlphaTab/Audio/Synth/Sf2/SoundFontInfo.cs deleted file mode 100644 index 22fdbc4b1..000000000 --- a/Source/AlphaTab/Audio/Synth/Sf2/SoundFontInfo.cs +++ /dev/null @@ -1,107 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.Audio.Synth.Util; -using AlphaTab.IO; - -namespace AlphaTab.Audio.Synth.Sf2 -{ - class SoundFontInfo - { - public short RomVersionMajor { get; set; } - public short RomVersionMinor { get; set; } - public short SfVersionMajor { get; set; } - public short SfVersionMinor { get; set; } - public string SoundEngine { get; set; } - public string BankName { get; set; } - public string DataRom { get; set; } - public string CreationDate { get; set; } - public string Author { get; set; } - public string TargetProduct { get; set; } - public string Copyright { get; set; } - public string Comments { get; set; } - public string Tools { get; set; } - - public SoundFontInfo(IReadable input) - { - Tools = ""; - Comments = ""; - Copyright = ""; - TargetProduct = ""; - Author = ""; - DataRom = ""; - CreationDate = ""; - BankName = ""; - SoundEngine = ""; - var id = input.Read8BitChars(4); - var size = input.ReadInt32LE(); - if (id.ToLower() != "list") - throw new Exception("Invalid soundfont. Could not find INFO LIST chunk."); - var readTo = input.Position + size; - id = input.Read8BitChars(4); - if (id.ToLower() != "info") - throw new Exception("Invalid soundfont. The LIST chunk is not of type INFO."); - - while (input.Position < readTo) - { - id = input.Read8BitChars(4); - size = input.ReadInt32LE(); - switch (id.ToLower()) - { - case "ifil": - SfVersionMajor = input.ReadInt16LE(); - SfVersionMinor = input.ReadInt16LE(); - break; - case "isng": - SoundEngine = input.Read8BitStringLength(size); - break; - case "inam": - BankName = input.Read8BitStringLength(size); - break; - case "irom": - DataRom = input.Read8BitStringLength(size); - break; - case "iver": - RomVersionMajor = input.ReadInt16LE(); - RomVersionMinor = input.ReadInt16LE(); - break; - case "icrd": - CreationDate = input.Read8BitStringLength(size); - break; - case "ieng": - Author = input.Read8BitStringLength(size); - break; - case "iprd": - TargetProduct = input.Read8BitStringLength(size); - break; - case "icop": - Copyright = input.Read8BitStringLength(size); - break; - case "icmt": - Comments = input.Read8BitStringLength(size); - break; - case "isft": - Tools = input.Read8BitStringLength(size); - break; - default: - throw new Exception("Invalid soundfont. The Chunk: " + id + " was not expected."); - } - } - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Sf2/SoundFontPresets.cs b/Source/AlphaTab/Audio/Synth/Sf2/SoundFontPresets.cs deleted file mode 100644 index 27c95f409..000000000 --- a/Source/AlphaTab/Audio/Synth/Sf2/SoundFontPresets.cs +++ /dev/null @@ -1,95 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.Audio.Synth.Util; -using AlphaTab.Audio.Synth.Sf2.Chunks; -using AlphaTab.IO; - -namespace AlphaTab.Audio.Synth.Sf2 -{ - class SoundFontPresets - { - public SampleHeader[] SampleHeaders { get; private set; } - public PresetHeader[] PresetHeaders { get; private set; } - public Instrument[] Instruments { get; private set; } - - public SoundFontPresets(IReadable input) - { - var id = input.Read8BitChars(4); - var size = input.ReadInt32LE(); - if (id.ToLower() != "list") - throw new Exception("Invalid soundfont. Could not find pdta LIST chunk."); - var readTo = input.Position + size; - id = input.Read8BitChars(4); - if (id.ToLower() != "pdta") - throw new Exception("Invalid soundfont. The LIST chunk is not of type pdta."); - - Modulator[] presetModulators = null; - Generator[] presetGenerators = null; - Modulator[] instrumentModulators = null; - Generator[] instrumentGenerators = null; - - ZoneChunk pbag = null; - ZoneChunk ibag = null; - PresetHeaderChunk phdr = null; - InstrumentChunk inst = null; - - while (input.Position < readTo) - { - id = input.Read8BitChars(4); - size = input.ReadInt32LE(); - switch (id.ToLower()) - { - case "phdr": - phdr = new PresetHeaderChunk(id, size, input); - break; - case "pbag": - pbag = new ZoneChunk(id, size, input); - break; - case "pmod": - presetModulators = new ModulatorChunk(id, size, input).Modulators; - break; - case "pgen": - presetGenerators = new GeneratorChunk(id, size, input).Generators; - break; - case "inst": - inst = new InstrumentChunk(id, size, input); - break; - case "ibag": - ibag = new ZoneChunk(id, size, input); - break; - case "imod": - instrumentModulators = new ModulatorChunk(id, size, input).Modulators; - break; - case "igen": - instrumentGenerators = new GeneratorChunk(id, size, input).Generators; - break; - case "shdr": - SampleHeaders = new SampleHeaderChunk(id, size, input).SampleHeaders; - break; - default: - throw new Exception("Invalid soundfont. Unrecognized sub chunk: " + id); - } - } - var pZones = pbag.ToZones(presetModulators, presetGenerators); - PresetHeaders = phdr.ToPresets(pZones); - var iZones = ibag.ToZones(instrumentModulators, instrumentGenerators); - Instruments = inst.ToInstruments(iZones); - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Sf2/SoundFontSampleData.cs b/Source/AlphaTab/Audio/Synth/Sf2/SoundFontSampleData.cs deleted file mode 100644 index bc824d6ba..000000000 --- a/Source/AlphaTab/Audio/Synth/Sf2/SoundFontSampleData.cs +++ /dev/null @@ -1,90 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.Audio.Synth.Util; -using AlphaTab.IO; - -namespace AlphaTab.Audio.Synth.Sf2 -{ - class SoundFontSampleData - { - public int BitsPerSample { get; set; } - public byte[] SampleData { get; set; } - - public SoundFontSampleData(IReadable input) - { - var id = input.Read8BitChars(4); - var size = input.ReadInt32LE(); - if (id.ToLower() != "list") - throw new Exception("Invalid soundfont. Could not find sdta LIST chunk."); - var readTo = input.Position + size; - id = input.Read8BitChars(4); - if (id.ToLower() != "sdta") - throw new Exception("Invalid soundfont. The LIST chunk is not of type sdta."); - - BitsPerSample = 0; - byte[] rawSampleData = null; - while (input.Position < readTo) - { - var subID = input.Read8BitChars(4); - size = input.ReadInt32LE(); - switch (subID.ToLower()) - { - case "smpl": - BitsPerSample = 16; - rawSampleData = input.ReadByteArray(size); - break; - case "sm24": - if (rawSampleData == null || size != Math.Ceiling(SampleData.Length / 2.0)) - {//ignore this chunk if wrong size or if it comes first - input.Skip(size); - } - else - { - BitsPerSample = 24; - for (var x = 0; x < SampleData.Length; x++) - { - var b = new byte[3]; - b[0] = (byte)input.ReadByte(); - b[1] = rawSampleData[2 * x]; - b[2] = rawSampleData[2 * x + 1]; - } - } - if (size % 2 == 1) - { - if (input.ReadByte() != 0) - { - input.Position--; - } - } - break; - default: - throw new Exception("Invalid soundfont. Unknown chunk id: " + subID + "."); - } - } - - if (BitsPerSample == 16) - { - SampleData = rawSampleData; - } - else if (BitsPerSample != 24) - throw new Exception("Only 16 and 24 bit samples are supported."); - - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Sf2/SourceTypeEnum.cs b/Source/AlphaTab/Audio/Synth/Sf2/SourceTypeEnum.cs deleted file mode 100644 index f92627d11..000000000 --- a/Source/AlphaTab/Audio/Synth/Sf2/SourceTypeEnum.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace AlphaTab.Audio.Synth.Sf2 -{ - enum SourceTypeEnum - { - Linear = 0, - Concave = 1, - Convex = 2, - Switch = 3 - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Audio/Synth/Sf2/TransformEnum.cs b/Source/AlphaTab/Audio/Synth/Sf2/TransformEnum.cs deleted file mode 100644 index 2ecabe9a6..000000000 --- a/Source/AlphaTab/Audio/Synth/Sf2/TransformEnum.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace AlphaTab.Audio.Synth.Sf2 -{ - enum TransformEnum - { - Linear = 0, - AbsoluteValue = 2 - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Audio/Synth/Sf2/Zone.cs b/Source/AlphaTab/Audio/Synth/Sf2/Zone.cs deleted file mode 100644 index 59ba6bfb9..000000000 --- a/Source/AlphaTab/Audio/Synth/Sf2/Zone.cs +++ /dev/null @@ -1,25 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Audio.Synth.Sf2 -{ - class Zone - { - public Modulator[] Modulators { get; set; } - public Generator[] Generators { get; set; } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Synthesis/CCValue.cs b/Source/AlphaTab/Audio/Synth/Synthesis/CCValue.cs deleted file mode 100644 index 3341daa7a..000000000 --- a/Source/AlphaTab/Audio/Synth/Synthesis/CCValue.cs +++ /dev/null @@ -1,70 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -namespace AlphaTab.Audio.Synth.Synthesis -{ - - class CCValue - { - private byte _coarseValue; - private byte _fineValue; - private short _combined; - - public byte Coarse - { - get { return _coarseValue; } - set { _coarseValue = value; UpdateCombined(); } - } - public byte Fine - { - get { return _fineValue; } - set { _fineValue = value; UpdateCombined(); } - } - public short Combined - { - get { return _combined; } - set { _combined = value; UpdateCoarseFinePair(); } - } - - public CCValue(byte coarse, byte fine) - { - _coarseValue = coarse; - _fineValue = fine; - _combined = 0; - UpdateCombined(); - } - - public CCValue(short combined) - { - _coarseValue = 0; - _fineValue = 0; - _combined = combined; - UpdateCoarseFinePair(); - } - - private void UpdateCombined() - { - _combined = (short)((_coarseValue << 7) | _fineValue); - } - private void UpdateCoarseFinePair() - { - _coarseValue = (byte)(_combined >> 7); - _fineValue = (byte)(_combined & 0x7F); - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Audio/Synth/Synthesis/PlaybackRange.cs b/Source/AlphaTab/Audio/Synth/Synthesis/PlaybackRange.cs deleted file mode 100644 index de00de2bb..000000000 --- a/Source/AlphaTab/Audio/Synth/Synthesis/PlaybackRange.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace AlphaTab.Audio.Synth.Synthesis -{ - /// - /// Represents a range of the song that should be played. - /// - public class PlaybackRange - { - /// - /// The position in midi ticks from where the song should start. - /// - public int StartTick { get; set; } - - /// - /// The position in midi ticks to where the song should be played. - /// - public int EndTick { get; set; } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Synthesis/SynthParameters.cs b/Source/AlphaTab/Audio/Synth/Synthesis/SynthParameters.cs deleted file mode 100644 index 7aa9cb6bc..000000000 --- a/Source/AlphaTab/Audio/Synth/Synthesis/SynthParameters.cs +++ /dev/null @@ -1,172 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.Audio.Synth.Bank.Components; -using AlphaTab.Audio.Synth.Util; - -namespace AlphaTab.Audio.Synth.Synthesis -{ - /// - /// Parameters for a single synth channel including its program, bank, and cc list. - /// - class SynthParameters - { - /// - /// program number - /// - public byte Program { get; set; } - /// - /// bank number - /// - public byte BankSelect { get; set; } - /// - /// channel pressure event - /// - public byte ChannelAfterTouch { get; set; } - /// - /// (vol) pan positions controlling both right and left output levels - /// - public CCValue Pan { get; set; } - /// - /// (vol) channel volume controller - /// - public CCValue Volume { get; set; } - /// - /// (vol) expression controller - /// - public CCValue Expression { get; set; } - /// - /// (pitch) mod wheel pitch modifier in partial cents ie. 22.3 - /// - public CCValue ModRange { get; set; } - /// - /// (pitch) pitch bend including both semitones and cents - /// - public CCValue PitchBend { get; set; } - /// - /// controls max and min pitch bend range semitones - /// - public byte PitchBendRangeCoarse { get; set; } - /// - /// controls max and min pitch bend range cents - /// - public byte PitchBendRangeFine { get; set; } - /// - /// (pitch) transposition in semitones - /// - public short MasterCoarseTune { get; set; } - /// - /// (pitch) transposition in cents - /// - public CCValue MasterFineTune { get; set; } - /// - /// hold pedal status (true) for active - /// - public bool HoldPedal { get; set; } - /// - /// legato pedal status (true) for active - /// - public bool LegatoPedal { get; set; } - /// - /// registered parameter number - /// - public CCValue Rpn { get; set; } - public Synthesizer Synth { get; set; } - - - //These are updated whenever a midi event that affects them is recieved. - - public float CurrentVolume { get; set; } - public int CurrentPitch { get; set; } //in cents - public int CurrentMod { get; set; } //in cents - public PanComponent CurrentPan { get; set; } - public float MixVolume { get; set; } - - public SynthParameters(Synthesizer synth) - { - Synth = synth; - - Pan = new CCValue(0); - Volume = new CCValue(0); - Expression = new CCValue(0); - ModRange = new CCValue(0); - PitchBend = new CCValue(0); - MasterFineTune = new CCValue(0); - Rpn = new CCValue(0); - - MixVolume = 1; - - CurrentPan = new PanComponent(); - - ResetControllers(); - } - /// - /// Resets all of the channel's controllers to initial first power on values. Not the same as CC-121. - /// - public void ResetControllers() - { - Program = 0; - BankSelect = 0; - ChannelAfterTouch = 0; //Reset Channel Pressure to 0 - Pan.Combined = 0x2000; - Volume.Fine = 0; Volume.Coarse = 100; //Reset Vol Positions back to 90/127 (GM spec) - Expression.Combined = 0x3FFF; //Reset Expression positions back to 127/127 - ModRange.Combined = 0; - PitchBend.Combined = 0x2000; - PitchBendRangeCoarse = 2; //Reset pitch wheel to +-2 semitones (GM spec) - PitchBendRangeFine = 0; - MasterCoarseTune = 0; - MasterFineTune.Combined = 0x2000; //Reset fine tune - HoldPedal = false; - LegatoPedal = false; - Rpn.Combined = 0x3FFF; //Reset rpn - UpdateCurrentPan(); - UpdateCurrentPitch(); - UpdateCurrentVolumeFromExpression(); - } - - public void UpdateCurrentPitch() - { - CurrentPitch = (int)(((PitchBend.Combined - 8192.0) / 8192.0) * ((100 * PitchBendRangeCoarse) + PitchBendRangeFine)); - } - - public void UpdateCurrentMod() - { - CurrentMod = (int)(SynthConstants.DefaultModDepth * (ModRange.Combined / 16383.0)); - } - - public void UpdateCurrentPan() - { - double value = SynthConstants.HalfPi * (Pan.Combined / 16383.0); - CurrentPan.Left = (float)Math.Cos(value); - CurrentPan.Right = (float)Math.Sin(value); - } - - public void UpdateCurrentVolumeFromVolume() - { - CurrentVolume = Volume.Combined / 16383f; - CurrentVolume *= CurrentVolume; - } - - public void UpdateCurrentVolumeFromExpression() - { - CurrentVolume = Expression.Combined / 16383f; - CurrentVolume *= CurrentVolume; - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Synthesis/Synthesizer.cs b/Source/AlphaTab/Audio/Synth/Synthesis/Synthesizer.cs deleted file mode 100644 index 2f91521a1..000000000 --- a/Source/AlphaTab/Audio/Synth/Synthesis/Synthesizer.cs +++ /dev/null @@ -1,668 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.Audio.Synth.Bank; -using AlphaTab.Audio.Synth.Bank.Patch; -using AlphaTab.Audio.Synth.Ds; -using AlphaTab.Audio.Synth.Midi; -using AlphaTab.Audio.Synth.Midi.Event; -using AlphaTab.Audio.Synth.Util; -using AlphaTab.Collections; -using AlphaTab.Util; - -namespace AlphaTab.Audio.Synth.Synthesis -{ - class SynthEvent - { - public int EventIndex { get; set; } - public MidiEvent Event { get; set; } - public bool IsMetronome { get; set; } - public double Time { get; set; } - - public SynthEvent(int eventIndex, MidiEvent e) - { - EventIndex = eventIndex; - Event = e; - } - - public static SynthEvent NewMetronomeEvent(int eventIndex, int metronomeLength) - { - var x = new SynthEvent(eventIndex, null); - x.IsMetronome = true; - return x; - } - } - - class Synthesizer - { - private readonly VoiceManager _voiceManager; - private readonly SynthParameters[] _synthChannels; - - private readonly Patch[] _layerList; - private readonly LinkedList _midiEventQueue; - private readonly int[] _midiEventCounts; - - private int _metronomeChannel; - private FastDictionary _mutedChannels; - private FastDictionary _soloChannels; - private bool _isAnySolo; - - - public int ActiveVoices - { - get { return _voiceManager.ActiveVoices.Length; } - } - - public int FreeVoices - { - get { return _voiceManager.FreeVoices.Length; } - } - - - /// - /// The size of the individual sub buffers in samples - /// - public int MicroBufferSize { get; private set; } - - /// - /// The number of sub buffers - /// - public int MicroBufferCount { get; private set; } - - /// - /// Gets or sets the overall buffer of samples consisting of multiple microbuffers. - /// - public SampleArray SampleBuffer { get; set; } - - /// - /// The patch bank that holds all of the currently loaded instrument patches - /// - public PatchBank SoundBank { get; private set; } - - /// - /// The number of samples per second produced per channel - /// - public int SampleRate { get; private set; } - - /// - /// The master volume - /// - public float MasterVolume { get; set; } - - - /// - /// The metronome volume - /// - public float MetronomeVolume - { - get { return _synthChannels[_metronomeChannel].MixVolume; } - set { _synthChannels[_metronomeChannel].MixVolume = value; } - } - - public Synthesizer(int sampleRate, int audioChannels, int bufferSize, int bufferCount, int polyphony) - { - var MinSampleRate = 8000; - var MaxSampleRate = 96000; - // - // Setup synth parameters - MasterVolume = 1; - - SampleRate = SynthHelper.ClampI(sampleRate, MinSampleRate, MaxSampleRate); - MicroBufferSize = SynthHelper.ClampI(bufferSize, (int)(SynthConstants.MinBufferSize * sampleRate), (int)(SynthConstants.MaxBufferSize * sampleRate)); - MicroBufferSize = (int)(Math.Ceiling(MicroBufferSize / (double)SynthConstants.DefaultBlockSize) * SynthConstants.DefaultBlockSize); //ensure multiple of block size - MicroBufferCount = (Math.Max(1, bufferCount)); - SampleBuffer = new SampleArray((MicroBufferSize * MicroBufferCount * audioChannels)); - - // Setup Controllers - _synthChannels = new SynthParameters[SynthConstants.DefaultChannelCount]; - for (int x = 0; x < _synthChannels.Length; x++) - { - _synthChannels[x] = new SynthParameters(this); - } - - // setup metronome channel - _metronomeChannel = _synthChannels.Length - 1; - - // Create synth voices - _voiceManager = new VoiceManager(SynthHelper.ClampI(polyphony, SynthConstants.MinPolyphony, SynthConstants.MaxPolyphony)); - - // Create midi containers - _midiEventQueue = new LinkedList(); - _midiEventCounts = new int[MicroBufferCount]; - _layerList = new Patch[15]; - - _mutedChannels = new FastDictionary(); - _soloChannels = new FastDictionary(); - - ResetSynthControls(); - } - - public void LoadBank(PatchBank bank) - { - UnloadBank(); - SoundBank = bank; - } - - public void UnloadBank() - { - if (SoundBank != null) - { - NoteOffAll(true); - _voiceManager.UnloadPatches(); - SoundBank = null; - } - } - - public void ResetSynthControls() - { - foreach (SynthParameters parameters in _synthChannels) - { - parameters.ResetControllers(); - } - _synthChannels[MidiHelper.DrumChannel].BankSelect = PatchBank.DrumBank; - ReleaseAllHoldPedals(); - - _synthChannels[_metronomeChannel].Volume.Coarse = 128; - _synthChannels[_metronomeChannel].UpdateCurrentVolumeFromVolume(); - _synthChannels[_metronomeChannel].BankSelect = PatchBank.DrumBank; - //_synthChannels[_metronomeChannel].MixVolume = 0; - } - - public void ResetPrograms() - { - foreach (SynthParameters parameters in _synthChannels) - { - parameters.Program = 0; - } - } - - public void Synthesize() - { - SampleBuffer.Clear(); - FillWorkingBuffer(false); - } - - public void SynthesizeSilent() - { - SampleBuffer.Clear(); - FillWorkingBuffer(true); - } - - private void FillWorkingBuffer(bool silent) - { - /*Break the process loop into sections representing the smallest timeframe before the midi controls need to be updated - the bigger the timeframe the more efficent the process is, but playback quality will be reduced.*/ - var sampleIndex = 0; - var anySolo = _isAnySolo; - for (int x = 0; x < MicroBufferCount; x++) - { - if (_midiEventQueue.Length > 0) - { - for (int i = 0; i < _midiEventCounts[x]; i++) - { - var m = _midiEventQueue.RemoveLast(); - if (m.IsMetronome) - { - NoteOff(_metronomeChannel, 37); - NoteOn(_metronomeChannel, 37, 95); - } - else - { - ProcessMidiMessage(m.Event); - } - } - } - //voice processing loop - var node = _voiceManager.ActiveVoices.First; //node used to traverse the active voices - while (node != null) - { - var channel = node.Value.VoiceParams.Channel; - // channel is muted if it is either explicitley muted, or another channel is set to solo but not this one. - var isChannelMuted = _mutedChannels.ContainsKey(channel) || - (anySolo && !_soloChannels.ContainsKey(channel)); - - if (silent) - { - node.Value.ProcessSilent(sampleIndex, sampleIndex + MicroBufferSize * 2); - } - else - { - node.Value.Process(sampleIndex, sampleIndex + MicroBufferSize * 2, isChannelMuted); - } - //if an active voice has stopped remove it from the list - if (node.Value.VoiceParams.State == VoiceStateEnum.Stopped) - { - var delnode = node; //node used to remove inactive voices - node = node.Next; - _voiceManager.RemoveVoiceFromRegistry(delnode.Value); - _voiceManager.ActiveVoices.Remove(delnode); - _voiceManager.FreeVoices.AddFirst(delnode.Value); - } - else - { - node = node.Next; - } - } - sampleIndex += MicroBufferSize * SynthConstants.AudioChannels; - } - Platform.Platform.ClearIntArray(_midiEventCounts); - } - - #region Midi Handling - - public void NoteOn(int channel, int note, int velocity) - { - // Get the correct instrument depending if it is a drum or not - var sChan = _synthChannels[channel]; - Patch inst = SoundBank.GetPatchByNumber(sChan.BankSelect, sChan.Program); - if (inst == null) - return; - // A NoteOn can trigger multiple voices via layers - int layerCount; - if (inst is MultiPatch) - { - layerCount = ((MultiPatch)inst).FindPatches(channel, note, velocity, _layerList); - } - else - { - layerCount = 1; - _layerList[0] = inst; - } - - // If a key with the same note value exists, stop it - if (_voiceManager.Registry[channel][note] != null) - { - var node = _voiceManager.Registry[channel][note]; - while (node != null) - { - node.Value.Stop(); - node = node.Next; - } - _voiceManager.RemoveFromRegistry(channel, note); - } - // Check exclusive groups - for (var x = 0; x < layerCount; x++) - { - bool notseen = true; - for (int i = x - 1; i >= 0; i--) - { - if (_layerList[x].ExclusiveGroupTarget == _layerList[i].ExclusiveGroupTarget) - { - notseen = false; - break; - } - } - if (_layerList[x].ExclusiveGroupTarget != 0 && notseen) - { - var node = _voiceManager.ActiveVoices.First; - while (node != null) - { - if (_layerList[x].ExclusiveGroupTarget == node.Value.Patch.ExclusiveGroup) - { - node.Value.Stop(); - _voiceManager.RemoveVoiceFromRegistry(node.Value); - } - node = node.Next; - } - } - } - // Assign a voice to each layer - for (int x = 0; x < layerCount; x++) - { - Voice voice = _voiceManager.GetFreeVoice(); - if (voice == null)// out of voices and skipping is enabled - break; - voice.Configure(channel, note, velocity, _layerList[x], _synthChannels[channel]); - _voiceManager.AddToRegistry(voice); - _voiceManager.ActiveVoices.AddLast(voice); - voice.Start(); - } - // Clear layer list - for (int x = 0; x < layerCount; x++) - _layerList[x] = null; - - } - - public void NoteOff(int channel, int note) - { - if (_synthChannels[channel].HoldPedal) - { - var node = _voiceManager.Registry[channel][note]; - while (node != null) - { - node.Value.VoiceParams.NoteOffPending = true; - node = node.Next; - } - } - else - { - var node = _voiceManager.Registry[channel][note]; - while (node != null) - { - node.Value.Stop(); - node = node.Next; - } - _voiceManager.RemoveFromRegistry(channel, note); - } - } - - public void NoteOffAll(bool immediate) - { - var node = _voiceManager.ActiveVoices.First; - if (immediate) - {//if immediate ignore hold pedals and clear the entire registry - _voiceManager.ClearRegistry(); - while (node != null) - { - node.Value.StopImmediately(); - var delnode = node; - node = node.Next; - _voiceManager.ActiveVoices.Remove(delnode); - _voiceManager.FreeVoices.AddFirst(delnode.Value); - } - } - else - {//otherwise we have to check for hold pedals and double check the registry before removing the voice - while (node != null) - { - VoiceParameters voiceParams = node.Value.VoiceParams; - if (voiceParams.State == VoiceStateEnum.Playing) - { - //if hold pedal is enabled do not stop the voice - if (_synthChannels[voiceParams.Channel].HoldPedal) - { - voiceParams.NoteOffPending = true; - } - else - { - node.Value.Stop(); - _voiceManager.RemoveVoiceFromRegistry(node.Value); - } - } - node = node.Next; - } - } - } - - public void NoteOffAllChannel(int channel, bool immediate) - { - var node = _voiceManager.ActiveVoices.First; - while (node != null) - { - if (channel == node.Value.VoiceParams.Channel) - { - if (immediate) - { - node.Value.StopImmediately(); - var delnode = node; - node = node.Next; - _voiceManager.ActiveVoices.Remove(delnode); - _voiceManager.FreeVoices.AddFirst(delnode.Value); - } - else - { - //if hold pedal is enabled do not stop the voice - if (_synthChannels[channel].HoldPedal) - node.Value.VoiceParams.NoteOffPending = true; - else - node.Value.Stop(); - node = node.Next; - } - } - } - } - - public void ProcessMidiMessage(MidiEvent e) - { - Logger.Debug("Midi", "Processing midi " + e.Command); - var command = e.Command; - var channel = e.Channel; - var data1 = e.Data1; - var data2 = e.Data2; - switch (command) - { - case MidiEventType.NoteOff: - NoteOff(channel, data1); - break; - case MidiEventType.NoteOn: - if (data2 == 0) - NoteOff(channel, data1); - else - NoteOn(channel, data1, data2); - break; - case MidiEventType.NoteAftertouch: - //synth uses channel after touch instead - break; - case MidiEventType.Controller: - switch ((ControllerType)data1) - { - case ControllerType.BankSelectCoarse: //Bank select coarse - if (channel == MidiHelper.DrumChannel) - data2 += PatchBank.DrumBank; - if (SoundBank.IsBankLoaded(data2)) - _synthChannels[channel].BankSelect = (byte)data2; - else - _synthChannels[channel].BankSelect = (byte)((channel == MidiHelper.DrumChannel) ? PatchBank.DrumBank : 0); - break; - case ControllerType.ModulationCoarse: //Modulation wheel coarse - _synthChannels[channel].ModRange.Coarse = (byte)data2; - _synthChannels[channel].UpdateCurrentMod(); - break; - case ControllerType.ModulationFine: //Modulation wheel fine - _synthChannels[channel].ModRange.Fine = (byte)data2; - _synthChannels[channel].UpdateCurrentMod(); - break; - case ControllerType.VolumeCoarse: //Channel volume coarse - _synthChannels[channel].Volume.Coarse = (byte)data2; - _synthChannels[channel].UpdateCurrentVolumeFromVolume(); - break; - case ControllerType.VolumeFine: //Channel volume fine - _synthChannels[channel].Volume.Fine = (byte)data2; - _synthChannels[channel].UpdateCurrentVolumeFromVolume(); - break; - case ControllerType.PanCoarse: //Pan coarse - _synthChannels[channel].Pan.Coarse = (byte)data2; - _synthChannels[channel].UpdateCurrentPan(); - break; - case ControllerType.PanFine: //Pan fine - _synthChannels[channel].Pan.Fine = (byte)data2; - _synthChannels[channel].UpdateCurrentPan(); - break; - case ControllerType.ExpressionControllerCoarse: //Expression coarse - _synthChannels[channel].Expression.Coarse = (byte)data2; - _synthChannels[channel].UpdateCurrentVolumeFromExpression(); - break; - case ControllerType.ExpressionControllerFine: //Expression fine - _synthChannels[channel].Expression.Fine = (byte)data2; - _synthChannels[channel].UpdateCurrentVolumeFromExpression(); - break; - case ControllerType.HoldPedal: //Hold pedal - if (_synthChannels[channel].HoldPedal && !(data2 > 63)) //if hold pedal is released stop any voices with pending release tags - ReleaseHoldPedal(channel); - _synthChannels[channel].HoldPedal = data2 > 63; - break; - case ControllerType.LegatoPedal: //Legato Pedal - _synthChannels[channel].LegatoPedal = data2 > 63; - break; - case ControllerType.NonRegisteredParameterCourse: //NRPN Coarse Select //fix for invalid DataEntry after unsupported NRPN events - _synthChannels[channel].Rpn.Combined = 0x3FFF; //todo implement NRPN - break; - case ControllerType.NonRegisteredParameterFine: //NRPN Fine Select //fix for invalid DataEntry after unsupported NRPN events - _synthChannels[channel].Rpn.Combined = 0x3FFF; //todo implement NRPN - break; - case ControllerType.RegisteredParameterCourse: //RPN Coarse Select - _synthChannels[channel].Rpn.Coarse = (byte)data2; - break; - case ControllerType.RegisteredParameterFine: //RPN Fine Select - _synthChannels[channel].Rpn.Fine = (byte)data2; - break; - case ControllerType.AllNotesOff: //Note Off All - NoteOffAll(false); - break; - case ControllerType.DataEntryCoarse: //DataEntry Coarse - switch (_synthChannels[channel].Rpn.Combined) - { - case 0: //change semitone, pitchwheel - _synthChannels[channel].PitchBendRangeCoarse = (byte)data2; - _synthChannels[channel].UpdateCurrentPitch(); - break; - case 1: //master fine tune coarse - _synthChannels[channel].MasterFineTune.Coarse = (byte)data2; - break; - case 2: //master coarse tune coarse - _synthChannels[channel].MasterCoarseTune = (short)(data2 - 64); - break; - } - break; - case ControllerType.DataEntryFine: //DataEntry Fine - switch (_synthChannels[channel].Rpn.Combined) - { - case 0: //change cents, pitchwheel - _synthChannels[channel].PitchBendRangeFine = (byte)data2; - _synthChannels[channel].UpdateCurrentPitch(); - break; - case 1: //master fine tune fine - _synthChannels[channel].MasterFineTune.Fine = (byte)data2; - break; - } - break; - case ControllerType.ResetControllers: //Reset All - _synthChannels[channel].Expression.Combined = 0x3FFF; - _synthChannels[channel].ModRange.Combined = 0; - if (_synthChannels[channel].HoldPedal) - ReleaseHoldPedal(channel); - _synthChannels[channel].HoldPedal = false; - _synthChannels[channel].LegatoPedal = false; - _synthChannels[channel].Rpn.Combined = 0x3FFF; - _synthChannels[channel].PitchBend.Combined = 0x2000; - _synthChannels[channel].ChannelAfterTouch = 0; - _synthChannels[channel].UpdateCurrentPitch(); //because pitchBend was reset - _synthChannels[channel].UpdateCurrentVolumeFromExpression(); //because expression was reset - break; - default: - return; - } - break; - case MidiEventType.ProgramChange: //Program Change - _synthChannels[channel].Program = (byte)data1; - break; - case MidiEventType.ChannelAftertouch: //Channel Aftertouch - _synthChannels[channel].ChannelAfterTouch = (byte)data2; - break; - case MidiEventType.PitchBend: //Pitch Bend - _synthChannels[channel].PitchBend.Coarse = (byte)data2; - _synthChannels[channel].PitchBend.Fine = (byte)data1; - _synthChannels[channel].UpdateCurrentPitch(); - break; - } - OnMidiEventProcessed(e); - } - - public event Action MidiEventProcessed; - private void OnMidiEventProcessed(MidiEvent e) - { - var handler = MidiEventProcessed; - if (handler != null) - { - handler(e); - } - } - - private void ReleaseAllHoldPedals() - { - LinkedListNode node = _voiceManager.ActiveVoices.First; - while (node != null) - { - if (node.Value.VoiceParams.NoteOffPending) - { - node.Value.Stop(); - _voiceManager.RemoveVoiceFromRegistry(node.Value); - } - node = node.Next; - } - } - - private void ReleaseHoldPedal(int channel) - { - LinkedListNode node = _voiceManager.ActiveVoices.First; - while (node != null) - { - if (node.Value.VoiceParams.Channel == channel && node.Value.VoiceParams.NoteOffPending) - { - node.Value.Stop(); - _voiceManager.RemoveVoiceFromRegistry(node.Value); - } - node = node.Next; - } - } - - - #endregion - - public void DispatchEvent(int i, SynthEvent synthEvent) - { - _midiEventQueue.AddFirst(synthEvent); - _midiEventCounts[i]++; - } - - public void SetChannelMute(int channel, bool mute) - { - if (mute) - { - _mutedChannels[channel] = true; - } - else - { - _mutedChannels.Remove(channel); - } - } - - public void ResetChannelStates() - { - _mutedChannels = new FastDictionary(); - _soloChannels = new FastDictionary(); - _isAnySolo = false; - } - - public void SetChannelSolo(int channel, bool solo) - { - if (solo) - { - _soloChannels[channel] = true; - } - else - { - _soloChannels.Remove(channel); - } - - _isAnySolo = _soloChannels.Count > 0; - } - - public void SetChannelProgram(int channel, byte program) - { - if (channel < 0 || channel >= _synthChannels.Length) return; - _synthChannels[channel].Program = (byte)program; - } - - public void SetChannelVolume(int channel, double volume) - { - if (channel < 0 || channel >= _synthChannels.Length) return; - _synthChannels[channel].MixVolume = (float)volume; - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Synthesis/Voice.cs b/Source/AlphaTab/Audio/Synth/Synthesis/Voice.cs deleted file mode 100644 index 2bef240cf..000000000 --- a/Source/AlphaTab/Audio/Synth/Synthesis/Voice.cs +++ /dev/null @@ -1,91 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Audio.Synth.Bank.Patch; - -namespace AlphaTab.Audio.Synth.Synthesis -{ - enum VoiceStateEnum - { - Stopped = 0, - Stopping = 1, - Playing = 2 - } - - class Voice - { - public Patch Patch { get; private set; } - public VoiceParameters VoiceParams { get; private set; } - - public Voice() - { - VoiceParams = new VoiceParameters(); - } - - public void Start() - { - if (VoiceParams.State != VoiceStateEnum.Stopped) - return; - if (Patch.Start(VoiceParams)) - VoiceParams.State = VoiceStateEnum.Playing; - } - - public void Stop() - { - if (VoiceParams.State != VoiceStateEnum.Playing) - return; - VoiceParams.State = VoiceStateEnum.Stopping; - Patch.Stop(VoiceParams); - } - - public void StopImmediately() - { - VoiceParams.State = VoiceStateEnum.Stopped; - } - - public void Process(int startIndex, int endIndex, bool isMuted) - { - //do not process if the voice is stopped - if (VoiceParams.State == VoiceStateEnum.Stopped) - return; - - //process using the patch's algorithm - Patch.Process(VoiceParams, startIndex, endIndex, isMuted, false); - } - - public void ProcessSilent(int startIndex, int endIndex) - { - //do not process if the voice is stopped - if (VoiceParams.State == VoiceStateEnum.Stopped) - return; - - //process using the patch's algorithm - Patch.Process(VoiceParams, startIndex, endIndex, true, true); - } - - public void Configure(int channel, int note, int velocity, Patch patch, SynthParameters synthParams) - { - VoiceParams.Reset(); - VoiceParams.Channel = channel; - VoiceParams.Note = note; - VoiceParams.Velocity = velocity; - VoiceParams.SynthParams = synthParams; - Patch = patch; - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Synthesis/VoiceManager.cs b/Source/AlphaTab/Audio/Synth/Synthesis/VoiceManager.cs deleted file mode 100644 index 7e91a10e2..000000000 --- a/Source/AlphaTab/Audio/Synth/Synthesis/VoiceManager.cs +++ /dev/null @@ -1,181 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Audio.Synth.Ds; -using AlphaTab.Audio.Synth.Util; - -namespace AlphaTab.Audio.Synth.Synthesis -{ - class VoiceNode - { - public Voice Value { get; set; } - public VoiceNode Next { get; set; } - } - - class VoiceManager - { - private Voice[] _voicePool; - private LinkedList _vNodes; - - public int Polyphony { get; set; } - public LinkedList FreeVoices { get; set; } - public LinkedList ActiveVoices { get; set; } - public VoiceNode[][] Registry { get; set; } - - public VoiceManager(int voiceCount) - { - Polyphony = voiceCount; - - _voicePool = new Voice[voiceCount]; - _vNodes = new LinkedList(); - FreeVoices = new LinkedList(); - ActiveVoices = new LinkedList(); - - for (int i = 0; i < voiceCount; i++) - { - var v = new Voice(); - _voicePool[i] = v; - _vNodes.AddLast(new VoiceNode()); - FreeVoices.AddLast(v); - } - - Registry = new VoiceNode[SynthConstants.DefaultChannelCount][]; - for (int i = 0; i < Registry.Length; i++) - { - Registry[i] = new VoiceNode[SynthConstants.DefaultKeyCount]; - } - } - - public Voice GetFreeVoice() - { - if (FreeVoices.Length > 0) - { - var voice = FreeVoices.First.Value; - FreeVoices.RemoveFirst(); - return voice; - } - - return StealQuietestVoice(); - } - - public void AddToRegistry(Voice voice) - { - var node = _vNodes.RemoveLast(); - node.Value = voice; - node.Next = Registry[voice.VoiceParams.Channel][voice.VoiceParams.Note]; - Registry[voice.VoiceParams.Channel][voice.VoiceParams.Note] = node; - } - - public void RemoveFromRegistry(int channel, int note) - { - var node = Registry[channel][note]; - while (node != null) - { - _vNodes.AddLast(node); - node = node.Next; - } - Registry[channel][note] = null; - } - - public void RemoveVoiceFromRegistry(Voice voice) - { - var node = Registry[voice.VoiceParams.Channel][voice.VoiceParams.Note]; - if (node == null) - return; - if (node.Value == voice) - { - Registry[voice.VoiceParams.Channel][voice.VoiceParams.Note] = node.Next; - _vNodes.AddLast(node); - } - else - { - var node2 = node; - node = node.Next; - while (node != null) - { - if (node.Value == voice) - { - node2.Next = node.Next; - _vNodes.AddLast(node); - return; - } - node2 = node; - node = node.Next; - } - } - } - - public void ClearRegistry() - { - var node = ActiveVoices.First; - while (node != null) - { - var vnode = Registry[node.Value.VoiceParams.Channel][node.Value.VoiceParams.Note]; - while (vnode != null) - { - _vNodes.AddLast(vnode); - vnode = vnode.Next; - } - Registry[node.Value.VoiceParams.Channel][node.Value.VoiceParams.Note] = null; - node = node.Next; - } - } - - public void UnloadPatches() - { - foreach (Voice v in _voicePool) - { - v.Configure(0, 0, 0, null, null); - var current = _vNodes.First; - while (current != null) - { - current.Value.Value = null; - current = current.Next; - } - } - } - - private Voice StealQuietestVoice() - { - var voiceVolume = 1000.0; - LinkedListNode quietest = null; - var node = ActiveVoices.First; - while (node != null) - { - if (node.Value.VoiceParams.State != VoiceStateEnum.Playing) - { - float volume = node.Value.VoiceParams.CombinedVolume; - if (volume < voiceVolume) - { - quietest = node; - voiceVolume = volume; - } - } - node = node.Next; - } - if (quietest == null) - quietest = ActiveVoices.First; - //check and remove from registry - RemoveVoiceFromRegistry(quietest.Value); - ActiveVoices.Remove(quietest); - //stop voice if it is not already - quietest.Value.VoiceParams.State = VoiceStateEnum.Stopped; - return quietest.Value; - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Synthesis/VoiceParameters.cs b/Source/AlphaTab/Audio/Synth/Synthesis/VoiceParameters.cs deleted file mode 100644 index 30d4626d1..000000000 --- a/Source/AlphaTab/Audio/Synth/Synthesis/VoiceParameters.cs +++ /dev/null @@ -1,128 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Audio.Synth.Bank.Components; -using AlphaTab.Audio.Synth.Bank.Components.Generators; -using AlphaTab.Audio.Synth.Ds; -using AlphaTab.Audio.Synth.Util; -using AlphaTab.Utils; - -namespace AlphaTab.Audio.Synth.Synthesis -{ - class VoiceParameters - { - private float mix1; - private float mix2; - - public int Channel { get; set; } - public int Note { get; set; } - public int Velocity { get; set; } - public bool NoteOffPending { get; set; } - public VoiceStateEnum State { get; set; } - public int PitchOffset { get; set; } - public float VolOffset { get; set; } - public SampleArray BlockBuffer { get; set; } - - public UnionData[] PData { get; set; } - public SynthParameters SynthParams { get; set; } - public GeneratorParameters[] GeneratorParams { get; set; } - public Envelope[] Envelopes { get; set; } - public Filter[] Filters { get; set; } - public Lfo[] Lfos { get; set; } - - public float CombinedVolume - { - get { return mix1 + mix2; } - } - - public VoiceParameters() - { - BlockBuffer = new SampleArray(SynthConstants.DefaultBlockSize); - //create default number of each component - PData = new UnionData[SynthConstants.MaxVoiceComponents]; - GeneratorParams = new GeneratorParameters[SynthConstants.MaxVoiceComponents]; - Envelopes = new Envelope[SynthConstants.MaxVoiceComponents]; - Filters = new Filter[SynthConstants.MaxVoiceComponents]; - Lfos = new Lfo[SynthConstants.MaxVoiceComponents]; - //initialize each component - for (int x = 0; x < SynthConstants.MaxVoiceComponents; x++) - { - GeneratorParams[x] = new GeneratorParameters(); - Envelopes[x] = new Envelope(); - Filters[x] = new Filter(); - Lfos[x] = new Lfo(); - } - } - - public void Reset() - { - NoteOffPending = false; - PitchOffset = 0; - VolOffset = 0; - for (int i = 0; i < PData.Length; i++) - { - PData[i] = new UnionData(); - } - mix1 = 0; - mix2 = 0; - } - - public void MixMonoToMonoInterp(int startIndex, float volume) - { - float inc = (volume - mix1) / SynthConstants.DefaultBlockSize; - for (int i = 0; i < BlockBuffer.Length; i++) - { - mix1 += inc; - SynthParams.Synth.SampleBuffer[startIndex + i] += BlockBuffer[i] * mix1; - } - mix1 = volume; - } - - public void MixMonoToStereoInterp(int startIndex, float leftVol, float rightVol) - { - float inc_l = (leftVol - mix1) / SynthConstants.DefaultBlockSize; - float inc_r = (rightVol - mix2) / SynthConstants.DefaultBlockSize; - for (int i = 0; i < BlockBuffer.Length; i++) - { - mix1 += inc_l; - mix2 += inc_r; - SynthParams.Synth.SampleBuffer[startIndex] += BlockBuffer[i] * mix1; - SynthParams.Synth.SampleBuffer[startIndex + 1] += BlockBuffer[i] * mix2; - startIndex += 2; - } - mix1 = leftVol; - mix2 = rightVol; - } - - public void MixStereoToStereoInterp(int startIndex, float leftVol, float rightVol) - { - float inc_l = (leftVol - mix1) / SynthConstants.DefaultBlockSize; - float inc_r = (rightVol - mix2) / SynthConstants.DefaultBlockSize; - for (int i = 0; i < BlockBuffer.Length; i++) - { - mix1 += inc_l; - mix2 += inc_r; - SynthParams.Synth.SampleBuffer[startIndex + i] += BlockBuffer[i] * mix1; - i++; - SynthParams.Synth.SampleBuffer[startIndex + i] += BlockBuffer[i] * mix2; - } - mix1 = leftVol; - mix2 = rightVol; - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Util/SynthConstants.cs b/Source/AlphaTab/Audio/Synth/Util/SynthConstants.cs deleted file mode 100644 index f8f96e09b..000000000 --- a/Source/AlphaTab/Audio/Synth/Util/SynthConstants.cs +++ /dev/null @@ -1,58 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Audio.Synth.Bank.Components.Generators; - -namespace AlphaTab.Audio.Synth.Util -{ - static class SynthConstants - { - public const int AudioChannels = 2; - - public const double Pi = 3.14159265358979; - public const double TwoPi = 2.0*Pi; - public const double HalfPi = Pi / 2.0; - public const double InverseSqrtOfTwo = 0.707106781186; - public const float DefaultLfoFrequency = 8.0f; - public const int DefaultModDepth = 100; - public const int DefaultPolyphony = 40; - public const int MinPolyphony = 5; - public const int MaxPolyphony = 250; - public const int DefaultBlockSize = 64; - public const double MaxBufferSize = 0.05; - public const double MinBufferSize = 0.001; - public const double DenormLimit = 1e-38; - public const double NonAudible = 1e-5; - public const int SincWidth = 16; - public const int SincResolution = 64; - public const int MaxVoiceComponents = 4; - public const int DefaultChannelCount = 16 + 1 /*metronome*/; - public const int DefaultKeyCount = 128; - - public const float DefaultMixGain = 0.35f; - - public const float MinVolume = 0; - public const float MaxVolume = 10; - - public const byte MinProgram = 0; - public const byte MaxProgram = 127; - - public const double MinPlaybackSpeed = 0.125; - public const double MaxPlaybackSpeed = 8; - } -} diff --git a/Source/AlphaTab/Audio/Synth/Util/SynthHelper.cs b/Source/AlphaTab/Audio/Synth/Util/SynthHelper.cs deleted file mode 100644 index 1b95cad0a..000000000 --- a/Source/AlphaTab/Audio/Synth/Util/SynthHelper.cs +++ /dev/null @@ -1,133 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; - -namespace AlphaTab.Audio.Synth.Util -{ - class SynthHelper - { - public static void SwapEndianess(byte[] data, int bits) - { - bits /= 8; //get bytes per sample - var swapArray = new byte[bits]; - for (int x = 0; x < data.Length; x += bits) - { - Platform.Platform.BlockCopy(data, x, swapArray, 0, bits); - Platform.Platform.Reverse(swapArray); - Platform.Platform.BlockCopy(swapArray, 0, data, x, bits); - } - } - - public static byte ClampB(byte value, byte min, byte max) - { - if (value <= min) - return min; - else if (value >= max) - return max; - else - return value; - } - public static double ClampD(double value, double min, double max) - { - if (value <= min) - return min; - else if (value >= max) - return max; - else - return value; - } - public static float ClampF(float value, float min, float max) - { - if (value <= min) - return min; - else if (value >= max) - return max; - else - return value; - } - public static int ClampI(int value, int min, int max) - { - if (value <= min) - return min; - else if (value >= max) - return max; - else - return value; - } - public static short ClampS(short value, short min, short max) - { - if (value <= min) - return min; - else if (value >= max) - return max; - else - return value; - } - - public static double NearestPowerOfTwo(double value) - { - return Math.Pow(2, Math.Round(Math.Log(value, 2))); - } - public static double SamplesFromTime(int sampleRate, double seconds) - { - return sampleRate * seconds; - } - public static double TimeFromSamples(int sampleRate, int samples) - { - return samples / (double)sampleRate; - } - - public static double DBtoLinear(double dBvalue) - { - return Math.Pow(10.0, (dBvalue / 20.0)); - } - public static double LineartoDB(double linearvalue) - { - return 20.0 * Math.Log10(linearvalue); - } - - //Midi Note and Frequency Conversions - public static double FrequencyToKey(double frequency, int rootkey) - { - return 12.0 * Math.Log(frequency / 440.0, 2.0) + rootkey; - } - public static double KeyToFrequency(double key, int rootkey) - { - return Math.Pow(2.0, (key - rootkey) / 12.0) * 440.0; - } - - public static double SemitoneToPitch(int key) - {//does not return a frequency, only the 2^(1/12) value. - if (key < -127) - key = -127; - else if (key > 127) - key = 127; - return Tables.SemitoneTable(127 + key); - } - public static double CentsToPitch(int cents) - {//does not return a frequency, only the 2^(1/12) value. - int key = cents / 100; - cents -= key * 100; - if (key < -127) - key = -127; - else if (key > 127) - key = 127; - return Tables.SemitoneTable(127 + key) * Tables.CentTable(100 + cents); - } - } -} diff --git a/Source/AlphaTab/Audio/Synth/Util/Tables.cs b/Source/AlphaTab/Audio/Synth/Util/Tables.cs deleted file mode 100644 index 1fb629c01..000000000 --- a/Source/AlphaTab/Audio/Synth/Util/Tables.cs +++ /dev/null @@ -1,207 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.Audio.Synth.Ds; - -namespace AlphaTab.Audio.Synth.Util -{ - class Tables - { - private static bool _isInitialized; - - public static SampleArray[] _envelopeTables; - public static SampleArray _semitoneTable; - public static SampleArray _centTable; - public static SampleArray _sincTable; - - public static SampleArray EnvelopeTables(int index) - { - if (!_isInitialized) Init(); - return _envelopeTables[index]; - } - - public static float SemitoneTable(int index) - { - if (!_isInitialized) Init(); - return _semitoneTable[index]; - } - - public static float CentTable(int index) - { - if (!_isInitialized) Init(); - return _centTable[index]; - } - - public static float SincTable(int index) - { - if (!_isInitialized) Init(); - return _sincTable[index]; - } - - private static void Init() - { - var EnvelopeSize = 64; - var ExponentialCoeff = .09f; - _envelopeTables = new SampleArray[4]; - _envelopeTables[0] = (RemoveDenormals(CreateSustainTable(EnvelopeSize))); - _envelopeTables[1] = (RemoveDenormals(CreateLinearTable(EnvelopeSize))); - _envelopeTables[2] = (RemoveDenormals(CreateExponentialTable(EnvelopeSize, ExponentialCoeff))); - _envelopeTables[3] = (RemoveDenormals(CreateSineTable(EnvelopeSize))); - _centTable = CreateCentTable(); - _semitoneTable = CreateSemitoneTable(); - _sincTable = CreateSincTable(SynthConstants.SincWidth, SynthConstants.SincResolution, .43f, HammingWindow); - _isInitialized = true; - } - - private static SampleArray CreateSquareTable(int size, int k) - {//Uses Fourier Expansion up to k terms - var FourOverPi = 4 / Math.PI; - var squaretable = new SampleArray(size); - var inc = 1.0 / size; - var phase = 0.0; - for (int x = 0; x < size; x++) - { - var value = 0.0; - for (int i = 1; i < k + 1; i++) - { - var twokminus1 = (2 * i) - 1; - value += Math.Sin(SynthConstants.TwoPi * (twokminus1) * phase) / (twokminus1); - } - squaretable[x] = SynthHelper.ClampF((float)(FourOverPi * value), -1, 1); - phase += inc; - } - return squaretable; - } - - private static SampleArray CreateCentTable() - {//-100 to 100 cents - var cents = new SampleArray(201); - for (int x = 0; x < cents.Length; x++) - { - cents[x] = (float)Math.Pow(2.0, (x - 100.0) / 1200.0); - } - return cents; - } - - private static SampleArray CreateSemitoneTable() - {//-127 to 127 semitones - var table = new SampleArray(255); - for (int x = 0; x < table.Length; x++) - { - table[x] = (float)Math.Pow(2.0, (x - 127.0) / 12.0); - } - return table; - } - - private static SampleArray CreateSustainTable(int size) - { - var table = new SampleArray(size); - for (int x = 0; x < size; x++) - { - table[x] = 1; - } - return table; - } - - private static SampleArray CreateLinearTable(int size) - { - var table = new SampleArray(size); - for (int x = 0; x < size; x++) - { - table[x] = x / (float)(size - 1); - } - return table; - } - - private static SampleArray CreateExponentialTable(int size, float coeff) - { - coeff = SynthHelper.ClampF(coeff, .001f, .9f); - var graph = new SampleArray(size); - var val = 0.0; - for (int x = 0; x < size; x++) - { - graph[x] = (float)val; - val += coeff * ((1 / 0.63) - val); - } - for (int x = 0; x < size; x++) - { - graph[x] = graph[x] / graph[graph.Length - 1]; - } - return graph; - } - - private static SampleArray CreateSineTable(int size) - { - var graph = new SampleArray(size); - var inc = (float)(3.0 * Math.PI / 2.0) / (size - 1); - var phase = 0.0; - for (int x = 0; x < size; x++) - { - graph[x] = (float)Math.Abs(Math.Sin(phase)); - phase += inc; - } - return graph; - } - - private static SampleArray RemoveDenormals(SampleArray data) - { - for (int x = 0; x < data.Length; x++) - { - if (Math.Abs(data[x]) < SynthConstants.DenormLimit) - data[x] = 0; - } - return data; - } - - private static float VonHannWindow(float i, int size) - { - return (float)(0.5 - 0.5 * Math.Cos(SynthConstants.TwoPi * (0.5 + i / size))); - } - - private static float HammingWindow(float i, int size) - { - return (float)(0.54 - 0.46 * Math.Cos(SynthConstants.TwoPi * i / size)); - } - - private static float BlackmanWindow(float i, int size) - { - return (float)(0.42659 - 0.49656 * Math.Cos(SynthConstants.TwoPi * i / size) + 0.076849 * Math.Cos(4.0 * Math.PI * i / size)); - } - - private static SampleArray CreateSincTable(int windowSize, int resolution, float cornerRatio, Func windowFunction) - { - var subWindow = ((windowSize / 2) + 1); - var table = new SampleArray((subWindow * resolution)); - var gain = 2.0 * cornerRatio; - for (int x = 0; x < subWindow; x++) - { - for (int y = 0; y < resolution; y++) - { - var a = x + (y / (float)(resolution)); - var sinc = SynthConstants.TwoPi * cornerRatio * a; - if (Math.Abs(sinc) > 0.00001) - sinc = Math.Sin(sinc) / sinc; - else - sinc = 1.0; - table[x * SynthConstants.SincResolution + y] = (float)(gain * sinc * windowFunction(a, windowSize)); - } - } - return table; - } - } -} diff --git a/Source/AlphaTab/Environment.cs b/Source/AlphaTab/Environment.cs deleted file mode 100644 index c9bd9b687..000000000 --- a/Source/AlphaTab/Environment.cs +++ /dev/null @@ -1,218 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.Collections; -using AlphaTab.Model; -using AlphaTab.Platform; -using AlphaTab.Rendering; -using AlphaTab.Rendering.Effects; -using AlphaTab.Rendering.Layout; -using AlphaTab.Rendering.Utils; - -namespace AlphaTab -{ - /// - /// This public class represents the global alphaTab environment where - /// alphaTab looks for information like available layout engines - /// staves etc. - /// - partial class Environment - { - public static FastDictionary> RenderEngines; - public static FastDictionary> LayoutEngines; - public static FastDictionary StaveProfiles; - public const string StaveProfileScoreTab = "score-tab"; - public const string StaveProfileTab = "tab"; - public const string StaveProfileTabMixed = "tab-mixed"; - public const string StaveProfileScore = "score"; - - static Environment() - { - Init(); - } - - public static IScoreRenderer CreateScoreRenderer(Settings settings) - { - return new ScoreRenderer(settings); - } - - public static void Init() - { - RenderEngines = new FastDictionary>(); - LayoutEngines = new FastDictionary>(); - StaveProfiles = new FastDictionary(); - - PlatformInit(); - - // default layout engines - LayoutEngines["default"] = r => new PageViewLayout(r); - LayoutEngines["page"] = r => new PageViewLayout(r); - LayoutEngines["horizontal"] = r => new HorizontalScreenLayout(r); - - // default combinations of stave textprofiles - StaveProfiles["default"] = StaveProfiles[StaveProfileScoreTab] = new BarRendererFactory[] - { - new EffectBarRendererFactory("score-effects", new IEffectBarRendererInfo[] { - new TempoEffectInfo(), - new TripletFeelEffectInfo(), - new MarkerEffectInfo(), - new TextEffectInfo(), - new ChordsEffectInfo(), - new FermataEffectInfo(), - new WhammyBarEffectInfo(), - new TrillEffectInfo(), - new OttaviaEffectInfo(true), - new WideBeatVibratoEffectInfo(), - new SlightBeatVibratoEffectInfo(), - new WideNoteVibratoEffectInfo(), - new SlightNoteVibratoEffectInfo(), - new AlternateEndingsEffectInfo(), - }), - new ScoreBarRendererFactory(), - new EffectBarRendererFactory("tab-effects", new IEffectBarRendererInfo[] { - new CrescendoEffectInfo(), - new OttaviaEffectInfo(false), - new DynamicsEffectInfo(), - new LyricsEffectInfo(), - new TrillEffectInfo(), - new WideBeatVibratoEffectInfo(), - new SlightBeatVibratoEffectInfo(), - new WideNoteVibratoEffectInfo(), - new SlightNoteVibratoEffectInfo(), - new TapEffectInfo(), - new FadeInEffectInfo(), - new HarmonicsEffectInfo(HarmonicType.Natural), - new HarmonicsEffectInfo(HarmonicType.Artificial), - new HarmonicsEffectInfo(HarmonicType.Pinch), - new HarmonicsEffectInfo(HarmonicType.Tap), - new HarmonicsEffectInfo(HarmonicType.Semi), - new HarmonicsEffectInfo(HarmonicType.Feedback), - new LetRingEffectInfo(), - new CapoEffectInfo(), - new FingeringEffectInfo(), - new PalmMuteEffectInfo(), - new PickStrokeEffectInfo(), - new PickSlideEffectInfo() - }), - new TabBarRendererFactory(false, false, false) - }; - - StaveProfiles[StaveProfileScore] = new BarRendererFactory[] - { - new EffectBarRendererFactory("score-effects", new IEffectBarRendererInfo[] { - new TempoEffectInfo(), - new TripletFeelEffectInfo(), - new MarkerEffectInfo(), - new TextEffectInfo(), - new ChordsEffectInfo(), - new FermataEffectInfo(), - new WhammyBarEffectInfo(), - new TrillEffectInfo(), - new OttaviaEffectInfo(true), - new WideBeatVibratoEffectInfo(), - new SlightBeatVibratoEffectInfo(), - new WideNoteVibratoEffectInfo(), - new SlightNoteVibratoEffectInfo(), - new FadeInEffectInfo(), - new LetRingEffectInfo(), - new PalmMuteEffectInfo(), - new PickStrokeEffectInfo(), - new PickSlideEffectInfo(), - new AlternateEndingsEffectInfo(), - }), - new ScoreBarRendererFactory(), - new EffectBarRendererFactory("score-bottom-effects", new IEffectBarRendererInfo[] { - new CrescendoEffectInfo(), - new OttaviaEffectInfo(false), - new DynamicsEffectInfo(), - new LyricsEffectInfo(), - }), - }; - - StaveProfiles[StaveProfileTab] = new BarRendererFactory[] - { - new EffectBarRendererFactory("tab-effects", new IEffectBarRendererInfo[] { - new TempoEffectInfo(), - new TripletFeelEffectInfo(), - new MarkerEffectInfo(), - new TextEffectInfo(), - new ChordsEffectInfo(), - new TripletFeelEffectInfo(), - new FermataEffectInfo(), - new TrillEffectInfo(), - new WideBeatVibratoEffectInfo(), - new SlightBeatVibratoEffectInfo(), - new WideNoteVibratoEffectInfo(), - new SlightNoteVibratoEffectInfo(), - new TapEffectInfo(), - new FadeInEffectInfo(), - new HarmonicsEffectInfo(HarmonicType.Artificial), - new HarmonicsEffectInfo(HarmonicType.Pinch), - new HarmonicsEffectInfo(HarmonicType.Tap), - new HarmonicsEffectInfo(HarmonicType.Semi), - new HarmonicsEffectInfo(HarmonicType.Feedback), - new LetRingEffectInfo(), - new CapoEffectInfo(), - new FingeringEffectInfo(), - new PalmMuteEffectInfo(), - new PickStrokeEffectInfo(), - new PickSlideEffectInfo(), - new AlternateEndingsEffectInfo() - }), - new TabBarRendererFactory(true, true, true), - new EffectBarRendererFactory("tab-bottom-effects", new IEffectBarRendererInfo[] { - new LyricsEffectInfo(), - }), - }; - - StaveProfiles[StaveProfileTabMixed] = new BarRendererFactory[] - { - new EffectBarRendererFactory("tab-effects", new IEffectBarRendererInfo[] { - new TempoEffectInfo(), - new TripletFeelEffectInfo(), - new MarkerEffectInfo(), - new TextEffectInfo(), - new ChordsEffectInfo(), - new TripletFeelEffectInfo(), - new TrillEffectInfo(), - new WideBeatVibratoEffectInfo(), - new SlightBeatVibratoEffectInfo(), - new WideNoteVibratoEffectInfo(), - new SlightNoteVibratoEffectInfo(), - new TapEffectInfo(), - new FadeInEffectInfo(), - new HarmonicsEffectInfo(HarmonicType.Artificial), - new HarmonicsEffectInfo(HarmonicType.Pinch), - new HarmonicsEffectInfo(HarmonicType.Tap), - new HarmonicsEffectInfo(HarmonicType.Semi), - new HarmonicsEffectInfo(HarmonicType.Feedback), - new LetRingEffectInfo(), - new CapoEffectInfo(), - new PalmMuteEffectInfo(), - new PickStrokeEffectInfo(), - new PickSlideEffectInfo(), - new AlternateEndingsEffectInfo() - }), - new TabBarRendererFactory(false, false, false), - new EffectBarRendererFactory("tab-bottom-effects", new IEffectBarRendererInfo[] { - new LyricsEffectInfo(), - }), - }; - } - } -} diff --git a/Source/AlphaTab/Exporter/AlphaTexExporter.cs b/Source/AlphaTab/Exporter/AlphaTexExporter.cs deleted file mode 100644 index 481dde97b..000000000 --- a/Source/AlphaTab/Exporter/AlphaTexExporter.cs +++ /dev/null @@ -1,657 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Collections; -using AlphaTab.Model; - -namespace AlphaTab.Exporter -{ - /// - /// This class allows converting scores into alphaTex. - /// - // ReSharper disable once UnusedMember.Global - public class AlphaTexExporter - { - private readonly StringBuilder _builder; - - /// - /// Initializes a new instance of the class. - /// - public AlphaTexExporter() - { - _builder = new StringBuilder(); - } - - /// - /// Exports the given track. - /// - /// The track to export - public void Export(Track track) - { - Score(track); - } - - private void Score(Track track) - { - MetaData(track); - Bars(track); - } - - /// - /// Returns the generated tex code. - /// - /// - public string ToTex() - { - return _builder.ToString(); - } - - private void MetaData(Track track) - { - var score = track.Score; - StringMetaData("title", score.Title); - StringMetaData("subtitle", score.SubTitle); - StringMetaData("artist", score.Artist); - StringMetaData("album", score.Album); - StringMetaData("words", score.Words); - StringMetaData("music", score.Music); - StringMetaData("copyright", score.Copyright); - - _builder.Append("\\tempo "); - _builder.Append(score.Tempo); - _builder.AppendLine(); - - var staff = track.Staves[0]; - - if (staff.Capo > 0) - { - _builder.Append("\\capo "); - _builder.Append(staff.Capo); - _builder.AppendLine(); - } - - _builder.Append("\\tuning"); - for (int i = 0; i < staff.Tuning.Length; i++) - { - _builder.Append(" "); - _builder.Append(Tuning.GetTextForTuning(staff.Tuning[i], true)); - } - - _builder.Append("\\instrument "); - _builder.Append(track.PlaybackInfo.Program); - _builder.AppendLine(); - - _builder.Append("."); - _builder.AppendLine(); - } - - private void StringMetaData(string key, string value) - { - if (!string.IsNullOrWhiteSpace(value)) - { - _builder.Append("\\"); - _builder.Append(key); - _builder.Append(" \""); - _builder.Append(value.Replace("\"", "\\\"")); - _builder.Append("\""); - _builder.AppendLine(); - } - } - - private void Bars(Track track) - { - // alphatab only supports single staves, - for (int i = 0; i < 1; i++) - { - for (int j = 0; j < track.Staves[i].Bars.Count; j++) - { - if (i > 0) - { - _builder.Append(" |"); - _builder.AppendLine(); - } - - Bar(track.Staves[i].Bars[j]); - } - } - } - - private void Bar(Bar bar) - { - BarMeta(bar); - Voice(bar.Voices[0]); - } - - private void Voice(Voice voice) - { - for (int i = 0; i < voice.Beats.Count; i++) - { - Beat(voice.Beats[i]); - } - } - - private void Beat(Beat beat) - { - if (beat.IsRest) - { - _builder.Append("r"); - } - else - { - if (beat.Notes.Count > 1) - { - _builder.Append("("); - } - - for (int i = 0; i < beat.Notes.Count; i++) - { - Note(beat.Notes[i]); - } - - if (beat.Notes.Count > 1) - { - _builder.Append(")"); - } - } - - _builder.Append("."); - _builder.Append((int)beat.Duration); - _builder.Append(" "); - - BeatEffects(beat); - } - - private void Note(Note note) - { - if (note.IsDead) - { - _builder.Append("x"); - } - else if (note.IsTieDestination) - { - _builder.Append("-"); - } - else - { - _builder.Append(note.Fret); - } - - _builder.Append("."); - _builder.Append(note.Beat.Voice.Bar.Staff.Tuning.Length - note.String + 1); - _builder.Append(" "); - - NoteEffects(note); - } - - private void NoteEffects(Note note) - { - bool hasEffectOpen = false; - - if (note.HasBend) - { - hasEffectOpen = EffectOpen(hasEffectOpen); - _builder.Append("be ("); - - for (int i = 0; i < note.BendPoints.Count; i++) - { - _builder.Append(note.BendPoints[i].Offset); - _builder.Append(" "); - _builder.Append(note.BendPoints[i].Value); - _builder.Append(" "); - } - _builder.Append(")"); - } - - switch (note.HarmonicType) - { - case HarmonicType.Natural: - hasEffectOpen = EffectOpen(hasEffectOpen); - _builder.Append("nh "); - break; - case HarmonicType.Artificial: - hasEffectOpen = EffectOpen(hasEffectOpen); - _builder.Append("ah "); - break; - case HarmonicType.Tap: - hasEffectOpen = EffectOpen(hasEffectOpen); - _builder.Append("th "); - break; - case HarmonicType.Pinch: - hasEffectOpen = EffectOpen(hasEffectOpen); - _builder.Append("ph "); - break; - case HarmonicType.Semi: - hasEffectOpen = EffectOpen(hasEffectOpen); - _builder.Append("sh "); - break; - } - - if (note.IsTrill) - { - hasEffectOpen = EffectOpen(hasEffectOpen); - _builder.Append("tr "); - _builder.Append(note.TrillFret); - _builder.Append(" "); - switch (note.TrillSpeed) - { - case Duration.Sixteenth: - _builder.Append("16 "); - break; - case Duration.ThirtySecond: - _builder.Append("32 "); - break; - case Duration.SixtyFourth: - _builder.Append("64 "); - break; - } - } - - if (note.Vibrato != VibratoType.None) - { - hasEffectOpen = EffectOpen(hasEffectOpen); - _builder.Append("v "); - } - - if (note.SlideType == SlideType.Legato) - { - hasEffectOpen = EffectOpen(hasEffectOpen); - _builder.Append("sl "); - } - - if (note.SlideType == SlideType.Shift) - { - hasEffectOpen = EffectOpen(hasEffectOpen); - _builder.Append("ss "); - } - - if (note.IsHammerPullOrigin) - { - hasEffectOpen = EffectOpen(hasEffectOpen); - _builder.Append("h "); - } - - if (note.IsGhost) - { - hasEffectOpen = EffectOpen(hasEffectOpen); - _builder.Append("g "); - } - - if (note.Accentuated == AccentuationType.Normal) - { - hasEffectOpen = EffectOpen(hasEffectOpen); - _builder.Append("ac "); - } - else if (note.Accentuated == AccentuationType.Heavy) - { - hasEffectOpen = EffectOpen(hasEffectOpen); - _builder.Append("hac "); - } - - if (note.IsPalmMute) - { - hasEffectOpen = EffectOpen(hasEffectOpen); - _builder.Append("pm "); - } - - if (note.IsStaccato) - { - hasEffectOpen = EffectOpen(hasEffectOpen); - _builder.Append("st "); - } - - if (note.IsLetRing) - { - hasEffectOpen = EffectOpen(hasEffectOpen); - _builder.Append("lr "); - } - - switch (note.LeftHandFinger) - { - case Fingers.Thumb: - hasEffectOpen = EffectOpen(hasEffectOpen); - _builder.Append("1 "); - break; - case Fingers.IndexFinger: - hasEffectOpen = EffectOpen(hasEffectOpen); - _builder.Append("2 "); - break; - case Fingers.MiddleFinger: - hasEffectOpen = EffectOpen(hasEffectOpen); - _builder.Append("3 "); - break; - case Fingers.AnnularFinger: - hasEffectOpen = EffectOpen(hasEffectOpen); - _builder.Append("4 "); - break; - case Fingers.LittleFinger: - hasEffectOpen = EffectOpen(hasEffectOpen); - _builder.Append("5 "); - break; - } - - switch (note.RightHandFinger) - { - case Fingers.Thumb: - hasEffectOpen = EffectOpen(hasEffectOpen); - _builder.Append("1 "); - break; - case Fingers.IndexFinger: - hasEffectOpen = EffectOpen(hasEffectOpen); - _builder.Append("2 "); - break; - case Fingers.MiddleFinger: - hasEffectOpen = EffectOpen(hasEffectOpen); - _builder.Append("3 "); - break; - case Fingers.AnnularFinger: - hasEffectOpen = EffectOpen(hasEffectOpen); - _builder.Append("4 "); - break; - case Fingers.LittleFinger: - hasEffectOpen = EffectOpen(hasEffectOpen); - _builder.Append("5 "); - break; - } - - EffectClose(hasEffectOpen); - } - - private bool EffectOpen(bool hasBeatEffectOpen) - { - if (!hasBeatEffectOpen) - { - _builder.Append("{"); - } - return true; - } - - private void EffectClose(bool hasBeatEffectOpen) - { - if (hasBeatEffectOpen) - { - _builder.Append("}"); - } - } - - private void BeatEffects(Beat beat) - { - bool hasEffectOpen = false; - - if (beat.FadeIn) - { - hasEffectOpen = EffectOpen(hasEffectOpen); - _builder.Append("f "); - } - - switch (beat.GraceType) - { - case GraceType.BendGrace: - _builder.Append("gr b "); - break; - case GraceType.OnBeat: - _builder.Append("gr ob "); - break; - case GraceType.BeforeBeat: - _builder.Append("gr "); - break; - } - - if (beat.Vibrato != VibratoType.None) - { - hasEffectOpen = EffectOpen(hasEffectOpen); - _builder.Append("v "); - } - - if (beat.Slap) - { - hasEffectOpen = EffectOpen(hasEffectOpen); - _builder.Append("s "); - } - - if (beat.Pop) - { - hasEffectOpen = EffectOpen(hasEffectOpen); - _builder.Append("p "); - } - - if (beat.Dots == 2) - { - hasEffectOpen = EffectOpen(hasEffectOpen); - _builder.Append("dd "); - } - else if (beat.Dots == 1) - { - hasEffectOpen = EffectOpen(hasEffectOpen); - _builder.Append("d "); - } - - if (beat.PickStroke == PickStroke.Up) - { - hasEffectOpen = EffectOpen(hasEffectOpen); - _builder.Append("su "); - } - else if (beat.PickStroke == PickStroke.Down) - { - hasEffectOpen = EffectOpen(hasEffectOpen); - _builder.Append("sd "); - } - - if (beat.HasTuplet) - { - int tupletValue = 0; - if (beat.TupletDenominator == 3 && beat.TupletNumerator == 2) - { - tupletValue = 3; - } - else if (beat.TupletDenominator == 5 && beat.TupletNumerator == 4) - { - tupletValue = 5; - } - else if (beat.TupletDenominator == 6 && beat.TupletNumerator == 4) - { - tupletValue = 6; - } - else if (beat.TupletDenominator == 7 && beat.TupletNumerator == 4) - { - tupletValue = 7; - } - else if (beat.TupletDenominator == 9 && beat.TupletNumerator == 8) - { - tupletValue = 9; - } - else if (beat.TupletDenominator == 10 && beat.TupletNumerator == 8) - { - tupletValue = 10; - } - else if (beat.TupletDenominator == 11 && beat.TupletNumerator == 8) - { - tupletValue = 11; - } - else if (beat.TupletDenominator == 12 && beat.TupletNumerator == 8) - { - tupletValue = 12; - } - - if (tupletValue != 0) - { - hasEffectOpen = EffectOpen(hasEffectOpen); - _builder.Append("tu "); - _builder.Append(tupletValue); - _builder.Append(" "); - } - } - - if (beat.HasWhammyBar) - { - hasEffectOpen = EffectOpen(hasEffectOpen); - _builder.Append("tbe ("); - - for (int i = 0; i < beat.WhammyBarPoints.Count; i++) - { - _builder.Append(beat.WhammyBarPoints[i].Offset); - _builder.Append(" "); - _builder.Append(beat.WhammyBarPoints[i].Value); - _builder.Append(" "); - } - _builder.Append(")"); - } - - if (beat.IsTremolo) - { - hasEffectOpen = EffectOpen(hasEffectOpen); - _builder.Append("tp "); - if (beat.TremoloSpeed == Duration.Eighth) - { - _builder.Append("8 "); - } - else if (beat.TremoloSpeed == Duration.Sixteenth) - { - _builder.Append("16 "); - } - else if (beat.TremoloSpeed == Duration.ThirtySecond) - { - _builder.Append("32 "); - } - else - { - _builder.Append("8 "); - } - } - - - EffectClose(hasEffectOpen); - } - - - private void BarMeta(Bar bar) - { - var masterBar = bar.MasterBar; - if (masterBar.Index > 0) - { - var previousMasterBar = masterBar.PreviousMasterBar; - var previousBar = bar.PreviousBar; - - if (previousMasterBar.TimeSignatureDenominator != masterBar.TimeSignatureDenominator || previousMasterBar.TimeSignatureNumerator != masterBar.TimeSignatureNumerator) - { - _builder.Append("\\ts "); - _builder.Append(masterBar.TimeSignatureNumerator); - _builder.Append(" "); - _builder.Append(masterBar.TimeSignatureDenominator); - _builder.AppendLine(); - } - - if (previousMasterBar.KeySignature != masterBar.KeySignature) - { - _builder.Append("\\ks "); - switch (masterBar.KeySignature) - { - case KeySignature.Cb: - _builder.Append("cb"); - break; - case KeySignature.Gb: - _builder.Append("gb"); - break; - case KeySignature.Db: - _builder.Append("db"); - break; - case KeySignature.Ab: - _builder.Append("ab"); - break; - case KeySignature.Eb: - _builder.Append("eb"); - break; - case KeySignature.Bb: - _builder.Append("bb"); - break; - case KeySignature.F: - _builder.Append("f"); - break; - case KeySignature.C: - _builder.Append("c"); - break; - case KeySignature.G: - _builder.Append("g"); - break; - case KeySignature.D: - _builder.Append("d"); - break; - case KeySignature.A: - _builder.Append("a"); - break; - case KeySignature.E: - _builder.Append("e"); - break; - case KeySignature.B: - _builder.Append("b"); - break; - case KeySignature.FSharp: - _builder.Append("f#"); - break; - case KeySignature.CSharp: - _builder.Append("c#"); - break; - } - _builder.AppendLine(); - } - - if (bar.Clef != previousBar.Clef) - { - _builder.Append("\\clef "); - switch (bar.Clef) - { - case Clef.Neutral: - _builder.Append("n"); - break; - case Clef.C3: - _builder.Append("c3"); - break; - case Clef.C4: - _builder.Append("c4"); - break; - case Clef.F4: - _builder.Append("f4"); - break; - case Clef.G2: - _builder.Append("g2"); - break; - } - _builder.AppendLine(); - } - - if (masterBar.TempoAutomation != null) - { - _builder.Append("\\tempo "); - _builder.Append(masterBar.TempoAutomation.Value); - _builder.AppendLine(); - } - } - - if (masterBar.IsRepeatStart) - { - _builder.Append("\\ro "); - _builder.AppendLine(); - } - - if (masterBar.IsRepeatEnd) - { - _builder.Append("\\rc "); - _builder.Append(masterBar.RepeatCount + 1); - _builder.AppendLine(); - } - } - } -} diff --git a/Source/AlphaTab/IO/BitReader.cs b/Source/AlphaTab/IO/BitReader.cs deleted file mode 100644 index 745f7a5f2..000000000 --- a/Source/AlphaTab/IO/BitReader.cs +++ /dev/null @@ -1,109 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -namespace AlphaTab.IO -{ - /// - /// This utility public class allows bitwise reading of a stream - /// - class BitReader - { - private const int ByteSize = 8; // size of byte in bits - - private int _currentByte; // the currently read byte - private int _position; // the current bit position within the current byte - - private readonly IReadable _source; - - public BitReader(IReadable source) - { - _source = source; - _position = ByteSize; // to ensure a byte is read on beginning - } - - public int ReadByte() - { - return ReadBits(ByteSize); - } - - public byte[] ReadBytes(int count) - { - byte[] bytes = new byte[count]; - for (int i = 0; i < count; i++) - { - bytes[i] = (byte) ReadByte(); - } - return bytes; - } - - public int ReadBits(int count) - { - var bits = 0; - var i = count - 1; - while ( i >= 0 ) - { - bits |= (ReadBit() << i); - i--; - } - return bits; - } - - public int ReadBitsReversed(int count) - { - var bits = 0; - for (int i = 0; i < count; i++) - { - bits |= (ReadBit() << i); - } - return bits; - } - - public int ReadBit() - { - // need a new byte? - if (_position >= ByteSize) - { - _currentByte = _source.ReadByte(); - if (_currentByte == -1) throw new EndOfReaderException(); - _position = 0; - } - - // shift the desired byte to the least significant bit and - // get the value using masking - var value = (_currentByte >> (ByteSize - _position - 1)) & 0x01; - _position++; - return value; - } - - public byte[] ReadAll() - { - var all = ByteBuffer.Empty(); - try - { - while (true) - { - all.WriteByte((byte) ReadByte()); - } - } - catch (EndOfReaderException) - { - } - return all.ToArray(); - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/IO/ByteBuffer.cs b/Source/AlphaTab/IO/ByteBuffer.cs deleted file mode 100644 index 4156413dc..000000000 --- a/Source/AlphaTab/IO/ByteBuffer.cs +++ /dev/null @@ -1,177 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; - -namespace AlphaTab.IO -{ - class ByteBuffer : IWriteable, IReadable - { - private byte[] _buffer; - private int _capacity; - - public int Length { get; private set; } - - public int Position { get; set; } - - public virtual byte[] GetBuffer() - { - return _buffer; - } - - - public static ByteBuffer Empty() - { - return WithCapactiy(0); - } - - public static ByteBuffer WithCapactiy(int capacity) - { - ByteBuffer buffer = new ByteBuffer(); - buffer._buffer = new byte[capacity]; - buffer._capacity = capacity; - return buffer; - } - - public static ByteBuffer FromBuffer(byte[] data) - { - ByteBuffer buffer = new ByteBuffer(); - buffer._buffer = data; - buffer._capacity = buffer.Length = data.Length; - return buffer; - } - - private ByteBuffer() - { - } - - public void Reset() - { - Position = 0; - } - - public void Skip(int offset) - { - Position += offset; - } - - private void SetCapacity(int value) - { - if (value != _capacity) - { - if (value > 0) - { - var newBuffer = new byte[value]; - if (Length > 0) Platform.Platform.BlockCopy(_buffer, 0, newBuffer, 0, Length); - _buffer = newBuffer; - } - else - { - _buffer = null; - } - _capacity = value; - } - } - - public int ReadByte() - { - int n = Length - Position; - if (n <= 0) - return -1; - - return _buffer[Position++]; - } - - public int Read(byte[] buffer, int offset, int count) - { - int n = Length - Position; - if (n > count) n = count; - if (n <= 0) - return 0; - - if (n <= 8) - { - int byteCount = n; - while (--byteCount >= 0) - buffer[offset + byteCount] = _buffer[Position + byteCount]; - } - else - Platform.Platform.BlockCopy(_buffer, Position, buffer, offset, n); - Position += n; - - return n; - } - - public void WriteByte(byte value) - { - byte[] buffer = new byte[1]; - buffer[0] = value; - Write(buffer, 0, 1); - } - - public void Write(byte[] buffer, int offset, int count) - { - int i = Position + count; - - if (i > Length) - { - if (i > _capacity) - { - EnsureCapacity(i); - } - Length = i; - } - if ((count <= 8) && (buffer != _buffer)) - { - int byteCount = count; - while (--byteCount >= 0) - _buffer[Position + byteCount] = buffer[offset + byteCount]; - } - else - { - Platform.Platform.BlockCopy(buffer, offset, _buffer, Position, Math.Min(count, buffer.Length - offset)); - } - Position = i; - } - - private void EnsureCapacity(int value) - { - if (value > _capacity) - { - int newCapacity = value; - if (newCapacity < 256) - newCapacity = 256; - if (newCapacity < _capacity * 2) - newCapacity = _capacity * 2; - SetCapacity(newCapacity); - } - } - - public byte[] ReadAll() - { - return ToArray(); - } - - public virtual byte[] ToArray() - { - byte[] copy = new byte[Length]; - Platform.Platform.BlockCopy(_buffer, 0, copy, 0, Length); - return copy; - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/IO/EndOfReaderException.cs b/Source/AlphaTab/IO/EndOfReaderException.cs deleted file mode 100644 index 36c2a230e..000000000 --- a/Source/AlphaTab/IO/EndOfReaderException.cs +++ /dev/null @@ -1,26 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.IO -{ - class EndOfReaderException : AlphaTabException - { - public EndOfReaderException() : base("") - { - } - } -} diff --git a/Source/AlphaTab/IO/IOHelper.cs b/Source/AlphaTab/IO/IOHelper.cs deleted file mode 100644 index 9a56c20c2..000000000 --- a/Source/AlphaTab/IO/IOHelper.cs +++ /dev/null @@ -1,162 +0,0 @@ -/* - * This file is part of alphaSynth. - * Copyright (c) 2014, T3866, PerryCodes, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Collections; - -namespace AlphaTab.IO -{ - static class IOHelper - { - public static int ReadInt32BE(this IReadable input) - { - var ch1 = input.ReadByte(); - var ch2 = input.ReadByte(); - var ch3 = input.ReadByte(); - var ch4 = input.ReadByte(); - - return ((ch1 << 24) | (ch2 << 16) | (ch3 << 8) | (ch4 << 0)); - } - - public static int ReadInt32LE(this IReadable input) - { - var ch1 = input.ReadByte(); - var ch2 = input.ReadByte(); - var ch3 = input.ReadByte(); - var ch4 = input.ReadByte(); - - return ((ch4 << 24) | (ch3 << 16) | (ch2 << 8) | (ch1 << 0)); - } - - public static uint ReadUInt32LE(this IReadable input) - { - var ch1 = input.ReadByte(); - var ch2 = input.ReadByte(); - var ch3 = input.ReadByte(); - var ch4 = input.ReadByte(); - - return Platform.Platform.ToUInt32((ch4 << 24) | (ch3 << 16) | (ch2 << 8) | (ch1 << 0)); - } - - public static ushort ReadUInt16LE(this IReadable input) - { - var ch1 = input.ReadByte(); - var ch2 = input.ReadByte(); - - return Platform.Platform.ToUInt16((ch2 << 8) | (ch1 << 0)); - } - - public static short ReadInt16LE(this IReadable input) - { - var ch1 = input.ReadByte(); - var ch2 = input.ReadByte(); - - return Platform.Platform.ToInt16((ch2 << 8) | (ch1 << 0)); - } - - public static uint ReadUInt32BE(this IReadable input) - { - var ch1 = input.ReadByte(); - var ch2 = input.ReadByte(); - var ch3 = input.ReadByte(); - var ch4 = input.ReadByte(); - - return Platform.Platform.ToUInt32((ch1 << 24) | (ch2 << 16) | (ch3 << 8) | (ch4 << 0)); - } - - public static ushort ReadUInt16BE(this IReadable input) - { - var ch1 = input.ReadByte(); - var ch2 = input.ReadByte(); - - return Platform.Platform.ToUInt16((ch1 << 8) | (ch2 << 0)); - } - - public static short ReadInt16BE(this IReadable input) - { - var ch1 = input.ReadByte(); - var ch2 = input.ReadByte(); - - return Platform.Platform.ToInt16((ch1 << 8) | (ch2 << 0)); - } - - public static byte[] ReadByteArray(this IReadable input, int length) - { - var v = new byte[length]; - input.Read(v, 0, length); - return v; - } - - public static string Read8BitChars(this IReadable input, int length) - { - byte[] b = new byte[length]; - input.Read(b, 0, b.Length); - return Platform.Platform.ToString(b, "utf-8"); - } - - public static string Read8BitString(this IReadable input) - { - var s = new StringBuilder(); - var c = input.ReadByte(); - while (c != 0) - { - s.AppendChar(c); - c = input.ReadByte(); - } - return s.ToString(); - } - - public static string Read8BitStringLength(this IReadable input, int length) - { - var s = new StringBuilder(); - var z = -1; - for (int i = 0; i < length; i++) - { - var c = input.ReadByte(); - if (c == 0 && z == -1) z = i; - s.AppendChar(c); - } - - var t = s.ToString(); - if (z >= 0) - return t.Substring(0, z); - return t; - } - - public static int ReadSInt8(this IReadable input) - { - var v = input.ReadByte(); - return ((((v & 255) >> 7) * (-256)) + (v & 255)); - } - - - public static int ReadInt24(this byte[] input, int index) - { - int i; - i = input[index] | (input[index + 1] << 8) | (input[index + 2] << 16); - if ((i & 0x800000) == 0x800000) - i = i | (0xFF << 24); - return i; - - } - - public static short ReadInt16(this byte[] input, int index) - { - return Platform.Platform.ToInt16(input[index] | (input[index + 1] << 8)); - } - } -} diff --git a/Source/AlphaTab/IO/IReadable.cs b/Source/AlphaTab/IO/IReadable.cs deleted file mode 100644 index 590c3b027..000000000 --- a/Source/AlphaTab/IO/IReadable.cs +++ /dev/null @@ -1,61 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.IO -{ - /// - /// Represents a stream of binary data that can be read from. - /// - public interface IReadable - { - /// - /// Gets or sets the current read position relative in the stream. - /// - int Position { get; set; } - /// - /// Gets the total number of bytes contained in the stream. - /// - int Length { get; } - /// - /// Resets the stream for reading the data from the beginning. - /// - void Reset(); - /// - /// Skip the given number of bytes. - /// - /// The number of bytes to skip. - void Skip(int offset); - /// - /// Read a single byte from the data stream. - /// - /// The value of the next byte or -1 if there is no more data. - int ReadByte(); - /// - /// Reads the given number of bytes from the stream into the given buffer. - /// - /// The buffer to fill. - /// The offset in the buffer where to start writing. - /// The number of bytes to read. - /// - int Read(byte[] buffer, int offset, int count); - /// - /// Reads the remaining data. - /// - /// - byte[] ReadAll(); - } -} \ No newline at end of file diff --git a/Source/AlphaTab/IO/IWriteable.cs b/Source/AlphaTab/IO/IWriteable.cs deleted file mode 100644 index 931a0fa3f..000000000 --- a/Source/AlphaTab/IO/IWriteable.cs +++ /dev/null @@ -1,38 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.IO -{ - /// - /// Represents a writer where binary data can be written to. - /// - public interface IWriteable - { - /// - /// Write a single byte to the stream. - /// - /// The value to write. - void WriteByte(byte value); - /// - /// Write data from the given buffer. - /// - /// The buffer to get the data from. - /// The offset where to start reading the data. - /// The number of bytes to write - void Write(byte[] buffer, int offset, int count); - } -} \ No newline at end of file diff --git a/Source/AlphaTab/IO/ZipFile.cs b/Source/AlphaTab/IO/ZipFile.cs deleted file mode 100644 index c1a08e7ec..000000000 --- a/Source/AlphaTab/IO/ZipFile.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using AlphaTab.Collections; - -namespace AlphaTab.IO -{ - /// - /// this public class represents a file within the GpxFileSystem - /// - class ZipEntry - { - public string FullName { get; set; } - public string FileName { get; set; } - public byte[] Data { get; set; } - } - - /// - /// This class allows reading zip files. - /// - partial class ZipFile - { - /// - /// You can set a file filter method using this setter. On parsing - /// the filestructure this function can determine based on the filename - /// whether this file will be available after loading. - /// This way we can reduce the amount of memory we store. - /// - public Func FileFilter { get; set; } - - /// - /// Gets the list of entries stored in this Zip File. - /// - public FastList Entries { get; set; } - - /// - /// Creates a new GpxFileSystem instance - /// - public ZipFile() - { - Entries = new FastList(); - FileFilter = s => true; - } - } -} diff --git a/Source/AlphaTab/Importer/AlphaTexException.cs b/Source/AlphaTab/Importer/AlphaTexException.cs deleted file mode 100644 index 339eb4f99..000000000 --- a/Source/AlphaTab/Importer/AlphaTexException.cs +++ /dev/null @@ -1,53 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -namespace AlphaTab.Importer -{ - class AlphaTexException : AlphaTabException - { - public int Position { get; set; } - public string NonTerm { get; set; } - public AlphaTexSymbols Expected { get; set; } - public AlphaTexSymbols Symbol { get; set; } - public object SymbolData { get; set; } - - public AlphaTexException(int position, string nonTerm, AlphaTexSymbols expected, AlphaTexSymbols symbol, object symbolData = null) - : base(BuildMessage(position, nonTerm, expected, symbol, symbolData)) - { - Position = position; - NonTerm = nonTerm; - Expected = expected; - Symbol = symbol; - SymbolData = symbolData; - } - - private static string BuildMessage(int position, string nonTerm, AlphaTexSymbols expected, AlphaTexSymbols symbol, object symbolData) - { - if (symbolData == null) - { - return "MalFormed AlphaTex: @" + position + ": Error on block " + nonTerm + - ", expected a " + expected + " found a " + symbol; - } - else - { - return "MalFormed AlphaTex: @" + position + ": Error on block " + nonTerm + - ", invalid value: " + symbolData; - } - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Importer/AlphaTexImporter.cs b/Source/AlphaTab/Importer/AlphaTexImporter.cs deleted file mode 100644 index d28b38582..000000000 --- a/Source/AlphaTab/Importer/AlphaTexImporter.cs +++ /dev/null @@ -1,1565 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.Audio; -using AlphaTab.Collections; -using AlphaTab.Model; -using AlphaTab.Platform; -using AlphaTab.Util; - -namespace AlphaTab.Importer -{ - /// - /// This importer can parse alphaTex markup into a score structure. - /// - class AlphaTexImporter : ScoreImporter - { - private const int Eof = 0; - private static readonly int[] TrackChannels = { 0, 1 }; - - private Score _score; - private Track _track; - private Staff _staff; - - private int _ch; - private int _curChPos; - - private AlphaTexSymbols _sy; - private object _syData; - private bool _anyDataLoaded; - - private bool _allowNegatives; - - private Duration _currentDuration; - private FastList _lyrics; - - public override string Name { get { return "AlphaTex"; } } - - public override Score ReadScore() - { - try - { - CreateDefaultScore(); - _curChPos = 0; - _currentDuration = Duration.Quarter; - _lyrics = new FastList(); - NextChar(); - NewSy(); - if (_sy == AlphaTexSymbols.LowerThan) - { - // potential XML, stop parsing (alphaTex never starts with <) - throw new UnsupportedFormatException("Unknown start sign <"); - } - Score(); - - _score.Finish(Settings); - _track.ApplyLyrics(_lyrics); - return _score; - } - catch (AlphaTexException e) - { - throw new UnsupportedFormatException(e.Message); - } - } - - private void Error(string nonterm, AlphaTexSymbols expected, bool symbolError = true) - { - AlphaTexException e; - if (symbolError) - { - e = new AlphaTexException(_curChPos, nonterm, expected, _sy); - } - else - { - e = new AlphaTexException(_curChPos, nonterm, expected, expected, _syData); - } - Logger.Error(Name, e.Message); - throw e; - } - - /// - /// Initializes the song with some required default values. - /// - /// - private void CreateDefaultScore() - { - _score = new Score(); - _score.Tempo = 120; - _score.TempoLabel = ""; - - _track = new Track(1); - - _track.PlaybackInfo.Program = 25; - _track.PlaybackInfo.PrimaryChannel = TrackChannels[0]; - _track.PlaybackInfo.SecondaryChannel = TrackChannels[1]; - - _staff = _track.Staves[0]; - _staff.Tuning = Tuning.GetDefaultTuningFor(6).Tunings; - - _score.AddTrack(_track); - } - - /// - /// Converts a clef string into the clef value. - /// - /// the string to convert - /// the clef value - private Clef ParseClefFromString(string str) - { - switch (str.ToLower()) - { - case "g2": - case "treble": - return Clef.G2; - case "f4": - case "bass": - return Clef.F4; - case "c3": - case "tenor": - return Clef.C3; - case "c4": - case "alto": - return Clef.C4; - case "n": - case "neutral": - return Clef.Neutral; - default: - return Clef.G2; // error("clef-value", AlphaTexSymbols.String, false); - } - } - - - /// - /// Converts a clef tuning into the clef value. - /// - /// the tuning value to convert - /// the clef value - private Clef ParseClefFromInt(int i) - { - switch (i) - { - case 43: - return Clef.G2; - case 65: - return Clef.F4; - case 48: - return Clef.C3; - case 60: - return Clef.C4; - default: - return Clef.G2; - } - } - - /// - /// Converts a keysignature string into the assocciated value. - /// - /// the string to convert - /// the assocciated keysignature value - private int ParseKeySignature(String str) - { - switch (str.ToLower()) - { - case "cb": return -7; - case "gb": return -6; - case "db": return -5; - case "ab": return -4; - case "eb": return -3; - case "bb": return -2; - case "f": return -1; - case "c": return 0; - case "g": return 1; - case "d": return 2; - case "a": return 3; - case "e": return 4; - case "b": return 5; - case "f#": return 6; - case "c#": return 7; - default: return 0; // error("keysignature-value", AlphaTexSymbols.String, false); return 0 - } - } - - /// - /// Reads the next character of the source stream. - /// - private void NextChar() - { - var b = Data.ReadByte(); - if (b == -1) - { - _ch = Eof; - } - else - { - _ch = b; - _curChPos++; - } - } - - /// - /// Reads the next terminal symbol. - /// - private void NewSy() - { - _sy = AlphaTexSymbols.No; - do - { - if (_ch == Eof) - { - _sy = AlphaTexSymbols.Eof; - } - else if (Platform.Platform.IsWhiteSpace(_ch)) - { - // skip whitespaces - NextChar(); - } - else if (_ch == 0x2F /* / */) - { - NextChar(); - if (_ch == 0x2F /* / */) - { - // single line comment - while (_ch != 0x0D /* \r */ && _ch != 0x0A /* \n */ && _ch != Eof) - { - NextChar(); - } - } - else if (_ch == 0x2A /* * */) - { - // multiline comment - while (_ch != Eof) - { - if (_ch == 0x2A /* * */) // possible end - { - NextChar(); - if (_ch == 0x2F /* / */) - { - NextChar(); - break; - } - } - else - { - NextChar(); - } - } - } - else - { - Error("symbol", AlphaTexSymbols.String, false); - } - } - else if (_ch == 0x22 /* " */ || _ch == 0x27 /* ' */) - { - var startChar = _ch; - NextChar(); - var s = new StringBuilder(); - _sy = AlphaTexSymbols.String; - while (_ch != startChar && _ch != Eof) - { - s.AppendChar(_ch); - NextChar(); - } - _syData = s.ToString(); - NextChar(); - } - else if (_ch == 0x2D /* - */) // negative number - { - // is number? - if (_allowNegatives && IsDigit(_ch)) - { - var number = ReadNumber(); - _sy = AlphaTexSymbols.Number; - _syData = number; - } - else - { - _sy = AlphaTexSymbols.String; - _syData = ReadName(); - } - } - else if (_ch == 0x2E /* . */) - { - _sy = AlphaTexSymbols.Dot; - NextChar(); - } - else if (_ch == 0x3A /* : */) - { - _sy = AlphaTexSymbols.DoubleDot; - NextChar(); - } - else if (_ch == 0x28 /* ( */) - { - _sy = AlphaTexSymbols.LParensis; - NextChar(); - } - else if (_ch == 0x5C /* \ */) - { - NextChar(); - var name = ReadName(); - _sy = AlphaTexSymbols.MetaCommand; - _syData = name; - } - else if (_ch == 0x29 /* ) */) - { - _sy = AlphaTexSymbols.RParensis; - NextChar(); - } - else if (_ch == 0x7B /* { */) - { - _sy = AlphaTexSymbols.LBrace; - NextChar(); - } - else if (_ch == 0x7D /* } */) - { - _sy = AlphaTexSymbols.RBrace; - NextChar(); - } - else if (_ch == 0x7C /* | */) - { - _sy = AlphaTexSymbols.Pipe; - NextChar(); - } - else if (_ch == 0x2A /* * */) - { - _sy = AlphaTexSymbols.Multiply; - NextChar(); - } - else if (_ch == 0x3C /* < */) - { - _sy = AlphaTexSymbols.LowerThan; - NextChar(); - } - else if (IsDigit(_ch)) - { - var number = ReadNumber(); - _sy = AlphaTexSymbols.Number; - _syData = number; - } - else if (IsLetter(_ch)) - { - var name = ReadName(); - var tuning = TuningParser.Parse(name); - if (tuning != null) - { - _sy = AlphaTexSymbols.Tuning; - _syData = tuning; - } - else - { - _sy = AlphaTexSymbols.String; - _syData = name; - } - } - else - { - Error("symbol", AlphaTexSymbols.String, false); - } - } while (_sy == AlphaTexSymbols.No); - } - - /// - /// Checks if the given character is a letter. - /// (no control characters, whitespaces, numbers or dots) - /// - /// the character - /// true if the given character is a letter, otherwise false. - private static bool IsLetter(int code) - { - // no control characters, whitespaces, numbers or dots - return !IsTerminal(code) && ( - (code >= 0x21 && code <= 0x2F) || - (code >= 0x3A && code <= 0x7E) || - (code > 0x80)); /* Unicode Symbols */ - } - - /// - /// Checks if the given charater is a non terminal. - /// - /// the character - /// true if the given character is a terminal, otherwise false. - private static bool IsTerminal(int ch) - { - return ch == 0x2E /* . */ || - ch == 0x7B /* { */ || - ch == 0x7D /* } */ || - ch == 0x5B /* [ */ || - ch == 0x5D /* ] */ || - ch == 0x28 /* ( */ || - ch == 0x29 /* ) */ || - ch == 0x7C /* | */ || - ch == 0x27 /* ' */ || - ch == 0x22 /* " */ || - ch == 0x5C /* \ */; - } - - /// - /// Checks if the given character is a digit. - /// - /// the character - /// true if the given character is a digit, otherwise false. - private bool IsDigit(int code) - { - return (code >= 0x30 && code <= 0x39) || /*0-9*/ - (code == 0x2D /* - */ && _allowNegatives); // allow - if negatives - } - - /// - /// Reads a string from the stream. - /// - /// the read string. - private string ReadName() - { - var str = new StringBuilder(); - do - { - str.AppendChar(_ch); - NextChar(); - } while (IsLetter(_ch) || IsDigit(_ch) || _ch == 0x23); - return str.ToString(); - } - - /// - /// Reads a number from the stream. - /// - /// the read number. - private int ReadNumber() - { - var str = new StringBuilder(); - do - { - str.AppendChar(_ch); - NextChar(); - } while (IsDigit(_ch)); - return Platform.Platform.ParseInt(str.ToString()); - } - - #region Recursive Decent Parser - - private void Score() - { - MetaData(); - Bars(); - } - - private void MetaData() - { - var anyMeta = false; - while (_sy == AlphaTexSymbols.MetaCommand) - { - var syData = _syData.ToString().ToLower(); - if (syData == "title") - { - NewSy(); - if (_sy == AlphaTexSymbols.String) - { - _score.Title = _syData.ToString(); - } - else - { - Error("title", AlphaTexSymbols.String); - } - NewSy(); - _anyDataLoaded = anyMeta = true; - } - else if (syData == "subtitle") - { - NewSy(); - if (_sy == AlphaTexSymbols.String) - { - _score.SubTitle = _syData.ToString(); - } - else - { - Error("subtitle", AlphaTexSymbols.String); - } - NewSy(); - _anyDataLoaded = anyMeta = true; - } - else if (syData == "artist") - { - NewSy(); - if (_sy == AlphaTexSymbols.String) - { - _score.Artist = _syData.ToString(); - } - else - { - Error("artist", AlphaTexSymbols.String); - } - NewSy(); - _anyDataLoaded = anyMeta = true; - } - else if (syData == "album") - { - NewSy(); - if (_sy == AlphaTexSymbols.String) - { - _score.Album = _syData.ToString(); - } - else - { - Error("album", AlphaTexSymbols.String); - } - NewSy(); - _anyDataLoaded = anyMeta = true; - } - else if (syData == "words") - { - NewSy(); - if (_sy == AlphaTexSymbols.String) - { - _score.Words = _syData.ToString(); - } - else - { - Error("words", AlphaTexSymbols.String); - } - NewSy(); - _anyDataLoaded = anyMeta = true; - } - else if (syData == "music") - { - NewSy(); - if (_sy == AlphaTexSymbols.String) - { - _score.Music = _syData.ToString(); - } - else - { - Error("music", AlphaTexSymbols.String); - } - NewSy(); - _anyDataLoaded = anyMeta = true; - } - else if (syData == "copyright") - { - NewSy(); - if (_sy == AlphaTexSymbols.String) - { - _score.Copyright = _syData.ToString(); - } - else - { - Error("copyright", AlphaTexSymbols.String); - } - NewSy(); - _anyDataLoaded = anyMeta = true; - } - else if (syData == "tempo") - { - NewSy(); - if (_sy == AlphaTexSymbols.Number) - { - _score.Tempo = (int)_syData; - } - else - { - Error("tempo", AlphaTexSymbols.Number); - } - NewSy(); - _anyDataLoaded = anyMeta = true; - } - else if (syData == "capo") - { - NewSy(); - if (_sy == AlphaTexSymbols.Number) - { - _staff.Capo = (int)_syData; - } - else - { - Error("capo", AlphaTexSymbols.Number); - } - NewSy(); - _anyDataLoaded = anyMeta = true; - } - else if (syData == "tuning") - { - NewSy(); - switch (_sy) - { - case AlphaTexSymbols.String: - var text = _syData.ToString().ToLower(); - if (text == "piano" || text == "none" || text == "voice") - { - // clear tuning - _staff.Tuning = new int[0]; - } - else - { - Error("tuning", AlphaTexSymbols.Tuning); - } - NewSy(); - break; - case AlphaTexSymbols.Tuning: - var tuning = new FastList(); - do - { - var t = (TuningParseResult)_syData; - tuning.Add(t.RealValue); - NewSy(); - } while (_sy == AlphaTexSymbols.Tuning); - - _staff.Tuning = tuning.ToArray(); - break; - default: - Error("tuning", AlphaTexSymbols.Tuning); - break; - } - _anyDataLoaded = anyMeta = true; - } - else if (syData == "instrument") - { - NewSy(); - if (_sy == AlphaTexSymbols.Number) - { - var instrument = (int)(_syData); - if (instrument >= 0 && instrument <= 128) - { - _track.PlaybackInfo.Program = (int)_syData; - } - else - { - Error("instrument", AlphaTexSymbols.Number, false); - } - } - else if (_sy == AlphaTexSymbols.String) // Name - { - var instrumentName = _syData.ToString().ToLower(); - _track.PlaybackInfo.Program = GeneralMidi.GetValue(instrumentName); - } - else - { - Error("instrument", AlphaTexSymbols.Number); - } - NewSy(); - _anyDataLoaded = anyMeta = true; - } - else if (syData == "lyrics") - { - NewSy(); - - var lyrics = new Lyrics(); - lyrics.StartBar = 0; - lyrics.Text = ""; - - if (_sy == AlphaTexSymbols.Number) // Name - { - lyrics.StartBar = (int)_syData; - NewSy(); - } - - if (_sy == AlphaTexSymbols.String) - { - lyrics.Text = (string)_syData; - NewSy(); - } - else - { - Error("lyrics", AlphaTexSymbols.String); - } - - _lyrics.Add(lyrics); - - _anyDataLoaded = anyMeta = true; - } - else if (anyMeta) - { - Error("metaDataTags", AlphaTexSymbols.String, false); - } - else - { - // fall forward to bar meta if unknown score meta was found - break; - } - } - - if (anyMeta) - { - if (_sy != AlphaTexSymbols.Dot) - { - Error("song", AlphaTexSymbols.Dot); - } - NewSy(); - } - else if (_sy == AlphaTexSymbols.Dot) - { - NewSy(); - } - } - - - private void Bars() - { - Bar(); - while (_sy != AlphaTexSymbols.Eof) - { - // read pipe from last bar - if (_sy != AlphaTexSymbols.Pipe) - { - Error("bar", AlphaTexSymbols.Pipe); - } - NewSy(); - - Bar(); - } - } - - private void Bar() - { - var master = new MasterBar(); - _score.AddMasterBar(master); - - var bar = new Bar(); - _staff.AddBar(bar); - - if (master.Index > 0) - { - master.KeySignature = master.PreviousMasterBar.KeySignature; - master.TimeSignatureDenominator = master.PreviousMasterBar.TimeSignatureDenominator; - master.TimeSignatureNumerator = master.PreviousMasterBar.TimeSignatureNumerator; - bar.Clef = bar.PreviousBar.Clef; - } - BarMeta(bar); - - var voice = new Voice(); - bar.AddVoice(voice); - - while (_sy != AlphaTexSymbols.Pipe && _sy != AlphaTexSymbols.Eof) - { - Beat(voice); - } - - if (voice.Beats.Count == 0) - { - var emptyBeat = new Beat(); - emptyBeat.IsEmpty = true; - voice.AddBeat(emptyBeat); - } - } - - private void Beat(Voice voice) - { - // duration specifier? - if (_sy == AlphaTexSymbols.DoubleDot) - { - _allowNegatives = true; - NewSy(); - _allowNegatives = false; - if (_sy != AlphaTexSymbols.Number) - { - Error("duration", AlphaTexSymbols.Number); - } - - _currentDuration = ParseDuration((int)_syData); - - NewSy(); - return; - } - - var beat = new Beat(); - voice.AddBeat(beat); - - if (voice.Bar.MasterBar.TempoAutomation != null && voice.Beats.Count == 1) - { - beat.Automations.Add(voice.Bar.MasterBar.TempoAutomation); - } - - // notes - if (_sy == AlphaTexSymbols.LParensis) - { - NewSy(); - - Note(beat); - while (_sy != AlphaTexSymbols.RParensis && _sy != AlphaTexSymbols.Eof) - { - Note(beat); - } - - if (_sy != AlphaTexSymbols.RParensis) - { - Error("note-list", AlphaTexSymbols.RParensis); - } - NewSy(); - } - // rest - else if (_sy == AlphaTexSymbols.String && _syData.ToString().ToLower() == "r") - { - // rest voice -> no notes - NewSy(); - } - else - { - Note(beat); - } - - // new duration - if (_sy == AlphaTexSymbols.Dot) - { - _allowNegatives = true; - NewSy(); - _allowNegatives = false; - if (_sy != AlphaTexSymbols.Number) - { - Error("duration", AlphaTexSymbols.Number); - } - - _currentDuration = ParseDuration((int)_syData); - NewSy(); - } - beat.Duration = _currentDuration; - - // beat multiplier (repeat beat n times) - var beatRepeat = 1; - if (_sy == AlphaTexSymbols.Multiply) - { - NewSy(); - - // multiplier count - if (_sy != AlphaTexSymbols.Number) - { - Error("multiplier", AlphaTexSymbols.Number); - } - else - { - beatRepeat = (int)_syData; - } - NewSy(); - } - - BeatEffects(beat); - - for (var i = 0; i < beatRepeat - 1; i++) - { - voice.AddBeat(beat.Clone()); - } - } - - private void BeatEffects(Beat beat) - { - if (_sy != AlphaTexSymbols.LBrace) - { - return; - } - NewSy(); - - while (_sy == AlphaTexSymbols.String) - { - _syData = _syData.ToString().ToLower(); - if (!ApplyBeatEffect(beat)) - { - Error("beat-effects", AlphaTexSymbols.String, false); - } - } - - if (_sy != AlphaTexSymbols.RBrace) - { - Error("beat-effects", AlphaTexSymbols.RBrace); - } - NewSy(); - } - - - /// - /// Tries to apply a beat effect to the given beat. - /// - /// true if a effect could be applied, otherwise false - private bool ApplyBeatEffect(Beat beat) - { - var syData = _syData.ToString().ToLower(); - if (syData == "f") - { - beat.FadeIn = true; - NewSy(); - return true; - } - if (syData == "v") - { - beat.Vibrato = VibratoType.Slight; - NewSy(); - return true; - } - if (syData == "s") - { - beat.Slap = true; - NewSy(); - return true; - } - if (syData == "p") - { - beat.Pop = true; - NewSy(); - return true; - } - if (syData == "dd") - { - beat.Dots = 2; - NewSy(); - return true; - } - if (syData == "d") - { - beat.Dots = 1; - NewSy(); - return true; - } - if (syData == "su") - { - beat.PickStroke = PickStroke.Up; - NewSy(); - return true; - } - if (syData == "sd") - { - beat.PickStroke = PickStroke.Down; - NewSy(); - return true; - } - if (syData == "tu") - { - NewSy(); - if (_sy != AlphaTexSymbols.Number) - { - Error("tuplet", AlphaTexSymbols.Number); - return false; - } - var tuplet = (int)_syData; - switch (tuplet) - { - case 3: - beat.TupletNumerator = 3; - beat.TupletDenominator = 2; - break; - case 5: - beat.TupletNumerator = 5; - beat.TupletDenominator = 4; - break; - case 6: - beat.TupletNumerator = 6; - beat.TupletDenominator = 4; - break; - case 7: - beat.TupletNumerator = 7; - beat.TupletDenominator = 4; - break; - case 9: - beat.TupletNumerator = 9; - beat.TupletDenominator = 8; - break; - case 10: - beat.TupletNumerator = 10; - beat.TupletDenominator = 8; - break; - case 11: - beat.TupletNumerator = 11; - beat.TupletDenominator = 8; - break; - case 12: - beat.TupletNumerator = 12; - beat.TupletNumerator = 8; - beat.TupletDenominator = 8; - break; - } - NewSy(); - return true; - } - if (syData == "tb" || syData == "tbe") - { - var exact = syData == "tbe"; - // read points - NewSy(); - if (_sy != AlphaTexSymbols.LParensis) - { - Error("tremolobar-effect", AlphaTexSymbols.LParensis); - return false; - } - _allowNegatives = true; - - NewSy(); - while (_sy != AlphaTexSymbols.RParensis && _sy != AlphaTexSymbols.Eof) - { - int offset; - int value; - - if (exact) - { - if (_sy != AlphaTexSymbols.Number) - { - Error("tremolobar-effect", AlphaTexSymbols.Number); - return false; - } - offset = (int)_syData; - - NewSy(); - if (_sy != AlphaTexSymbols.Number) - { - Error("tremolobar-effect", AlphaTexSymbols.Number); - return false; - } - value = (int)_syData; - } - else - { - if (_sy != AlphaTexSymbols.Number) - { - Error("tremolobar-effect", AlphaTexSymbols.Number); - return false; - } - offset = 0; - value = (int)_syData; - } - - beat.AddWhammyBarPoint(new BendPoint(offset, value)); - - NewSy(); - } - - while (beat.WhammyBarPoints.Count > 60) - { - beat.RemoveWhammyBarPoint(beat.WhammyBarPoints.Count - 1); - } - - // set positions - if (!exact) - { - var count = beat.WhammyBarPoints.Count; - var step = (60 / count); - var i = 0; - while (i < count) - { - beat.WhammyBarPoints[i].Offset = Math.Min(60, (i * step)); - i++; - } - } - else - { - beat.WhammyBarPoints.Sort((a, b) => a.Offset - b.Offset); - } - _allowNegatives = false; - - if (_sy != AlphaTexSymbols.RParensis) - { - Error("tremolobar-effect", AlphaTexSymbols.RParensis); - return false; - } - NewSy(); - return true; - } - - if (syData == "gr") - { - NewSy(); - if (_syData.ToString().ToLower() == "ob") - { - beat.GraceType = GraceType.OnBeat; - NewSy(); - } - else if (_syData.ToString().ToLower() == "b") - { - beat.GraceType = GraceType.BendGrace; - NewSy(); - } - else - { - beat.GraceType = GraceType.BeforeBeat; - } - return true; - } - - if (syData == "tp") - { - NewSy(); - var duration = Duration.Eighth; - if (_sy == AlphaTexSymbols.Number) - { - switch ((int)_syData) - { - case 8: - duration = Duration.Eighth; - break; - case 16: - duration = Duration.Sixteenth; - break; - case 32: - duration = Duration.ThirtySecond; - break; - default: - duration = Duration.Eighth; - break; - } - NewSy(); - } - beat.TremoloSpeed = duration; - - return true; - } - - return false; - } - - private void Note(Beat beat) - { - // fret.string - - var isDead = _syData.ToString() == "x"; - var isTie = _syData.ToString() == "-"; - var fret = -1; - var octave = -1; - var tone = -1; - switch (_sy) - { - case AlphaTexSymbols.Number: - fret = (int)_syData; - break; - case AlphaTexSymbols.String: - if (isTie || isDead) - { - fret = 0; - } - else - { - Error("note-fret", AlphaTexSymbols.Number); - } - break; - case AlphaTexSymbols.Tuning: - var tuning = (TuningParseResult)_syData; - octave = tuning.Octave; - tone = tuning.NoteValue; - break; - default: - Error("note-fret", AlphaTexSymbols.Number); - break; - - } - - NewSy(); // Fret done - - var isFretted = octave == -1 && _staff.Tuning.Length > 0; - - int @string = -1; - if (isFretted) - { - // Fret [Dot] String - - if (_sy != AlphaTexSymbols.Dot) - { - Error("note", AlphaTexSymbols.Dot); - } - NewSy(); // dot done - - if (_sy != AlphaTexSymbols.Number) - { - Error("note-string", AlphaTexSymbols.Number); - } - @string = (int)_syData; - if (@string < 1 || @string > _staff.Tuning.Length) - { - Error("note-string", AlphaTexSymbols.Number, false); - } - NewSy(); // string done - } - - // read effects - var note = new Note(); - beat.AddNote(note); - if (isFretted) - { - note.String = _staff.Tuning.Length - (@string - 1); - note.IsDead = isDead; - note.IsTieDestination = isTie; - if (!isTie) - { - note.Fret = fret; - } - } - else - { - note.Octave = octave; - note.Tone = tone; - note.IsTieDestination = isTie; - } - - NoteEffects(note); - - } - private void NoteEffects(Note note) - { - if (_sy != AlphaTexSymbols.LBrace) - { - return; - } - NewSy(); - - while (_sy == AlphaTexSymbols.String) - { - var syData = _syData.ToString().ToLower(); - _syData = syData; - if (syData == "b" || syData == "be") - { - var exact = (string)_syData == "be"; - // read points - NewSy(); - if (_sy != AlphaTexSymbols.LParensis) - { - Error("bend-effect", AlphaTexSymbols.LParensis); - } - - NewSy(); - while (_sy != AlphaTexSymbols.RParensis && _sy != AlphaTexSymbols.Eof) - { - var offset = 0; - var value = 0; - if (exact) - { - if (_sy != AlphaTexSymbols.Number) - { - Error("bend-effect-value", AlphaTexSymbols.Number); - } - offset = (int)_syData; - - NewSy(); - if (_sy != AlphaTexSymbols.Number) - { - Error("bend-effect-value", AlphaTexSymbols.Number); - } - value = (int)_syData; - } - else - { - if (_sy != AlphaTexSymbols.Number) - { - Error("bend-effect-value", AlphaTexSymbols.Number); - } - value = (int)_syData; - } - - note.AddBendPoint(new BendPoint(offset, value)); - NewSy(); - } - - while (note.BendPoints.Count > 60) - { - note.BendPoints.RemoveAt(note.BendPoints.Count - 1); - } - - // set positions - if (exact) - { - note.BendPoints.Sort((a, b) => a.Offset - b.Offset); - } - else - { - var count = note.BendPoints.Count; - var step = 60 / (count - 1); - var i = 0; - while (i < count) - { - note.BendPoints[i].Offset = Math.Min(60, (i * step)); - i++; - } - } - - - if (_sy != AlphaTexSymbols.RParensis) - { - Error("bend-effect", AlphaTexSymbols.RParensis); - } - NewSy(); - } - else if (syData == "nh") - { - note.HarmonicType = HarmonicType.Natural; - NewSy(); - } - else if (syData == "ah") - { - // todo: Artificial Key - note.HarmonicType = HarmonicType.Artificial; - NewSy(); - } - else if (syData == "th") - { - // todo: store tapped fret in data - note.HarmonicType = HarmonicType.Tap; - NewSy(); - } - else if (syData == "ph") - { - note.HarmonicType = HarmonicType.Pinch; - NewSy(); - } - else if (syData == "sh") - { - note.HarmonicType = HarmonicType.Semi; - NewSy(); - } - else if (syData == "tr") - { - NewSy(); - if (_sy != AlphaTexSymbols.Number) - { - Error("trill-effect", AlphaTexSymbols.Number); - } - int fret = (int)_syData; - NewSy(); - - var duration = Duration.Sixteenth; - if (_sy == AlphaTexSymbols.Number) - { - switch ((int)_syData) - { - case 16: - duration = Duration.Sixteenth; - break; - case 32: - duration = Duration.ThirtySecond; - break; - case 64: - duration = Duration.SixtyFourth; - break; - default: - duration = Duration.Sixteenth; - break; - } - NewSy(); - } - - note.TrillValue = fret + note.StringTuning; - note.TrillSpeed = duration; - } - else if (syData == "v") - { - NewSy(); - note.Vibrato = VibratoType.Slight; - } - else if (syData == "sl") - { - NewSy(); - note.SlideType = SlideType.Legato; - } - else if (syData == "ss") - { - NewSy(); - note.SlideType = SlideType.Shift; - } - else if (syData == "h") - { - NewSy(); - note.IsHammerPullOrigin = true; - } - else if (syData == "g") - { - NewSy(); - note.IsGhost = true; - } - else if (syData == "ac") - { - NewSy(); - note.Accentuated = AccentuationType.Normal; - } - else if (syData == "hac") - { - NewSy(); - note.Accentuated = AccentuationType.Heavy; - } - else if (syData == "pm") - { - NewSy(); - note.IsPalmMute = true; - } - else if (syData == "st") - { - NewSy(); - note.IsStaccato = true; - } - else if (syData == "lr") - { - NewSy(); - note.IsLetRing = true; - } - else if (syData == "x") - { - NewSy(); - note.Fret = 0; - note.IsDead = true; - } - else if (syData == "lf") - { - NewSy(); - var finger = Fingers.Thumb; - if (_sy == AlphaTexSymbols.Number) - { - finger = ToFinger((int)_syData); - NewSy(); - } - note.LeftHandFinger = finger; - } - else if (syData == "rf") - { - NewSy(); - var finger = Fingers.Thumb; - if (_sy == AlphaTexSymbols.Number) - { - finger = ToFinger((int)_syData); - NewSy(); - } - note.RightHandFinger = finger; - } - else if (ApplyBeatEffect(note.Beat)) // also try beat effects - { - // Success - } - else - { - Error(syData, AlphaTexSymbols.String, false); - } - } - - if (_sy != AlphaTexSymbols.RBrace) - { - Error("note-effect", AlphaTexSymbols.RBrace, false); - } - NewSy(); - } - - private Fingers ToFinger(int syData) - { - switch (syData) - { - case 1: - return Fingers.Thumb; - case 2: - return Fingers.IndexFinger; - case 3: - return Fingers.MiddleFinger; - case 4: - return Fingers.AnnularFinger; - case 5: - return Fingers.LittleFinger; - } - return Fingers.Thumb; - } - - private Duration ParseDuration(int duration) - { - switch (duration) - { - case -4: return Duration.QuadrupleWhole; - case -2: return Duration.DoubleWhole; - case 1: return Duration.Whole; - case 2: return Duration.Half; - case 4: return Duration.Quarter; - case 8: return Duration.Eighth; - case 16: return Duration.Sixteenth; - case 32: return Duration.ThirtySecond; - case 64: return Duration.SixtyFourth; - case 128: return Duration.OneHundredTwentyEighth; - default: return Duration.Quarter; - } - } - - private void BarMeta(Bar bar) - { - var master = bar.MasterBar; - while (_sy == AlphaTexSymbols.MetaCommand) - { - var syData = _syData.ToString().ToLower(); - if (syData == "ts") - { - NewSy(); - if (_sy != AlphaTexSymbols.Number) - { - Error("timesignature-numerator", AlphaTexSymbols.Number); - } - master.TimeSignatureNumerator = (int)_syData; - NewSy(); - if (_sy != AlphaTexSymbols.Number) - { - Error("timesignature-denominator", AlphaTexSymbols.Number); - } - master.TimeSignatureDenominator = (int)_syData; - } - else if (syData == "ro") - { - master.IsRepeatStart = true; - } - else if (syData == "rc") - { - NewSy(); - if (_sy != AlphaTexSymbols.Number) - { - Error("repeatclose", AlphaTexSymbols.Number); - } - master.RepeatCount = ((int)_syData) - 1; - } - else if (syData == "ks") - { - NewSy(); - if (_sy != AlphaTexSymbols.String) - { - Error("keysignature", AlphaTexSymbols.String); - } - master.KeySignature = (KeySignature)ParseKeySignature(_syData.ToString().ToLower()); - } - else if (syData == "clef") - { - NewSy(); - switch (_sy) - { - case AlphaTexSymbols.String: - bar.Clef = ParseClefFromString(_syData.ToString().ToLower()); - break; - case AlphaTexSymbols.Number: - bar.Clef = ParseClefFromInt((int)_syData); - break; - case AlphaTexSymbols.Tuning: - TuningParseResult parseResult = (TuningParseResult) _syData; - bar.Clef = ParseClefFromInt(parseResult.RealValue); - break; - default: - Error("clef", AlphaTexSymbols.String); - break; - } - - } - else if (syData == "tempo") - { - NewSy(); - if (_sy != AlphaTexSymbols.Number) - { - Error("tempo", AlphaTexSymbols.Number); - } - var tempoAutomation = new Automation(); - tempoAutomation.IsLinear = true; - tempoAutomation.Type = AutomationType.Tempo; - tempoAutomation.Value = (float)_syData; - master.TempoAutomation = tempoAutomation; - } - else - { - Error("measure-effects", AlphaTexSymbols.String, false); - } - NewSy(); - } - } - - #endregion - } - -} \ No newline at end of file diff --git a/Source/AlphaTab/Importer/AlphaTexSymbols.cs b/Source/AlphaTab/Importer/AlphaTexSymbols.cs deleted file mode 100644 index 309b68d53..000000000 --- a/Source/AlphaTab/Importer/AlphaTexSymbols.cs +++ /dev/null @@ -1,41 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Importer -{ - /// - /// A list of terminals recognized by the alphaTex-parser - /// - enum AlphaTexSymbols - { - No, - Eof, - Number, - DoubleDot, - Dot, - String, - Tuning, - LParensis, - RParensis, - LBrace, - RBrace, - Pipe, - MetaCommand, - Multiply, - LowerThan - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Importer/BinaryStylesheet.cs b/Source/AlphaTab/Importer/BinaryStylesheet.cs deleted file mode 100644 index 9c13c78d1..000000000 --- a/Source/AlphaTab/Importer/BinaryStylesheet.cs +++ /dev/null @@ -1,33 +0,0 @@ -using AlphaTab.Collections; -using AlphaTab.Model; - -namespace AlphaTab.Importer -{ - class BinaryStylesheet - { - public FastDictionary Raw { get; } - - public BinaryStylesheet() - { - Raw = new FastDictionary(); - } - - public void Apply(Score score) - { - foreach (var key in Raw) - { - switch (key) - { - case "StandardNotation/hideDynamics": - score.Stylesheet.HideDynamics = (bool) Raw[key]; - break; - } - } - } - - public void AddValue(string key, object value) - { - Raw[key] = value; - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Importer/BinaryStylesheetParser.cs b/Source/AlphaTab/Importer/BinaryStylesheetParser.cs deleted file mode 100644 index 786cfcdc0..000000000 --- a/Source/AlphaTab/Importer/BinaryStylesheetParser.cs +++ /dev/null @@ -1,125 +0,0 @@ -using AlphaTab.IO; -using AlphaTab.Model; -using AlphaTab.Rendering.Utils; - -namespace AlphaTab.Importer -{ - /// - /// A parser for the BinaryStylesheet file of Guitar Pro 6 and 7. - /// - /// - /// The BinaryStylesheet is a simple binary key-value store. - /// - /// File: - /// int32 (big endian) | Number of KeyValuePairs - /// KeyValuePair[] | The raw records - /// - /// KeyValuePair: - /// 1 Byte | length of the key - /// n Bytes | key as utf8 encoded string - /// 1 Byte | Data Type - /// n Bytes | Value - /// - /// Values based on Data Type: - /// 0 = bool - /// 0 == false - /// 1 = int32 (big endian) - /// 2 = float (big endian, IEEE) - /// 3 = string - /// int16 (big endian) | length of string - /// n bytes | utf-8 encoded string - /// 4 = point - /// int32 (big endian) | X-coordinate - /// int32 (big endian) | Y-coordinate - /// 5 = size - /// int32 (big endian) | Width - /// int32 (big endian) | Height - /// 6 = rectangle - /// int32 (big endian) | X-coordinate - /// int32 (big endian) | Y-coordinate - /// int32 (big endian) | Width - /// int32 (big endian) | Height - /// 7 = color - /// 1 byte | Red - /// 1 byte | Green - /// 1 byte | Blue - /// 1 byte | Alpha - /// - class BinaryStylesheetParser - { - enum DataType - { - Boolean, - Integer, - Float, - String, - Point, - Size, - Rectangle, - Color - } - - public BinaryStylesheet Stylesheet { get; private set; } - - public void Parse(byte[] data) - { - // BinaryStylesheet apears to be big-endien - Stylesheet = new BinaryStylesheet(); - - var readable = ByteBuffer.FromBuffer(data); - var entryCount = readable.ReadInt32BE(); - for (int i = 0; i < entryCount; i++) - { - var key = readable.GpReadString(readable.ReadByte(), "utf-8"); - var type = (DataType)readable.ReadByte(); - - switch (type) - { - case DataType.Boolean: - var flag = readable.ReadByte() == 1; - Stylesheet.AddValue(key, flag); - break; - case DataType.Integer: - var ivalue = readable.ReadInt32BE(); - Stylesheet.AddValue(key, ivalue); - break; - case DataType.Float: - var fvalue = readable.GpReadFloat(); - Stylesheet.AddValue(key, fvalue); - break; - case DataType.String: - var s = readable.GpReadString(readable.ReadInt16BE(), "utf-8"); - Stylesheet.AddValue(key, s); - break; - case DataType.Point: - var x = readable.ReadInt32BE(); - var y = readable.ReadInt32BE(); - Stylesheet.AddValue(key, new BendPoint(x, y)); - break; - case DataType.Size: - var width = readable.ReadInt32BE(); - var height = readable.ReadInt32BE(); - Stylesheet.AddValue(key, new BendPoint(width, height)); - break; - case DataType.Rectangle: - var rectX = readable.ReadInt32BE(); - var rectY = readable.ReadInt32BE(); - var rectW = readable.ReadInt32BE(); - var rectH = readable.ReadInt32BE(); - Stylesheet.AddValue(key, new Bounds - { - X = rectX, - Y = rectY, - W = rectW, - H = rectH - }); - break; - case DataType.Color: - var color = readable.GpReadColor(true); - Stylesheet.AddValue(key, color); - break; - } - } - } - } -} diff --git a/Source/AlphaTab/Importer/Gp3To5Importer.cs b/Source/AlphaTab/Importer/Gp3To5Importer.cs deleted file mode 100644 index b4a4addbe..000000000 --- a/Source/AlphaTab/Importer/Gp3To5Importer.cs +++ /dev/null @@ -1,1442 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.Audio; -using AlphaTab.Collections; -using AlphaTab.Model; -using AlphaTab.Platform; -using AlphaTab.Platform.Model; -using AlphaTab.Util; -using AlphaTab.IO; - -namespace AlphaTab.Importer -{ - class Gp3To5Importer : ScoreImporter - { - private const string VersionString = "FICHIER GUITAR PRO "; - - private int _versionNumber; - - private Score _score; - - private TripletFeel _globalTripletFeel; - - private int _lyricsTrack; - private FastList _lyrics; - - private int _barCount; - private int _trackCount; - - private FastList _playbackInfos; - private string _encoding; - - public override string Name { get { return "Guitar Pro 3-5"; } } - - public override Score ReadScore() - { - _encoding = GetSetting("encoding", "utf-8"); - ReadVersion(); - - _score = new Score(); - - // basic song info - ReadScoreInformation(); - - // triplet feel before Gp5 - if (_versionNumber < 500) - { - _globalTripletFeel = Data.GpReadBool() ? TripletFeel.Triplet8th : TripletFeel.NoTripletFeel; - } - - // beat lyrics - if (_versionNumber >= 400) - { - ReadLyrics(); - } - - // rse master settings since GP5.1 - if (_versionNumber >= 510) - { - // master volume (4) - // master effect (4) - // master equalizer (10) - // master equalizer preset (1) - Data.Skip(19); - } - - // page setup since GP5 - if (_versionNumber >= 500) - { - ReadPageSetup(); - _score.TempoLabel = Data.GpReadStringIntByte(_encoding); - } - - // tempo stuff - _score.Tempo = Data.ReadInt32LE(); - if (_versionNumber >= 510) - { - Data.GpReadBool(); // hide tempo? - } - // keysignature and octave - /* var keySignature = */ - Data.ReadInt32LE(); - if (_versionNumber >= 400) - { - /* octave = */ - Data.ReadByte(); - } - - ReadPlaybackInfos(); - - // repetition stuff - if (_versionNumber >= 500) - { - // "Coda" bar index (2) - // "Double Coda" bar index (2) - // "Segno" bar index (2) - // "Segno Segno" bar index (2) - // "Fine" bar index (2) - // "Da Capo" bar index (2) - // "Da Capo al Coda" bar index (2) - // "Da Capo al Double Coda" bar index (2) - // "Da Capo al Fine" bar index (2) - // "Da Segno" bar index (2) - // "Da Segno al Coda" bar index (2) - // "Da Segno al Double Coda" bar index (2) - // "Da Segno al Fine "bar index (2) - // "Da Segno Segno" bar index (2) - // "Da Segno Segno al Coda" bar index (2) - // "Da Segno Segno al Double Coda" bar index (2) - // "Da Segno Segno al Fine" bar index (2) - // "Da Coda" bar index (2) - // "Da Double Coda" bar index (2) - Data.Skip(38); - // unknown (4) - Data.Skip(4); - } - - // contents - _barCount = Data.ReadInt32LE(); - _trackCount = Data.ReadInt32LE(); - - ReadMasterBars(); - ReadTracks(); - ReadBars(); - - _score.Finish(Settings); - if (_lyrics != null && _lyricsTrack >= 0) - { - _score.Tracks[_lyricsTrack].ApplyLyrics(_lyrics); - } - - return _score; - } - - public void ReadVersion() - { - string version = Data.GpReadStringByteLength(30, _encoding); - if (!version.StartsWith("FICHIER GUITAR PRO ")) - { - throw new UnsupportedFormatException(); - } - - version = version.Substring(VersionString.Length + 1); - int dot = version.IndexOf('.'); - - _versionNumber = (100 * Platform.Platform.ParseInt(version.Substring(0, dot))) + Platform.Platform.ParseInt(version.Substring(dot + 1)); - - Logger.Info(Name, "Guitar Pro version " + version + " detected"); - } - - public void ReadScoreInformation() - { - _score.Title = Data.GpReadStringIntUnused(_encoding); - _score.SubTitle = Data.GpReadStringIntUnused(_encoding); - _score.Artist = Data.GpReadStringIntUnused(_encoding); - _score.Album = Data.GpReadStringIntUnused(_encoding); - _score.Words = Data.GpReadStringIntUnused(_encoding); - - _score.Music = (_versionNumber >= 500) ? Data.GpReadStringIntUnused(_encoding) : _score.Words; - - _score.Copyright = Data.GpReadStringIntUnused(_encoding); - _score.Tab = Data.GpReadStringIntUnused(_encoding); - _score.Instructions = Data.GpReadStringIntUnused(_encoding); - int noticeLines = Data.ReadInt32LE(); - var notice = new StringBuilder(); - for (int i = 0; i < noticeLines; i++) - { - if (i > 0) notice.AppendLine(); - notice.Append(Data.GpReadStringIntUnused(_encoding)); - } - _score.Notices = notice.ToString(); - } - - public void ReadLyrics() - { - _lyrics = new FastList(); - - _lyricsTrack = Data.ReadInt32LE() - 1; - for (int i = 0; i < 5; i++) - { - var lyrics = new Lyrics(); - lyrics.StartBar = Data.ReadInt32LE() - 1; - lyrics.Text = Data.GpReadStringInt(_encoding); - _lyrics.Add(lyrics); - } - } - - public void ReadPageSetup() - { - // Page Width (4) - // Page Heigth (4) - // Padding Left (4) - // Padding Right (4) - // Padding Top (4) - // Padding Bottom (4) - // Size Proportion(4) - // Header and Footer display flags (2) - Data.Skip(30); - // title format - // subtitle format - // artist format - // album format - // words format - // music format - // words and music format - // copyright format - // pagpublic enumber format - for (int i = 0; i < 10; i++) - { - Data.GpReadStringIntByte(_encoding); - } - } - - public void ReadPlaybackInfos() - { - _playbackInfos = new FastList(); - for (int i = 0; i < 64; i++) - { - PlaybackInformation info = new PlaybackInformation(); - info.PrimaryChannel = i; - info.SecondaryChannel = i; - info.Program = Data.ReadInt32LE(); - - info.Volume = Data.ReadByte(); - info.Balance = Data.ReadByte(); - Data.Skip(6); - - _playbackInfos.Add(info); - } - } - - public void ReadMasterBars() - { - for (int i = 0; i < _barCount; i++) - { - ReadMasterBar(); - } - } - - public void ReadMasterBar() - { - MasterBar previousMasterBar = default(MasterBar); - if (_score.MasterBars.Count > 0) - { - previousMasterBar = _score.MasterBars[_score.MasterBars.Count - 1]; - } - - MasterBar newMasterBar = new MasterBar(); - int flags = Data.ReadByte(); - - // time signature - if ((flags & 0x01) != 0) - { - newMasterBar.TimeSignatureNumerator = Data.ReadByte(); - } - else if (previousMasterBar != null) - { - newMasterBar.TimeSignatureNumerator = previousMasterBar.TimeSignatureNumerator; - } - if ((flags & 0x02) != 0) - { - newMasterBar.TimeSignatureDenominator = Data.ReadByte(); - } - else if (previousMasterBar != null) - { - newMasterBar.TimeSignatureDenominator = previousMasterBar.TimeSignatureDenominator; - } - - // repeatings - newMasterBar.IsRepeatStart = (flags & 0x04) != 0; - if ((flags & 0x08) != 0) - { - newMasterBar.RepeatCount = Data.ReadByte() + (_versionNumber >= 500 ? 0 : 1); - } - - // alternate endings - if ((flags & 0x10) != 0) - { - if (_versionNumber < 500) - { - MasterBar currentMasterBar = previousMasterBar; - // get the already existing alternatives to ignore them - int existentAlternatives = 0; - while (currentMasterBar != null) - { - // found another repeat ending? - if (currentMasterBar.IsRepeatEnd && currentMasterBar != previousMasterBar) - break; - // found the opening? - if (currentMasterBar.IsRepeatStart) - break; - - existentAlternatives |= currentMasterBar.AlternateEndings; - - currentMasterBar = currentMasterBar.PreviousMasterBar; - } - - // now calculate the alternative for this bar - var repeatAlternative = 0; - var repeatMask = Data.ReadByte(); - for (var i = 0; i < 8; i++) - { - // only add the repeating if it is not existing - var repeating = (1 << i); - if (repeatMask > i && (existentAlternatives & repeating) == 0) - { - repeatAlternative |= repeating; - } - } - - - newMasterBar.AlternateEndings = (byte)repeatAlternative; - } - else - { - newMasterBar.AlternateEndings = (byte)Data.ReadByte(); - } - } - - // marker - if ((flags & 0x20) != 0) - { - Section section = new Section(); - section.Text = Data.GpReadStringIntByte(_encoding); - section.Marker = ""; - Data.GpReadColor(); - newMasterBar.Section = section; - } - - // keysignature - if ((flags & 0x40) != 0) - { - newMasterBar.KeySignature = (KeySignature)Data.ReadSignedByte(); - newMasterBar.KeySignatureType = (KeySignatureType)Data.ReadByte(); - } - else if (previousMasterBar != null) - { - newMasterBar.KeySignature = previousMasterBar.KeySignature; - newMasterBar.KeySignatureType = previousMasterBar.KeySignatureType; - } - - if ((_versionNumber >= 500) && ((flags & 0x03) != 0)) - { - Data.Skip(4); - } - - // better alternate ending mask in GP5 - if ((_versionNumber >= 500) && ((flags & 0x10) == 0)) - { - newMasterBar.AlternateEndings = (byte)Data.ReadByte(); - } - - // tripletfeel - if (_versionNumber >= 500) - { - var tripletFeel = Data.ReadByte(); - switch (tripletFeel) - { - case 1: - newMasterBar.TripletFeel = TripletFeel.Triplet8th; - break; - case 2: - newMasterBar.TripletFeel = TripletFeel.Triplet16th; - break; - } - - Data.ReadByte(); - } - else - { - newMasterBar.TripletFeel = _globalTripletFeel; - } - - newMasterBar.IsDoubleBar = (flags & 0x80) != 0; - _score.AddMasterBar(newMasterBar); - } - - public void ReadTracks() - { - for (int i = 0; i < _trackCount; i++) - { - ReadTrack(); - } - } - - public void ReadTrack() - { - var newTrack = new Track(1); - _score.AddTrack(newTrack); - - var mainStaff = newTrack.Staves[0]; - - var flags = Data.ReadByte(); - newTrack.Name = Data.GpReadStringByteLength(40, _encoding); - if ((flags & 0x01) != 0) - { - mainStaff.StaffKind = StaffKind.Percussion; - } - - var stringCount = Data.ReadInt32LE(); - var tuning = new FastList(); - for (int i = 0; i < 7; i++) - { - var stringTuning = Data.ReadInt32LE(); - if (stringCount > i) - { - tuning.Add(stringTuning); - } - } - mainStaff.Tuning = tuning.ToArray(); - - var port = Data.ReadInt32LE(); - var index = Data.ReadInt32LE() - 1; - var effectChannel = Data.ReadInt32LE() - 1; - Data.Skip(4); // Fretcount - if (index >= 0 && index < _playbackInfos.Count) - { - var info = _playbackInfos[index]; - info.Port = port; - info.IsSolo = (flags & 0x10) != 0; - info.IsMute = (flags & 0x20) != 0; - info.SecondaryChannel = effectChannel; - - if (GeneralMidi.IsGuitar(info.Program)) - { - mainStaff.DisplayTranspositionPitch = -12; - } - - newTrack.PlaybackInfo = info; - } - - mainStaff.Capo = Data.ReadInt32LE(); - newTrack.Color = Data.GpReadColor(); - - if (_versionNumber >= 500) - { - // flags for - // 0x01 -> show tablature - // 0x02 -> show standard notation - Data.ReadByte(); - // flags for - // 0x02 -> auto let ring - // 0x04 -> auto brush - Data.ReadByte(); - - // unknown - Data.Skip(43); - } - - // unknown - if (_versionNumber >= 510) - { - Data.Skip(4); - Data.GpReadStringIntByte(_encoding); - Data.GpReadStringIntByte(_encoding); - } - } - - public void ReadBars() - { - for (int i = 0; i < _barCount; i++) - { - for (int t = 0; t < _trackCount; t++) - { - ReadBar(_score.Tracks[t]); - } - } - } - - public void ReadBar(Track track) - { - var newBar = new Bar(); - var mainStaff = track.Staves[0]; - - if (mainStaff.StaffKind == StaffKind.Percussion) - { - newBar.Clef = Clef.Neutral; - } - - mainStaff.AddBar(newBar); - - var voiceCount = 1; - if (_versionNumber >= 500) - { - Data.ReadByte(); - voiceCount = 2; - } - - for (int v = 0; v < voiceCount; v++) - { - ReadVoice(track, newBar); - } - } - - public void ReadVoice(Track track, Bar bar) - { - var beatCount = Data.ReadInt32LE(); - if (beatCount == 0) - { - return; - } - - var newVoice = new Voice(); - bar.AddVoice(newVoice); - - for (int i = 0; i < beatCount; i++) - { - ReadBeat(track, bar, newVoice); - } - } - - public void ReadBeat(Track track, Bar bar, Voice voice) - { - var newBeat = new Beat(); - var flags = Data.ReadByte(); - - if ((flags & 0x01) != 0) - { - newBeat.Dots = 1; - } - - if ((flags & 0x40) != 0) - { - var type = Data.ReadByte(); - newBeat.IsEmpty = (type & 0x02) == 0; - } - voice.AddBeat(newBeat); - - var duration = Data.ReadSignedByte(); - switch (duration) - { - case -2: - newBeat.Duration = Duration.Whole; - break; - case -1: - newBeat.Duration = Duration.Half; - break; - case 0: - newBeat.Duration = Duration.Quarter; - break; - case 1: - newBeat.Duration = Duration.Eighth; - break; - case 2: - newBeat.Duration = Duration.Sixteenth; - break; - case 3: - newBeat.Duration = Duration.ThirtySecond; - break; - case 4: - newBeat.Duration = Duration.SixtyFourth; - break; - default: - newBeat.Duration = Duration.Quarter; - break; - } - - if ((flags & 0x20) != 0) - { - newBeat.TupletNumerator = Data.ReadInt32LE(); - switch (newBeat.TupletNumerator) - { - case 1: - newBeat.TupletDenominator = 1; - break; - case 3: - newBeat.TupletDenominator = 2; - break; - case 5: - case 6: - case 7: - newBeat.TupletDenominator = 4; - break; - case 9: - case 10: - case 11: - case 12: - case 13: - newBeat.TupletDenominator = 8; - break; - case 2: - case 4: - case 8: - break; - default: - newBeat.TupletNumerator = 1; - newBeat.TupletDenominator = 1; - break; - } - } - - if ((flags & 0x02) != 0) - { - ReadChord(newBeat); - } - - if ((flags & 0x04) != 0) - { - newBeat.Text = Data.GpReadStringIntUnused(_encoding); - } - - if ((flags & 0x08) != 0) - { - ReadBeatEffects(newBeat); - } - - if ((flags & 0x10) != 0) - { - ReadMixTableChange(newBeat); - } - - var stringFlags = Data.ReadByte(); - for (int i = 6; i >= 0; i--) - { - if ((stringFlags & (1 << i)) != 0 && (6 - i) < bar.Staff.Tuning.Length) - { - ReadNote(track, bar, voice, newBeat, (6 - i)); - } - } - - if (_versionNumber >= 500) - { - Data.ReadByte(); - var flag = Data.ReadByte(); - if ((flag & 0x08) != 0) - { - Data.ReadByte(); - } - } - } - - public void ReadChord(Beat beat) - { - var chord = new Chord(); - var chordId = Platform.Platform.NewGuid(); - if (_versionNumber >= 500) - { - Data.Skip(17); - chord.Name = Data.GpReadStringByteLength(21, _encoding); - Data.Skip(4); - chord.FirstFret = Data.ReadInt32LE(); - for (int i = 0; i < 7; i++) - { - var fret = Data.ReadInt32LE(); - if (i < beat.Voice.Bar.Staff.Tuning.Length) - { - chord.Strings.Add(fret); - } - } - - var numberOfBarres = Data.ReadByte(); - var barreFrets = new byte[5]; - Data.Read(barreFrets, 0, barreFrets.Length); - for (int i = 0; i < numberOfBarres; i++) - { - chord.BarreFrets.Add(barreFrets[i]); - } - - Data.Skip(26); - } - else - { - if (Data.ReadByte() != 0) // mode1? - { - // gp4 - if (_versionNumber >= 400) - { - // Sharp (1) - // Unused (3) - // Root (1) - // Major/Minor (1) - // Nin,Eleven or Thirteen (1) - // Bass (4) - // Diminished/Augmented (4) - // Add (1) - Data.Skip(16); - chord.Name = Data.GpReadStringByteLength(21, _encoding); - // Unused (2) - // Fifth (1) - // Ninth (1) - // Eleventh (1) - Data.Skip(4); - chord.FirstFret = (Data.ReadInt32LE()); - for (int i = 0; i < 7; i++) - { - var fret = Data.ReadInt32LE(); - if (i < beat.Voice.Bar.Staff.Tuning.Length) - { - chord.Strings.Add(fret); - } - } - - var numberOfBarres = Data.ReadByte(); - var barreFrets = new byte[5]; - Data.Read(barreFrets, 0, barreFrets.Length); - for (int i = 0; i < numberOfBarres; i++) - { - chord.BarreFrets.Add(barreFrets[i]); - } - - // Barree end (5) - // Omission1,3,5,7,9,11,13 (7) - // Unused (1) - // Fingering (7) - // Show Diagram Fingering (1) - // ?? - Data.Skip(26); - } - else - { - // unknown - Data.Skip(25); - chord.Name = Data.GpReadStringByteLength(34, _encoding); - chord.FirstFret = Data.ReadInt32LE(); - for (int i = 0; i < 6; i++) - { - var fret = Data.ReadInt32LE(); - if (i < beat.Voice.Bar.Staff.Tuning.Length) - { - chord.Strings.Add(fret); - } - } - // unknown - Data.Skip(36); - } - } - else - { - int strings = _versionNumber >= 406 ? 7 : 6; - - chord.Name = Data.GpReadStringIntByte(_encoding); - chord.FirstFret = Data.ReadInt32LE(); - if (chord.FirstFret > 0) - { - for (int i = 0; i < strings; i++) - { - var fret = Data.ReadInt32LE(); - if (i < beat.Voice.Bar.Staff.Tuning.Length) - { - chord.Strings.Add(fret); - } - } - } - } - } - - - if (!string.IsNullOrEmpty(chord.Name)) - { - beat.ChordId = chordId; - beat.Voice.Bar.Staff.Chords[beat.ChordId] = chord; - } - } - - public void ReadBeatEffects(Beat beat) - { - var flags = Data.ReadByte(); - var flags2 = 0; - if (_versionNumber >= 400) - { - flags2 = Data.ReadByte(); - } - - beat.FadeIn = (flags & 0x10) != 0; - if ( (_versionNumber < 400 && (flags & 0x01) != 0) || (flags & 0x02) != 0) - { - beat.Vibrato = VibratoType.Slight; - } - beat.HasRasgueado = (flags2 & 0x01) != 0; - - if ((flags & 0x20) != 0 && _versionNumber >= 400) - { - var slapPop = Data.ReadSignedByte(); - switch (slapPop) - { - case 1: - beat.Tap = true; - break; - case 2: - beat.Slap = true; - break; - case 3: - beat.Pop = true; - break; - } - } - else if ((flags & 0x20) != 0) - { - var slapPop = Data.ReadSignedByte(); - switch (slapPop) - { - case 1: - beat.Tap = true; - break; - case 2: - beat.Slap = true; - break; - case 3: - beat.Pop = true; - break; - } - Data.Skip(4); - } - - if ((flags2 & 0x04) != 0) - { - ReadTremoloBarEffect(beat); - } - - if ((flags & 0x40) != 0) - { - int strokeUp; - int strokeDown; - - if (_versionNumber < 500) - { - strokeDown = Data.ReadByte(); - strokeUp = Data.ReadByte(); - } - else - { - strokeUp = Data.ReadByte(); - strokeDown = Data.ReadByte(); - } - - if (strokeUp > 0) - { - beat.BrushType = BrushType.BrushUp; - beat.BrushDuration = ToStrokeValue(strokeUp); - } - else if (strokeDown > 0) - { - beat.BrushType = BrushType.BrushDown; - beat.BrushDuration = ToStrokeValue(strokeDown); - } - } - - if ((flags2 & 0x02) != 0) - { - switch (Data.ReadSignedByte()) - { - case 0: - beat.PickStroke = PickStroke.None; - break; - case 1: - beat.PickStroke = PickStroke.Up; - break; - case 2: - beat.PickStroke = PickStroke.Down; - break; - } - } - } - - public void ReadTremoloBarEffect(Beat beat) - { - Data.ReadByte(); // type - Data.ReadInt32LE(); // value - var pointCount = Data.ReadInt32LE(); - if (pointCount > 0) - { - for (int i = 0; i < pointCount; i++) - { - var point = new BendPoint(); - point.Offset = Data.ReadInt32LE(); // 0...60 - point.Value = Data.ReadInt32LE() / BendStep; // 0..12 (amount of quarters) - Data.GpReadBool(); // vibrato - beat.AddWhammyBarPoint(point); - } - } - } - - private static int ToStrokeValue(int value) - { - switch (value) - { - case 1: - return 30; - case 2: - return 30; - case 3: - return 60; - case 4: - return 120; - case 5: - return 240; - case 6: - return 480; - default: - return 0; - } - } - - public void ReadMixTableChange(Beat beat) - { - var tableChange = new MixTableChange(); - tableChange.Instrument = Data.ReadSignedByte(); - if (_versionNumber >= 500) - { - Data.Skip(16); // Rse Info - } - tableChange.Volume = Data.ReadSignedByte(); - tableChange.Balance = Data.ReadSignedByte(); - var chorus = Data.ReadSignedByte(); - var reverb = Data.ReadSignedByte(); - var phaser = Data.ReadSignedByte(); - var tremolo = Data.ReadSignedByte(); - if (_versionNumber >= 500) - { - tableChange.TempoName = Data.GpReadStringIntByte(_encoding); - } - tableChange.Tempo = Data.ReadInt32LE(); - - // durations - if (tableChange.Volume >= 0) - { - Data.ReadByte(); - } - - if (tableChange.Balance >= 0) - { - Data.ReadByte(); - } - - if (chorus >= 0) - { - Data.ReadByte(); - } - - if (reverb >= 0) - { - Data.ReadByte(); - } - - if (phaser >= 0) - { - Data.ReadByte(); - } - - if (tremolo >= 0) - { - Data.ReadByte(); - } - - if (tableChange.Tempo >= 0) - { - tableChange.Duration = Data.ReadSignedByte(); - if (_versionNumber >= 510) - { - Data.ReadByte(); // hideTempo (bool) - } - } - - if (_versionNumber >= 400) - { - Data.ReadByte(); // all tracks flag - } - - // unknown - if (_versionNumber >= 500) - { - Data.ReadByte(); - } - // unknown - if (_versionNumber >= 510) - { - Data.GpReadStringIntByte(_encoding); - Data.GpReadStringIntByte(_encoding); - } - - if (tableChange.Volume >= 0) - { - var volumeAutomation = new Automation(); - volumeAutomation.IsLinear = true; - volumeAutomation.Type = AutomationType.Volume; - volumeAutomation.Value = tableChange.Volume; - beat.Automations.Add(volumeAutomation); - } - - if (tableChange.Balance >= 0) - { - var balanceAutomation = new Automation(); - balanceAutomation.IsLinear = true; - balanceAutomation.Type = AutomationType.Balance; - balanceAutomation.Value = tableChange.Balance; - beat.Automations.Add(balanceAutomation); - } - - if (tableChange.Instrument >= 0) - { - var instrumentAutomation = new Automation(); - instrumentAutomation.IsLinear = true; - instrumentAutomation.Type = AutomationType.Instrument; - instrumentAutomation.Value = tableChange.Instrument; - beat.Automations.Add(instrumentAutomation); - } - - if (tableChange.Tempo >= 0) - { - var tempoAutomation = new Automation(); - tempoAutomation.IsLinear = true; - tempoAutomation.Type = AutomationType.Tempo; - tempoAutomation.Value = tableChange.Tempo; - beat.Automations.Add(tempoAutomation); - - beat.Voice.Bar.MasterBar.TempoAutomation = tempoAutomation; - } - } - - public void ReadNote(Track track, Bar bar, Voice voice, Beat beat, int stringIndex) - { - var newNote = new Note(); - newNote.String = bar.Staff.Tuning.Length - stringIndex; - - var flags = Data.ReadByte(); - if ((flags & 0x02) != 0) - { - newNote.Accentuated = AccentuationType.Heavy; - } - else if ((flags & 0x40) != 0) - { - newNote.Accentuated = AccentuationType.Normal; - } - - newNote.IsGhost = ((flags & 0x04) != 0); - if ((flags & 0x20) != 0) - { - var noteType = Data.ReadByte(); - if (noteType == 3) - { - newNote.IsDead = true; - } - else if (noteType == 2) - { - newNote.IsTieDestination = true; - } - } - - if ((flags & 0x01) != 0 && _versionNumber < 500) - { - Data.ReadByte(); // duration - Data.ReadByte(); // tuplet - } - - if ((flags & 0x10) != 0) - { - var dynamicNumber = Data.ReadSignedByte(); - newNote.Dynamic = ToDynamicValue(dynamicNumber); - beat.Dynamic = newNote.Dynamic; - } - - if ((flags & 0x20) != 0) - { - newNote.Fret = Data.ReadSignedByte(); - } - - if ((flags & 0x80) != 0) - { - newNote.LeftHandFinger = (Fingers)Data.ReadSignedByte(); - newNote.RightHandFinger = (Fingers)Data.ReadSignedByte(); - newNote.IsFingering = true; - } - - if (_versionNumber >= 500) - { - if ((flags & 0x01) != 0) - { - newNote.DurationPercent = Data.GpReadDouble(); - } - var flags2 = Data.ReadByte(); - newNote.AccidentalMode = (flags2 & 0x02) != 0 - ? NoteAccidentalMode.SwapAccidentals - : NoteAccidentalMode.Default; - } - - beat.AddNote(newNote); - if ((flags & 0x08) != 0) - { - ReadNoteEffects(track, voice, beat, newNote); - } - } - - public DynamicValue ToDynamicValue(int value) - { - switch (value) - { - case 1: - return DynamicValue.PPP; - case 2: - return DynamicValue.PP; - case 3: - return DynamicValue.P; - case 4: - return DynamicValue.MP; - case 5: - return DynamicValue.MF; - case 6: - return DynamicValue.F; - case 7: - return DynamicValue.FF; - case 8: - return DynamicValue.FFF; - default: - return DynamicValue.F; - } - } - - public void ReadNoteEffects(Track track, Voice voice, Beat beat, Note note) - { - var flags = Data.ReadByte(); - var flags2 = 0; - if (_versionNumber >= 400) - { - flags2 = Data.ReadByte(); - } - - if ((flags & 0x01) != 0) - { - ReadBend(note); - } - - if ((flags & 0x10) != 0) - { - ReadGrace(voice, note); - } - - if ((flags2 & 0x04) != 0) - { - ReadTremoloPicking(beat); - } - - if ((flags2 & 0x08) != 0) - { - ReadSlide(note); - } - else if (_versionNumber < 400) - { - if ((flags & 0x04) != 0) - { - note.SlideType = SlideType.Shift; - } - } - - if ((flags2 & 0x10) != 0) - { - ReadArtificialHarmonic(note); - } - else if (_versionNumber < 400) - { - if ((flags & 0x04) != 0) - { - note.HarmonicType = HarmonicType.Natural; - note.HarmonicValue = DeltaFretToHarmonicValue(note.Fret); - } - if ((flags & 0x08) != 0) - { - note.HarmonicType = HarmonicType.Artificial; - } - } - - if ((flags2 & 0x20) != 0) - { - ReadTrill(note); - } - - note.IsLetRing = (flags & 0x08) != 0; - note.IsHammerPullOrigin = (flags & 0x02) != 0; - if ((flags2 & 0x40) != 0) - { - note.Vibrato = VibratoType.Slight; - } - note.IsPalmMute = (flags2 & 0x02) != 0; - note.IsStaccato = (flags2 & 0x01) != 0; - } - - private const int BendStep = 25; - public void ReadBend(Note note) - { - Data.ReadByte(); // type - Data.ReadInt32LE(); // value - var pointCount = Data.ReadInt32LE(); - if (pointCount > 0) - { - for (int i = 0; i < pointCount; i++) - { - var point = new BendPoint(); - point.Offset = Data.ReadInt32LE(); // 0...60 - point.Value = Data.ReadInt32LE() / BendStep; // 0..12 (amount of quarters) - Data.GpReadBool(); // vibrato - note.AddBendPoint(point); - } - } - } - - public void ReadGrace(Voice voice, Note note) - { - var graceBeat = new Beat(); - var graceNote = new Note(); - graceNote.String = note.String; - graceNote.Fret = Data.ReadSignedByte(); - graceBeat.Duration = Duration.ThirtySecond; - graceBeat.Dynamic = ToDynamicValue(Data.ReadSignedByte()); - var transition = Data.ReadSignedByte(); - switch (transition) - { - case 0: // none - break; - case 1: - graceNote.SlideType = SlideType.Legato; - graceNote.SlideTarget = note; - break; - case 2: // bend - break; - case 3: // hammer - graceNote.IsHammerPullOrigin = true; - break; - } - graceNote.Dynamic = graceBeat.Dynamic; - Data.Skip(1); // duration - - if (_versionNumber < 500) - { - graceBeat.GraceType = GraceType.BeforeBeat; - } - else - { - var flags = Data.ReadByte(); - graceNote.IsDead = (flags & 0x01) != 0; - graceBeat.GraceType = (flags & 0x02) != 0 ? GraceType.OnBeat : GraceType.BeforeBeat; - } - - graceBeat.AddNote(graceNote); - voice.AddGraceBeat(graceBeat); - } - - public void ReadTremoloPicking(Beat beat) - { - var speed = Data.ReadByte(); - switch (speed) - { - case 1: - beat.TremoloSpeed = Duration.Eighth; - break; - case 2: - beat.TremoloSpeed = Duration.Sixteenth; - break; - case 3: - beat.TremoloSpeed = Duration.ThirtySecond; - break; - } - } - - public void ReadSlide(Note note) - { - if (_versionNumber >= 500) - { - var type = Data.ReadSignedByte(); - switch (type) - { - case 1: - note.SlideType = SlideType.Shift; - break; - case 2: - note.SlideType = SlideType.Legato; - break; - case 4: - note.SlideType = SlideType.OutDown; - break; - case 8: - note.SlideType = SlideType.OutUp; - break; - case 16: - note.SlideType = SlideType.IntoFromBelow; - break; - case 32: - note.SlideType = SlideType.IntoFromAbove; - break; - default: - note.SlideType = SlideType.None; - break; - } - } - else - { - var type = Data.ReadSignedByte(); - switch (type) - { - case 1: - note.SlideType = SlideType.Shift; - break; - case 2: - note.SlideType = SlideType.Legato; - break; - case 3: - note.SlideType = SlideType.OutDown; - break; - case 4: - note.SlideType = SlideType.OutUp; - break; - case -1: - note.SlideType = SlideType.IntoFromBelow; - break; - case -2: - note.SlideType = SlideType.IntoFromAbove; - break; - default: - note.SlideType = SlideType.None; - break; - } - } - } - - public void ReadArtificialHarmonic(Note note) - { - var type = Data.ReadByte(); - if (_versionNumber >= 500) - { - switch (type) - { - case 1: - note.HarmonicType = HarmonicType.Natural; - note.HarmonicValue = DeltaFretToHarmonicValue(note.Fret); - break; - case 2: - // ReSharper disable UnusedVariable - var harmonicTone = Data.ReadByte(); - var harmonicKey = Data.ReadByte(); - var harmonicOctaveOffset = Data.ReadByte(); - note.HarmonicType = HarmonicType.Artificial; - // ReSharper restore UnusedVariable - break; - // TODO: how to calculate the harmonic value? - case 3: - note.HarmonicType = HarmonicType.Tap; - note.HarmonicValue = DeltaFretToHarmonicValue(Data.ReadByte()); - break; - case 4: - note.HarmonicType = HarmonicType.Pinch; - note.HarmonicValue = 12; - break; - case 5: - note.HarmonicType = HarmonicType.Semi; - note.HarmonicValue = 12; - break; - } - } - else if (_versionNumber >= 400) - { - switch (type) - { - case 1: - note.HarmonicType = HarmonicType.Natural; - break; - case 3: - note.HarmonicType = HarmonicType.Tap; - break; - case 4: - note.HarmonicType = HarmonicType.Pinch; - break; - case 5: - note.HarmonicType = HarmonicType.Semi; - break; - case 15: - note.HarmonicType = HarmonicType.Artificial; - break; - case 17: - note.HarmonicType = HarmonicType.Artificial; - break; - case 22: - note.HarmonicType = HarmonicType.Artificial; - break; - } - } - } - - public float DeltaFretToHarmonicValue(int deltaFret) - { - switch (deltaFret) - { - case 2: - return 2.4f; - case 3: - return 3.2f; - case 4: - case 5: - case 7: - case 9: - case 12: - case 16: - case 17: - case 19: - case 24: - return deltaFret; - case 8: - return 8.2f; - case 10: - return 9.6f; - case 14: - case 15: - return 14.7f; - case 21: - case 22: - return 21.7f; - default: - return 12; - } - } - - public void ReadTrill(Note note) - { - note.TrillValue = Data.ReadByte() + note.StringTuning; - switch (Data.ReadByte()) - { - case 1: - note.TrillSpeed = Duration.Sixteenth; - break; - case 2: - note.TrillSpeed = Duration.ThirtySecond; - break; - case 3: - note.TrillSpeed = Duration.SixtyFourth; - break; - } - } - - } -} diff --git a/Source/AlphaTab/Importer/Gp7Importer.cs b/Source/AlphaTab/Importer/Gp7Importer.cs deleted file mode 100644 index 4741e8cd3..000000000 --- a/Source/AlphaTab/Importer/Gp7Importer.cs +++ /dev/null @@ -1,94 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using AlphaTab.IO; -using AlphaTab.Model; -using AlphaTab.Platform; -using AlphaTab.Util; - -namespace AlphaTab.Importer -{ - /// - /// This ScoreImporter can read Guitar Pro 7 (gp) files. - /// - class Gp7Importer : ScoreImporter - { - public override string Name { get { return "Guitar Pro 7"; } } - - public override Score ReadScore() - { - // at first we need to load the binary file system - // from the GPX container - Logger.Info(Name, "Loading ZIP entries"); - var fileSystem = new ZipFile(); - fileSystem.FileFilter = s => s.EndsWith(GpxFileSystem.ScoreGpif) || s.EndsWith(GpxFileSystem.BinaryStylesheet); - try - { - fileSystem.Load(Data); - } - catch (Exception e) - { - throw new UnsupportedFormatException(e.Message); - } - Logger.Info(Name, "Zip entries loaded"); - - string xml = null; - byte[] binaryStylesheet = null; - foreach (var entry in fileSystem.Entries) - { - switch (entry.FileName) - { - case GpxFileSystem.ScoreGpif: - xml = Platform.Platform.ToString(entry.Data, GetSetting("encoding", "utf-8")); - break; - case GpxFileSystem.BinaryStylesheet: - binaryStylesheet = entry.Data; - break; - } - } - - // lets set the fileSystem to null, maybe the garbage collector will come along - // and kick the fileSystem binary data before we finish parsing - fileSystem.Entries = null; - fileSystem = null; - - // the score.gpif file within this filesystem stores - // the score information as XML we need to parse. - Logger.Info(Name, "Start Parsing score.gpif"); - var gpifParser = new GpifParser(); - gpifParser.ParseXml(xml, Settings); - Logger.Info(Name, "score.gpif parsed"); - - var score = gpifParser.Score; - if (binaryStylesheet != null) - { - Logger.Info(Name, "Start Parsing BinaryStylesheet"); - var stylesheetParser = new BinaryStylesheetParser(); - stylesheetParser.Parse(binaryStylesheet); - if (stylesheetParser.Stylesheet != null) - { - stylesheetParser.Stylesheet.Apply(score); - } - Logger.Info(Name, "BinaryStylesheet parsed"); - } - - return score; - } - } -} diff --git a/Source/AlphaTab/Importer/GpBinaryHelpers.cs b/Source/AlphaTab/Importer/GpBinaryHelpers.cs deleted file mode 100644 index 4eb50d19f..000000000 --- a/Source/AlphaTab/Importer/GpBinaryHelpers.cs +++ /dev/null @@ -1,104 +0,0 @@ -using AlphaTab.IO; -using AlphaTab.Platform.Model; - -namespace AlphaTab.Importer -{ - static class GpBinaryHelpers - { - public static double GpReadDouble(this IReadable data) - { - var bytes = new byte[8]; - data.Read(bytes, 0, bytes.Length); - return Platform.Platform.ToDouble(bytes); - } - - public static float GpReadFloat(this IReadable data) - { - var bytes = new byte[4]; - bytes[3] = (byte)data.ReadByte(); - bytes[2] = (byte)data.ReadByte(); - bytes[2] = (byte)data.ReadByte(); - bytes[1] = (byte)data.ReadByte(); - return Platform.Platform.ToFloat(bytes); - } - - public static Color GpReadColor(this IReadable data, bool readAlpha = false) - { - byte r = (byte)data.ReadByte(); - byte g = (byte)data.ReadByte(); - byte b = (byte)data.ReadByte(); - byte a = 255; - if (readAlpha) - { - a = (byte) data.ReadByte(); - } - else - { - data.Skip(1); - } - return new Color(r, g, b, a); - } - - - public static bool GpReadBool(this IReadable data) - { - return data.ReadByte() != 0; - } - - /// - /// Skips an integer (4byte) and reads a string using - /// a bytesize - /// - /// - public static string GpReadStringIntUnused(this IReadable data, string encoding) - { - data.Skip(4); - return data.GpReadString(data.ReadByte(), encoding); - } - - /// - /// Reads an integer as size, and then the string itself - /// - /// - public static string GpReadStringInt(this IReadable data, string encoding) - { - return data.GpReadString(data.ReadInt32LE(), encoding); - } - - /// - /// Reads an integer as size, skips a byte and reads the string itself - /// - /// - public static string GpReadStringIntByte(this IReadable data, string encoding) - { - var length = data.ReadInt32LE() - 1; - data.ReadByte(); - return data.GpReadString(length, encoding); - } - - public static string GpReadString(this IReadable data, int length, string encoding) - { - byte[] b = new byte[length]; - data.Read(b, 0, b.Length); - return Platform.Platform.ToString(b, encoding); - } - - /// - /// Reads a byte as size and the string itself. - /// Additionally it is ensured the specified amount of bytes is read. - /// - /// the data to read from. - /// the amount of bytes to read - /// - public static string GpReadStringByteLength(this IReadable data, int length, string encoding) - { - var stringLength = data.ReadByte(); - var s = data.GpReadString(stringLength, encoding); - if (stringLength < length) - { - data.Skip(length - stringLength); - } - return s; - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Importer/GpifParser.cs b/Source/AlphaTab/Importer/GpifParser.cs deleted file mode 100644 index 3e468629a..000000000 --- a/Source/AlphaTab/Importer/GpifParser.cs +++ /dev/null @@ -1,2122 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using System.Diagnostics; -using AlphaTab.Audio; -using AlphaTab.Collections; -using AlphaTab.Model; -using AlphaTab.Platform; -using AlphaTab.Platform.Model; -using AlphaTab.Util; -using AlphaTab.Xml; - -namespace AlphaTab.Importer -{ - /// - /// This structure represents a duration within a gpif model. - /// - class GpifRhythm - { - public int Dots { get; set; } - public int TupletDenominator { get; set; } - public int TupletNumerator { get; set; } - public Duration Value { get; set; } - - public GpifRhythm() - { - TupletDenominator = -1; - TupletNumerator = -1; - Value = Duration.Quarter; - } - } - - /// - /// This public class can parse a score.gpif xml file into the model structure - /// - class GpifParser - { - private const string InvalidId = "-1"; - /// - /// GPX range: 0-100 - /// Internal range: 0 - 60 - /// - private const float BendPointPositionFactor = 60.0f / 100.0f; - /// - /// GPIF: 25 per quarternote - /// Internal Range: 1 per quarter note - /// - private const double BendPointValueFactor = 1 / 25.0; - - public Score Score { get; set; } - - - private FastDictionary> _masterTrackAutomations; - private string[] _tracksMapping; - private FastDictionary _tracksById; // contains tracks by their id - - private FastList _masterBars; // contains all masterbars in correct order - private FastList _barsOfMasterBar; // contains ids of bars placed at this masterbar over all tracks (sequencially) - - private FastDictionary _barsById; // contains bars by their id - private FastDictionary _voicesOfBar; // contains ids of voices stored in a bar (key = bar id) - - private FastDictionary _voiceById; // contains voices by their id - private FastDictionary _beatsOfVoice; // contains ids of beats stored in a voice (key = voice id) - - private FastDictionary _rhythmOfBeat; // contains ids of rhythm used by a beat (key = beat id) - private FastDictionary _beatById; // contains beats by their id - - private FastDictionary _rhythmById; // contains rhythms by their id - - private FastDictionary _noteById; // contains notes by their id - private FastDictionary _notesOfBeat; // contains ids of notes stored in a beat (key = beat id); - private FastDictionary _tappedNotes; // contains a flag indicating whether a note is tapped (key = note id); - - private FastDictionary> _lyricsByTrack; - - public void ParseXml(string xml, Settings settings) - { - _masterTrackAutomations = new FastDictionary>(); - _tracksMapping = new string[0]; - _tracksById = new FastDictionary(); - _masterBars = new FastList(); - _barsOfMasterBar = new FastList(); - _voicesOfBar = new FastDictionary(); - _barsById = new FastDictionary(); - _voiceById = new FastDictionary(); - _beatsOfVoice = new FastDictionary(); - _beatById = new FastDictionary(); - _rhythmOfBeat = new FastDictionary(); - _rhythmById = new FastDictionary(); - _notesOfBeat = new FastDictionary(); - _noteById = new FastDictionary(); - _tappedNotes = new FastDictionary(); - _lyricsByTrack = new FastDictionary>(); - - XmlDocument dom; - try - { - dom = new XmlDocument(xml); - } - catch (Exception) - { - throw new UnsupportedFormatException(); - } - ParseDom(dom); - - BuildModel(); - - Score.Finish(settings); - - if (_lyricsByTrack.Count > 0) - { - foreach (var trackId in _lyricsByTrack) - { - var track = _tracksById[trackId]; - track.ApplyLyrics(_lyricsByTrack[trackId]); - } - } - } - - #region Xml Parsing - - private void ParseDom(XmlDocument dom) - { - var root = dom.DocumentElement; - if (root == null) return; - - // the XML uses IDs for referring elements within the - // model. Therefore we do the parsing in 2 steps: - // - at first we read all model elements and store them by ID in a lookup table - // - after that we need to join up the information. - if (root.LocalName == "GPIF") - { - Score = new Score(); - - // parse all children - foreach (var n in root.ChildNodes) - { - if (n.NodeType == XmlNodeType.Element) - { - switch (n.LocalName) - { - case "Score": - ParseScoreNode(n); - break; - case "MasterTrack": - ParseMasterTrackNode(n); - break; - case "Tracks": - ParseTracksNode(n); - break; - case "MasterBars": - ParseMasterBarsNode(n); - break; - case "Bars": - ParseBars(n); - break; - case "Voices": - ParseVoices(n); - break; - case "Beats": - ParseBeats(n); - break; - case "Notes": - ParseNotes(n); - break; - case "Rhythms": - ParseRhythms(n); - break; - } - } - } - } - else - { - throw new UnsupportedFormatException(); - } - } - - // - // ... - // - - private void ParseScoreNode(XmlNode element) - { - foreach (var c in element.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "Title": - Score.Title = c.FirstChild.InnerText; - break; - case "SubTitle": - Score.SubTitle = c.FirstChild.InnerText; - break; - case "Artist": - Score.Artist = c.FirstChild.InnerText; - break; - case "Album": - Score.Album = c.FirstChild.InnerText; - break; - case "Words": - Score.Words = c.FirstChild.InnerText; - break; - case "Music": - Score.Music = c.FirstChild.InnerText; - break; - case "WordsAndMusic": - if (c.FirstChild != null && c.FirstChild.InnerText != "") - { - var wordsAndMusic = c.FirstChild.InnerText; - if (!string.IsNullOrEmpty(wordsAndMusic) && string.IsNullOrEmpty(Score.Words)) - { - Score.Words = wordsAndMusic; - } - if (!string.IsNullOrEmpty(wordsAndMusic) && string.IsNullOrEmpty(Score.Music)) - { - Score.Music = wordsAndMusic; - } - } - break; - case "Copyright": - Score.Copyright = c.FirstChild.InnerText; - break; - case "Tabber": - Score.Tab = c.FirstChild.InnerText; - break; - case "Instructions": - Score.Instructions = c.FirstChild.InnerText; - break; - case "Notices": - Score.Notices = c.FirstChild.InnerText; - break; - } - } - } - } - - // - // ... - // - - private void ParseMasterTrackNode(XmlNode node) - { - foreach (var c in node.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "Automations": - ParseAutomations(c, _masterTrackAutomations); - break; - case "Tracks": - _tracksMapping = c.InnerText.Split(' '); - break; - } - } - } - } - - private void ParseAutomations(XmlNode node, FastDictionary> automations) - { - foreach (var c in node.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "Automation": - ParseAutomation(c, automations); - break; - } - } - } - } - - private void ParseAutomation(XmlNode node, FastDictionary> automations) - { - string type = null; - bool isLinear = false; - string barId = null; - float ratioPosition = 0; - float value = 0; - int reference = 0; - string text = null; - - foreach (var c in node.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "Type": - type = c.InnerText; - break; - case "Linear": - isLinear = c.InnerText.ToLower() == "true"; - break; - case "Bar": - barId = c.InnerText; - break; - case "Position": - ratioPosition = Platform.Platform.ParseFloat(c.InnerText); - break; - case "Value": - var parts = c.InnerText.Split(' '); - value = Platform.Platform.ParseFloat(parts[0]); - reference = Platform.Platform.ParseInt(parts[1]); - break; - case "Text": - text = c.InnerText; - break; - } - } - } - - if (type == null) return; - Automation automation = null; - switch (type) - { - case "Tempo": - automation = Automation.BuildTempoAutomation(isLinear, ratioPosition, value, reference); - break; - // TODO: other automations - } - - if (automation != null) - { - automation.Text = text; - } - - if (barId != null) - { - if (!automations.ContainsKey(barId)) - { - automations[barId] = new FastList(); - } - automations[barId].Add(automation); - } - } - - // - // ... - // - - private void ParseTracksNode(XmlNode node) - { - foreach (var c in node.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "Track": - ParseTrack(c); - break; - } - } - } - } - - private void ParseTrack(XmlNode node) - { - var track = new Track(1); - var staff = track.Staves[0]; - staff.StaffKind = StaffKind.Score; - var trackId = node.GetAttribute("id"); - - foreach (var c in node.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "Name": - track.Name = c.InnerText; - break; - case "Color": - var parts = c.InnerText.Split(' '); - if (parts.Length >= 3) - { - var r = Platform.Platform.ParseInt(parts[0]); - var g = Platform.Platform.ParseInt(parts[1]); - var b = Platform.Platform.ParseInt(parts[2]); - track.Color = new Color((byte)r, (byte)g, (byte)b); - } - break; - case "Instrument": - var instrumentName = c.GetAttribute("ref"); - if (instrumentName.EndsWith("-gs") || instrumentName.EndsWith("GrandStaff")) - { - track.EnsureStaveCount(2); - track.Staves[1].StaffKind = StaffKind.Score; - } - break; - case "InstrumentSet": - ParseInstrumentSet(track, c); - break; - case "ShortName": - track.ShortName = c.InnerText; - break; - case "Lyrics": - ParseLyrics(trackId, c); - break; - case "Properties": - ParseTrackProperties(track, c); - break; - case "GeneralMidi": - case "MidiConnection": - ParseGeneralMidi(track, c); - break; - case "Sounds": - ParseSounds(track, c); - break; - case "PlaybackState": - var state = c.InnerText; - track.PlaybackInfo.IsSolo = state == "Solo"; - track.PlaybackInfo.IsMute = state == "Mute"; - break; - case "PartSounding": - ParsePartSounding(track, c); - break; - case "Staves": - ParseStaves(track, c); - break; - case "Transpose": - ParseTranspose(track, c); - break; - } - } - } - - _tracksById[trackId] = track; - } - - private void ParseInstrumentSet(Track track, XmlNode node) - { - int staffIndex = 0; - foreach (var c in node.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "Type": - if (c.InnerText == "drumKit") - { - foreach (var staff in track.Staves) - { - staff.StaffKind = StaffKind.Percussion; - } - } - break; - } - } - } - } - - private void ParseStaves(Track track, XmlNode node) - { - int staffIndex = 0; - foreach (var c in node.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "Staff": - track.EnsureStaveCount(staffIndex + 1); - var staff = track.Staves[staffIndex]; - ParseStaff(staff, c); - staffIndex++; - break; - } - } - } - } - - private void ParseStaff(Staff staff, XmlNode node) - { - foreach (var c in node.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "Properties": - ParseStaffProperties(staff, c); - break; - } - } - } - } - - private void ParseStaffProperties(Staff staff, XmlNode node) - { - foreach (var c in node.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "Property": - ParseStaffProperty(staff, c); - break; - } - } - } - } - - private void ParseStaffProperty(Staff staff, XmlNode node) - { - var propertyName = node.GetAttribute("name"); - switch (propertyName) - { - case "Tuning": - var tuningParts = node.FindChildElement("Pitches").InnerText.Split(' '); - var tuning = new int[tuningParts.Length]; - for (int i = 0; i < tuning.Length; i++) - { - tuning[tuning.Length - 1 - i] = Platform.Platform.ParseInt(tuningParts[i]); - } - - staff.Tuning = tuning; - if (staff.StaffKind != StaffKind.Percussion) - { - staff.StaffKind = StaffKind.Mixed; - } - break; - case "DiagramCollection": - case "ChordCollection": - ParseDiagramCollection(staff, node); - break; - case "CapoFret": - var capo = Platform.Platform.ParseInt(node.FindChildElement("Fret").InnerText); - staff.Capo = capo; - break; - } - - } - - private void ParseLyrics(string trackId, XmlNode node) - { - var tracks = new FastList(); - foreach (var c in node.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "Line": - tracks.Add(ParseLyricsLine(c)); - break; - } - } - } - _lyricsByTrack[trackId] = tracks; - } - - private Lyrics ParseLyricsLine(XmlNode node) - { - var lyrics = new Lyrics(); - foreach (var c in node.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "Offset": - lyrics.StartBar = Platform.Platform.ParseInt(c.InnerText); - break; - case "Text": - lyrics.Text = c.InnerText; - break; - } - } - } - return lyrics; - } - - private void ParseDiagramCollection(Track track, XmlNode node) - { - var items = node.FindChildElement("Items"); - foreach (var c in items.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "Item": - ParseDiagramItem(track, c); - break; - } - } - } - } - private void ParseDiagramCollection(Staff staff, XmlNode node) - { - var items = node.FindChildElement("Items"); - foreach (var c in items.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "Item": - ParseDiagramItem(staff, c); - break; - } - } - } - } - - private void ParseDiagramItem(Track track, XmlNode node) - { - var chord = new Chord(); - var chordId = node.GetAttribute("id"); - foreach (var staff in track.Staves) - { - staff.Chords[chordId] = chord; - } - ParseDiagramItem(chord, node); - } - - private void ParseDiagramItem(Staff staff, XmlNode node) - { - var chord = new Chord(); - var chordId = node.GetAttribute("id"); - staff.Chords[chordId] = chord; - ParseDiagramItem(chord, node); - } - - private void ParseDiagramItem(Chord chord, XmlNode node) - { - chord.Name = node.GetAttribute("name"); - - var diagram = node.FindChildElement("Diagram"); - var stringCount = Platform.Platform.ParseInt(diagram.GetAttribute("stringCount")); - var baseFret = Platform.Platform.ParseInt(diagram.GetAttribute("baseFret")); - chord.FirstFret = baseFret + 1; - for (int i = 0; i < stringCount; i++) - { - chord.Strings.Add(-1); - } - - foreach (var c in diagram.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "Fret": - var guitarString = Platform.Platform.ParseInt(c.GetAttribute("string")); - chord.Strings[stringCount - guitarString - 1] = baseFret + Platform.Platform.ParseInt(c.GetAttribute("fret")); - break; - case "Fingering": - var existingFingers = new FastDictionary(); - - foreach (var p in c.ChildNodes) - { - if (p.NodeType == XmlNodeType.Element) - { - switch (p.LocalName) - { - case "Position": - var finger = Fingers.Unknown; - var fret = baseFret + Platform.Platform.ParseInt(p.GetAttribute("fret")); - switch (p.GetAttribute("finger")) - { - case "Index": - finger = Fingers.IndexFinger; - break; - case "Middle": - finger = Fingers.MiddleFinger; - break; - case "Rank": - finger = Fingers.AnnularFinger; - break; - case "Pinky": - finger = Fingers.LittleFinger; - break; - case "Thumb": - finger = Fingers.Thumb; - break; - case "None": - break; - } - - if (finger != Fingers.Unknown) - { - if (existingFingers.ContainsKey(finger)) - { - chord.BarreFrets.Add(fret); - } - else - { - existingFingers[finger] = true; - } - } - - break; - } - } - } - - break; - } - } - } - } - - - private void ParseTrackProperties(Track track, XmlNode node) - { - foreach (var c in node.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "Property": - ParseTrackProperty(track, c); - break; - } - } - } - } - - private void ParseTrackProperty(Track track, XmlNode node) - { - var propertyName = node.GetAttribute("name"); - switch (propertyName) - { - case "Tuning": - var tuningParts = node.FindChildElement("Pitches").InnerText.Split(' '); - var tuning = new int[tuningParts.Length]; - for (int i = 0; i < tuning.Length; i++) - { - tuning[tuning.Length - 1 - i] = Platform.Platform.ParseInt(tuningParts[i]); - } - - foreach (var staff in track.Staves) - { - staff.Tuning = tuning; - staff.StaffKind = StaffKind.Mixed; - } - break; - case "DiagramCollection": - case "ChordCollection": - ParseDiagramCollection(track, node); - break; - case "CapoFret": - var capo = Platform.Platform.ParseInt(node.FindChildElement("Fret").InnerText); - foreach (var staff in track.Staves) - { - staff.Capo = capo; - } - break; - } - } - - private void ParseGeneralMidi(Track track, XmlNode node) - { - foreach (var c in node.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "Program": - track.PlaybackInfo.Program = Platform.Platform.ParseInt(c.InnerText); - break; - case "Port": - track.PlaybackInfo.Port = Platform.Platform.ParseInt(c.InnerText); - break; - case "PrimaryChannel": - track.PlaybackInfo.PrimaryChannel = Platform.Platform.ParseInt(c.InnerText); - break; - case "SecondaryChannel": - track.PlaybackInfo.SecondaryChannel = Platform.Platform.ParseInt(c.InnerText); - break; - } - } - } - - var isPercussion = node.GetAttribute("table") == "Percussion"; - if (isPercussion) - { - foreach (var staff in track.Staves) - { - staff.StaffKind = StaffKind.Percussion; - } - } - } - - private void ParseSounds(Track track, XmlNode node) - { - foreach (var c in node.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "Sound": - ParseSound(track, c); - break; - } - } - } - } - - - private void ParseSound(Track track, XmlNode node) - { - foreach (var c in node.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "MIDI": - ParseSoundMidi(track, c); - break; - } - } - } - } - - private void ParseSoundMidi(Track track, XmlNode node) - { - foreach (var c in node.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "Program": - track.PlaybackInfo.Program = Platform.Platform.ParseInt(c.InnerText); - break; - } - } - } - } - - private void ParsePartSounding(Track track, XmlNode node) - { - foreach (var c in node.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "TranspositionPitch": - foreach (var staff in track.Staves) - { - staff.DisplayTranspositionPitch = Platform.Platform.ParseInt(c.InnerText); - } - break; - } - } - } - } - - private void ParseTranspose(Track track, XmlNode node) - { - int octave = 0; - int chromatic = 0; - foreach (var c in node.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "Chromatic": - chromatic = Platform.Platform.ParseInt(c.InnerText); - break; - case "Octave": - octave = Platform.Platform.ParseInt(c.InnerText); - break; - } - } - } - - foreach (var staff in track.Staves) - { - staff.DisplayTranspositionPitch = octave * 12 + chromatic; - } - } - - // - // ... - // - - private void ParseMasterBarsNode(XmlNode node) - { - foreach (var c in node.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "MasterBar": - ParseMasterBar(c); - break; - } - } - } - } - - private void ParseMasterBar(XmlNode node) - { - var masterBar = new MasterBar(); - foreach (var c in node.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "Time": - var timeParts = c.InnerText.Split('/'); - masterBar.TimeSignatureNumerator = Platform.Platform.ParseInt(timeParts[0]); - masterBar.TimeSignatureDenominator = Platform.Platform.ParseInt(timeParts[1]); - break; - case "DoubleBar": - masterBar.IsDoubleBar = true; - break; - case "Section": - masterBar.Section = new Section(); - masterBar.Section.Marker = c.FindChildElement("Letter").InnerText; - masterBar.Section.Text = c.FindChildElement("Text").InnerText; - break; - case "Repeat": - if (c.GetAttribute("start").ToLower() == "true") - { - masterBar.IsRepeatStart = true; - } - if (c.GetAttribute("end").ToLower() == "true" && c.GetAttribute("count") != null) - { - masterBar.RepeatCount = Platform.Platform.ParseInt(c.GetAttribute("count")); - } - break; - // TODO case "Directions": // Coda segno etc. - case "AlternateEndings": - var alternateEndings = c.InnerText.Split(' '); - var i = 0; - for (int k = 0; k < alternateEndings.Length; k++) - { - i |= 1 << (-1 + Platform.Platform.ParseInt(alternateEndings[k])); - } - masterBar.AlternateEndings = (byte)i; - break; - case "Bars": - _barsOfMasterBar.Add(c.InnerText.Split(' ')); - break; - case "TripletFeel": - switch (c.InnerText) - { - case "NoTripletFeel": - masterBar.TripletFeel = TripletFeel.NoTripletFeel; - break; - case "Triplet8th": - masterBar.TripletFeel = TripletFeel.Triplet8th; - break; - case "Triplet16th": - masterBar.TripletFeel = TripletFeel.Triplet16th; - break; - case "Dotted8th": - masterBar.TripletFeel = TripletFeel.Dotted8th; - break; - case "Dotted16th": - masterBar.TripletFeel = TripletFeel.Dotted16th; - break; - case "Scottish8th": - masterBar.TripletFeel = TripletFeel.Scottish8th; - break; - case "Scottish16th": - masterBar.TripletFeel = TripletFeel.Scottish16th; - break; - } - break; - case "Key": - masterBar.KeySignature = (KeySignature)Platform.Platform.ParseInt(c.FindChildElement("AccidentalCount").InnerText); - var mode = c.FindChildElement("Mode"); - if (mode != null) - { - switch (mode.InnerText.ToLower()) - { - case "major": - masterBar.KeySignatureType = KeySignatureType.Major; - break; - case "minor": - masterBar.KeySignatureType = KeySignatureType.Minor; - break; - } - } - break; - case "Fermatas": - ParseFermatas(masterBar, c); - break; - - } - } - } - _masterBars.Add(masterBar); - } - - private void ParseFermatas(MasterBar masterBar, XmlNode node) - { - foreach (var c in node.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "Fermata": - ParseFermata(masterBar, c); - break; - } - } - } - } - - private void ParseFermata(MasterBar masterBar, XmlNode node) - { - var offset = 0; - var fermata = new Fermata(); - foreach (var c in node.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "Type": - switch (c.InnerText) - { - case "Short": - fermata.Type = FermataType.Short; - break; - case "Medium": - fermata.Type = FermataType.Medium; - break; - case "Long": - fermata.Type = FermataType.Long; - break; - } - break; - case "Length": - fermata.Length = Platform.Platform.ParseFloat(c.InnerText); - break; - case "Offset": - var parts = c.InnerText.Split('/'); - if (parts.Length == 2) - { - float numerator = Platform.Platform.ParseInt(parts[0]); - float denominator = Platform.Platform.ParseInt(parts[1]); - offset = (int)((numerator / denominator) * MidiUtils.QuarterTime); - } - break; - } - } - } - - masterBar.AddFermata(offset, fermata); - } - - // - // ... - // - - private void ParseBars(XmlNode node) - { - foreach (var c in node.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "Bar": - ParseBar(c); - break; - } - } - } - } - - private void ParseBar(XmlNode node) - { - var bar = new Bar(); - var barId = node.GetAttribute("id"); - - foreach (var c in node.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "Voices": - _voicesOfBar[barId] = c.InnerText.Split(' '); - break; - case "Clef": - switch (c.InnerText) - { - case "Neutral": - bar.Clef = Clef.Neutral; - break; - case "G2": - bar.Clef = Clef.G2; - break; - case "F4": - bar.Clef = Clef.F4; - break; - case "C4": - bar.Clef = Clef.C4; - break; - case "C3": - bar.Clef = Clef.C3; - break; - } - break; - case "Ottavia": - switch (c.InnerText) - { - case "8va": - bar.ClefOttava = Ottavia._8va; - break; - case "15ma": - bar.ClefOttava = Ottavia._15ma; - break; - case "8vb": - bar.ClefOttava = Ottavia._8vb; - break; - case "15mb": - bar.ClefOttava = Ottavia._15mb; - break; - } - break; - case "SimileMark": - switch (c.InnerText) - { - case "Simple": - bar.SimileMark = SimileMark.Simple; - break; - case "FirstOfDouble": - bar.SimileMark = SimileMark.FirstOfDouble; - break; - case "SecondOfDouble": - bar.SimileMark = SimileMark.SecondOfDouble; - break; - } - break; - } - } - } - - _barsById[barId] = bar; - } - - // - // ... - // - - private void ParseVoices(XmlNode node) - { - foreach (var c in node.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "Voice": - ParseVoice(c); - break; - } - } - } - } - - private void ParseVoice(XmlNode node) - { - var voice = new Voice(); - var voiceId = node.GetAttribute("id"); - - foreach (var c in node.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "Beats": - _beatsOfVoice[voiceId] = c.InnerText.Split(' '); - break; - } - } - } - - _voiceById[voiceId] = voice; - } - - // - // ... - // - - private void ParseBeats(XmlNode node) - { - foreach (var c in node.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "Beat": - ParseBeat(c); - break; - } - } - } - } - - private void ParseBeat(XmlNode node) - { - var beat = new Beat(); - var beatId = node.GetAttribute("id"); - - foreach (var c in node.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "Notes": - _notesOfBeat[beatId] = c.InnerText.Split(' '); - break; - case "Rhythm": - _rhythmOfBeat[beatId] = c.GetAttribute("ref"); - break; - case "Fadding": - if (c.InnerText == "FadeIn") - { - beat.FadeIn = true; - } - break; - case "Tremolo": - switch (c.InnerText) - { - case "1/2": - beat.TremoloSpeed = Duration.Eighth; - break; - case "1/4": - beat.TremoloSpeed = Duration.Sixteenth; - break; - case "1/8": - beat.TremoloSpeed = Duration.ThirtySecond; - break; - } - break; - case "Chord": - beat.ChordId = c.InnerText; - break; - case "Hairpin": - switch (c.InnerText) - { - case "Crescendo": - beat.Crescendo = CrescendoType.Crescendo; - break; - case "Decrescendo": - beat.Crescendo = CrescendoType.Decrescendo; - break; - } - break; - case "Arpeggio": - if (c.InnerText == "Up") - { - beat.BrushType = BrushType.ArpeggioUp; - } - else - { - beat.BrushType = BrushType.ArpeggioDown; - } - break; - // TODO: brushDuration - case "Properties": - ParseBeatProperties(c, beat); - break; - case "XProperties": - ParseBeatXProperties(c, beat); - break; - case "FreeText": - beat.Text = c.InnerText; - break; - case "Dynamic": - switch (c.InnerText) - { - case "PPP": - beat.Dynamic = DynamicValue.PPP; - break; - case "PP": - beat.Dynamic = DynamicValue.PP; - break; - case "P": - beat.Dynamic = DynamicValue.P; - break; - case "MP": - beat.Dynamic = DynamicValue.MP; - break; - case "MF": - beat.Dynamic = DynamicValue.MF; - break; - case "F": - beat.Dynamic = DynamicValue.F; - break; - case "FF": - beat.Dynamic = DynamicValue.FF; - break; - case "FFF": - beat.Dynamic = DynamicValue.FFF; - break; - } - break; - case "GraceNotes": - switch (c.InnerText) - { - case "OnBeat": - beat.GraceType = GraceType.OnBeat; - break; - case "BeforeBeat": - beat.GraceType = GraceType.BeforeBeat; - break; - } - break; - case "Legato": - if (c.GetAttribute("origin") == "true") - { - beat.IsLegatoOrigin = true; - } - break; - case "Whammy": - - var whammyOrigin = new BendPoint(); - whammyOrigin.Value = ToBendValue(Platform.Platform.ParseFloat(c.GetAttribute("originValue"))); - whammyOrigin.Offset = ToBendOffset(Platform.Platform.ParseFloat(c.GetAttribute("originOffset"))); - beat.AddWhammyBarPoint(whammyOrigin); - - var whammyMiddle1 = new BendPoint(); - whammyMiddle1.Value = ToBendValue(Platform.Platform.ParseFloat(c.GetAttribute("middleValue"))); - whammyMiddle1.Offset = ToBendOffset(Platform.Platform.ParseFloat(c.GetAttribute("middleOffset1"))); - beat.AddWhammyBarPoint(whammyMiddle1); - - - var whammyMiddle2 = new BendPoint(); - whammyMiddle2.Value = ToBendValue(Platform.Platform.ParseFloat(c.GetAttribute("middleValue"))); - whammyMiddle2.Offset = ToBendOffset(Platform.Platform.ParseFloat(c.GetAttribute("middleOffset2"))); - beat.AddWhammyBarPoint(whammyMiddle2); - - var whammyDestination = new BendPoint(); - whammyDestination.Value = ToBendValue(Platform.Platform.ParseFloat(c.GetAttribute("destinationValue"))); - whammyDestination.Offset = ToBendOffset(Platform.Platform.ParseFloat(c.GetAttribute("destinationOffset"))); - beat.AddWhammyBarPoint(whammyDestination); - break; - case "Ottavia": - switch (c.InnerText) - { - case "8va": - beat.Ottava = Ottavia._8va; - break; - case "8vb": - beat.Ottava = Ottavia._8vb; - break; - case "15ma": - beat.Ottava = Ottavia._15ma; - break; - case "15mb": - beat.Ottava = Ottavia._15mb; - break; - } - break; - } - } - } - - _beatById[beatId] = beat; - } - - private void ParseBeatXProperties(XmlNode node, Beat beat) - { - foreach (var c in node.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "XProperty": - var id = c.GetAttribute("id"); - int val; - switch (id) - { - case "1124204545": - val = Platform.Platform.ParseInt(c.FindChildElement("Int").InnerText); - beat.InvertBeamDirection = val == 1; - break; - case "687935489": - val = Platform.Platform.ParseInt(c.FindChildElement("Int").InnerText); - beat.BrushDuration = val; - break; - } - break; - } - } - } - } - - private void ParseBeatProperties(XmlNode node, Beat beat) - { - bool isWhammy = false; - BendPoint whammyOrigin = null; - int? whammyMiddleValue = null; - int? whammyMiddleOffset1 = null; - int? whammyMiddleOffset2 = null; - BendPoint whammyDestination = null; - - foreach (var c in node.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "Property": - var name = c.GetAttribute("name"); - switch (name) - { - case "Brush": - if (c.FindChildElement("Direction").InnerText == "Up") - { - beat.BrushType = BrushType.BrushUp; - } - else - { - beat.BrushType = BrushType.BrushDown; - } - break; - // TODO: brush duration - case "PickStroke": - if (c.FindChildElement("Direction").InnerText == "Up") - { - beat.PickStroke = PickStroke.Up; - } - else - { - beat.PickStroke = PickStroke.Down; - } - break; - // TODO: brush duration - case "Slapped": - if (c.FindChildElement("Enable") != null) - beat.Slap = true; - break; - case "Popped": - if (c.FindChildElement("Enable") != null) - beat.Pop = true; - break; - case "VibratoWTremBar": - switch (c.FindChildElement("Strength").InnerText) - { - case "Wide": - beat.Vibrato = VibratoType.Wide; - break; - case "Slight": - beat.Vibrato = VibratoType.Slight; - break; - } - break; - case "WhammyBar": - isWhammy = true; - break; - case "WhammyBarExtend": - // not clear what this is used for - break; - - case "WhammyBarOriginValue": - if (whammyOrigin == null) whammyOrigin = new BendPoint(); - whammyOrigin.Value = ToBendValue(Platform.Platform.ParseFloat(c.FindChildElement("Float").InnerText)); - break; - case "WhammyBarOriginOffset": - if (whammyOrigin == null) whammyOrigin = new BendPoint(); - whammyOrigin.Offset = ToBendOffset(Platform.Platform.ParseFloat(c.FindChildElement("Float").InnerText)); - break; - - case "WhammyBarMiddleValue": - whammyMiddleValue = ToBendValue(Platform.Platform.ParseFloat(c.FindChildElement("Float").InnerText)); - break; - - case "WhammyBarMiddleOffset1": - whammyMiddleOffset1 = ToBendOffset(Platform.Platform.ParseFloat(c.FindChildElement("Float").InnerText)); - break; - case "WhammyBarMiddleOffset2": - whammyMiddleOffset2 = ToBendOffset(Platform.Platform.ParseFloat(c.FindChildElement("Float").InnerText)); - break; - - case "WhammyBarDestinationValue": - if (whammyDestination == null) - whammyDestination = new BendPoint(BendPoint.MaxPosition); - whammyDestination.Value = ToBendValue(Platform.Platform.ParseFloat(c.FindChildElement("Float").InnerText)); - break; - - case "WhammyBarDestinationOffset": - if (whammyDestination == null) whammyDestination = new BendPoint(); - whammyDestination.Offset = ToBendOffset(Platform.Platform.ParseFloat(c.FindChildElement("Float").InnerText)); - - break; - } - break; - } - } - } - - if (isWhammy) - { - if (whammyOrigin == null) whammyOrigin = new BendPoint(); - if (whammyDestination == null) whammyDestination = new BendPoint(BendPoint.MaxPosition); - beat.AddWhammyBarPoint(whammyOrigin); - - if (whammyMiddleOffset1 != null && whammyMiddleValue != null) - { - beat.AddWhammyBarPoint(new BendPoint(whammyMiddleOffset1.Value, whammyMiddleValue.Value)); - } - if (whammyMiddleOffset2 != null && whammyMiddleValue != null) - { - beat.AddWhammyBarPoint(new BendPoint(whammyMiddleOffset2.Value, whammyMiddleValue.Value)); - } - - if (whammyMiddleOffset1 == null && whammyMiddleOffset2 == null && whammyMiddleValue != null) - { - beat.AddWhammyBarPoint(new BendPoint(BendPoint.MaxPosition / 2, whammyMiddleValue.Value)); - } - beat.AddWhammyBarPoint(whammyDestination); - } - } - - // - // ... - // - - private void ParseNotes(XmlNode node) - { - foreach (var c in node.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "Note": - ParseNote(c); - break; - } - } - } - } - - private void ParseNote(XmlNode node) - { - var note = new Note(); - var noteId = node.GetAttribute("id"); - - foreach (var c in node.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "Properties": - ParseNoteProperties(c, note, noteId); - break; - case "AntiAccent": - if (c.InnerText.ToLower() == "normal") - { - note.IsGhost = true; - } - break; - case "LetRing": - note.IsLetRing = true; - break; - case "Trill": - note.TrillValue = Platform.Platform.ParseInt(c.InnerText); - note.TrillSpeed = Duration.Sixteenth; - break; - case "Accent": - var accentFlags = Platform.Platform.ParseInt(c.InnerText); - if ((accentFlags & 0x01) != 0) - note.IsStaccato = true; - if ((accentFlags & 0x04) != 0) - note.Accentuated = AccentuationType.Heavy; - if ((accentFlags & 0x08) != 0) - note.Accentuated = AccentuationType.Normal; - break; - case "Tie": - if (c.GetAttribute("origin").ToLower() == "true") - { - note.IsTieOrigin = true; - } - if (c.GetAttribute("destination").ToLower() == "true") - { - note.IsTieDestination = true; - } - break; - case "Vibrato": - switch (c.InnerText) - { - case "Slight": - note.Vibrato = VibratoType.Slight; - break; - case "Wide": - note.Vibrato = VibratoType.Wide; - break; - } - break; - case "LeftFingering": - note.IsFingering = true; - switch (c.InnerText) - { - case "P": - note.LeftHandFinger = Fingers.Thumb; - break; - case "I": - note.LeftHandFinger = Fingers.IndexFinger; - break; - case "M": - note.LeftHandFinger = Fingers.MiddleFinger; - break; - case "A": - note.LeftHandFinger = Fingers.AnnularFinger; - break; - case "C": - note.LeftHandFinger = Fingers.LittleFinger; - break; - } - break; - case "RightFingering": - note.IsFingering = true; - switch (c.InnerText) - { - case "P": - note.RightHandFinger = Fingers.Thumb; - break; - case "I": - note.RightHandFinger = Fingers.IndexFinger; - break; - case "M": - note.RightHandFinger = Fingers.MiddleFinger; - break; - case "A": - note.RightHandFinger = Fingers.AnnularFinger; - break; - case "C": - note.RightHandFinger = Fingers.LittleFinger; - break; - } - break; - } - } - } - - _noteById[noteId] = note; - } - - private void ParseNoteProperties(XmlNode node, Note note, string noteId) - { - bool isBended = false; - BendPoint bendOrigin = null; - int? bendMiddleValue = null; - int? bendMiddleOffset1 = null; - int? bendMiddleOffset2 = null; - BendPoint bendDestination = null; - - foreach (var c in node.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "Property": - var name = c.GetAttribute("name"); - switch (name) - { - case "String": - note.String = Platform.Platform.ParseInt(c.FindChildElement("String").InnerText) + 1; - break; - case "Fret": - note.Fret = Platform.Platform.ParseInt(c.FindChildElement("Fret").InnerText); - break; - case "Element": - note.Element = Platform.Platform.ParseInt(c.FindChildElement("Element").InnerText); - break; - case "Variation": - note.Variation = Platform.Platform.ParseInt(c.FindChildElement("Variation").InnerText); - break; - case "Tapped": - _tappedNotes[noteId] = true; - break; - case "HarmonicType": - var htype = c.FindChildElement("HType"); - if (htype != null) - { - switch (htype.InnerText) - { - case "NoHarmonic": - note.HarmonicType = HarmonicType.None; - break; - case "Natural": - note.HarmonicType = HarmonicType.Natural; - break; - case "Artificial": - note.HarmonicType = HarmonicType.Artificial; - break; - case "Pinch": - note.HarmonicType = HarmonicType.Pinch; - break; - case "Tap": - note.HarmonicType = HarmonicType.Tap; - break; - case "Semi": - note.HarmonicType = HarmonicType.Semi; - break; - case "Feedback": - note.HarmonicType = HarmonicType.Feedback; - break; - } - } - break; - case "HarmonicFret": - var hfret = c.FindChildElement("HFret"); - if (hfret != null) - { - note.HarmonicValue = Platform.Platform.ParseFloat(hfret.InnerText); - } - break; - case "Muted": - if (c.FindChildElement("Enable") != null) - note.IsDead = true; - break; - case "PalmMuted": - if (c.FindChildElement("Enable") != null) - note.IsPalmMute = true; - break; - // case "Element": - // case "Variation": - // case "Tone": - case "Octave": - note.Octave = Platform.Platform.ParseInt(c.FindChildElement("Number").InnerText); - break; - case "Tone": - note.Tone = Platform.Platform.ParseInt(c.FindChildElement("Step").InnerText); - break; - case "Bended": - isBended = true; - break; - - case "BendOriginValue": - if (bendOrigin == null) bendOrigin = new BendPoint(); - bendOrigin.Value = ToBendValue(Platform.Platform.ParseFloat(c.FindChildElement("Float").InnerText)); - break; - case "BendOriginOffset": - if (bendOrigin == null) bendOrigin = new BendPoint(); - bendOrigin.Offset = ToBendOffset(Platform.Platform.ParseFloat(c.FindChildElement("Float").InnerText)); - break; - - case "BendMiddleValue": - bendMiddleValue = ToBendValue(Platform.Platform.ParseFloat(c.FindChildElement("Float").InnerText)); - break; - - case "BendMiddleOffset1": - bendMiddleOffset1 = ToBendOffset(Platform.Platform.ParseFloat(c.FindChildElement("Float").InnerText)); - break; - case "BendMiddleOffset2": - bendMiddleOffset2 = ToBendOffset(Platform.Platform.ParseFloat(c.FindChildElement("Float").InnerText)); - break; - - case "BendDestinationValue": - if (bendDestination == null) bendDestination = new BendPoint(BendPoint.MaxPosition); - // NOTE: If we directly cast the expression of value to (int) it is 3 instead of 4, strange compiler - // optimizations happening here: - // (int)(Platform.ParseFloat(GetValue(c.FindChildElement("Float")))* BendPointValueFactor) => (int)(100f * 0.04f) => 3 - // (Platform.ParseFloat(GetValue(c.FindChildElement("Float")))* BendPointValueFactor) => (100f * 0.04f) => 4.0 - bendDestination.Value = ToBendValue(Platform.Platform.ParseFloat(c.FindChildElement("Float").InnerText)); - break; - - case "BendDestinationOffset": - if (bendDestination == null) bendDestination = new BendPoint(); - bendDestination.Offset = ToBendOffset(Platform.Platform.ParseFloat(c.FindChildElement("Float").InnerText)); - break; - - case "HopoOrigin": - if (c.FindChildElement("Enable") != null) - note.IsHammerPullOrigin = true; - break; - case "HopoDestination": - // NOTE: gets automatically calculated - // if (FindChildElement(node, "Enable") != null) - // note.isHammerPullDestination = true; - break; - case "Slide": - var slideFlags = Platform.Platform.ParseInt(c.FindChildElement("Flags").InnerText); - if ((slideFlags & 1) != 0) - note.SlideType = SlideType.Shift; - if ((slideFlags & 2) != 0) - note.SlideType = SlideType.Legato; - if ((slideFlags & 4) != 0) - note.SlideType = SlideType.OutDown; - if ((slideFlags & 8) != 0) - note.SlideType = SlideType.OutUp; - if ((slideFlags & 16) != 0) - note.SlideType = SlideType.IntoFromBelow; - if ((slideFlags & 32) != 0) - note.SlideType = SlideType.IntoFromAbove; - if ((slideFlags & 64) != 0) - note.SlideType = SlideType.PickSlideDown; - if ((slideFlags & 128) != 0) - note.SlideType = SlideType.PickSlideUp; - break; - } - break; - } - } - } - - if (isBended) - { - if (bendOrigin == null) bendOrigin = new BendPoint(); - if (bendDestination == null) bendDestination = new BendPoint(BendPoint.MaxPosition); - note.AddBendPoint(bendOrigin); - - if (bendMiddleOffset1 != null && bendMiddleValue != null) - { - note.AddBendPoint(new BendPoint(bendMiddleOffset1.Value, bendMiddleValue.Value)); - } - - if (bendMiddleOffset2 != null && bendMiddleValue != null) - { - note.AddBendPoint(new BendPoint(bendMiddleOffset2.Value, bendMiddleValue.Value)); - } - - if (bendMiddleOffset1 == null && bendMiddleOffset2 == null && bendMiddleValue != null) - { - note.AddBendPoint(new BendPoint(BendPoint.MaxPosition / 2, bendMiddleValue.Value)); - } - note.AddBendPoint(bendDestination); - } - } - - private int ToBendValue(double gpxValue) - { - return (int)(gpxValue * BendPointValueFactor); - } - - private int ToBendOffset(float gpxOffset) - { - var converted = gpxOffset * BendPointPositionFactor; - return (int)(converted); - } - - private void ParseRhythms(XmlNode node) - { - foreach (var c in node.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "Rhythm": - ParseRhythm(c); - break; - } - } - } - } - - private void ParseRhythm(XmlNode node) - { - var rhythm = new GpifRhythm(); - var rhythmId = node.GetAttribute("id"); - foreach (var c in node.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "NoteValue": - switch (c.InnerText) - { - case "Long": - rhythm.Value = Duration.QuadrupleWhole; - break; - case "DoubleWhole": - rhythm.Value = Duration.DoubleWhole; - break; - case "Whole": - rhythm.Value = Duration.Whole; - break; - case "Half": - rhythm.Value = Duration.Half; - break; - case "Quarter": - rhythm.Value = Duration.Quarter; - break; - case "Eighth": - rhythm.Value = Duration.Eighth; - break; - case "16th": - rhythm.Value = Duration.Sixteenth; - break; - case "32nd": - rhythm.Value = Duration.ThirtySecond; - break; - case "64th": - rhythm.Value = Duration.SixtyFourth; - break; - case "128th": - rhythm.Value = Duration.OneHundredTwentyEighth; - break; - case "256th": - rhythm.Value = Duration.TwoHundredFiftySixth; - break; - } - break; - case "PrimaryTuplet": - rhythm.TupletNumerator = Platform.Platform.ParseInt(c.GetAttribute("num")); - rhythm.TupletDenominator = Platform.Platform.ParseInt(c.GetAttribute("den")); - break; - case "AugmentationDot": - rhythm.Dots = Platform.Platform.ParseInt(c.GetAttribute("count")); - break; - } - } - } - - _rhythmById[rhythmId] = rhythm; - } - - #endregion - - #region Model Building - - private void BuildModel() - { - // build score - for (int i = 0, j = _masterBars.Count; i < j; i++) - { - var masterBar = _masterBars[i]; - Score.AddMasterBar(masterBar); - } - - // add tracks to score - foreach (var trackId in _tracksMapping) - { - if (string.IsNullOrEmpty(trackId)) - { - continue; - } - var track = _tracksById[trackId]; - Score.AddTrack(track); - } - - // process all masterbars - for (int masterBarIndex = 0; masterBarIndex < _barsOfMasterBar.Count; masterBarIndex++) - { - var barIds = _barsOfMasterBar[masterBarIndex]; - - // add all bars of masterbar vertically to all tracks - int staffIndex = 0; - for (int barIndex = 0, trackIndex = 0; barIndex < barIds.Length && trackIndex < Score.Tracks.Count; barIndex++) - { - var barId = barIds[barIndex]; - if (barId != InvalidId) - { - var bar = _barsById[barId]; - var track = Score.Tracks[trackIndex]; - var staff = track.Staves[staffIndex]; - staff.AddBar(bar); - - // stave is full? -> next track - if (staffIndex == track.Staves.Count - 1) - { - trackIndex++; - staffIndex = 0; - } - else - { - staffIndex++; - } - } - else - { - - // no bar for track - trackIndex++; - } - } - } - - - // build bars - foreach (var barId in _barsById) - { - var bar = _barsById[barId]; - if (_voicesOfBar.ContainsKey(barId)) - { - // add voices to bars - foreach (var voiceId in _voicesOfBar[barId]) - { - if (voiceId != InvalidId) - { - bar.AddVoice(_voiceById[voiceId]); - } - else - { - // invalid voice -> empty voice - var voice = new Voice(); - bar.AddVoice(voice); - - var beat = new Beat(); - beat.IsEmpty = true; - beat.Duration = Duration.Quarter; - voice.AddBeat(beat); - } - } - } - } - - // build beats - foreach (var beatId in _beatById) - { - var beat = _beatById[beatId]; - var rhythmId = _rhythmOfBeat[beatId]; - var rhythm = _rhythmById[rhythmId]; - - // set beat duration - beat.Duration = rhythm.Value; - beat.Dots = rhythm.Dots; - beat.TupletNumerator = rhythm.TupletNumerator; - beat.TupletDenominator = rhythm.TupletDenominator; - - // add notes to beat - if (_notesOfBeat.ContainsKey(beatId)) - { - foreach (var noteId in _notesOfBeat[beatId]) - { - if (noteId != InvalidId) - { - beat.AddNote(_noteById[noteId]); - if (_tappedNotes.ContainsKey(noteId)) - { - beat.Tap = true; - } - } - } - } - } - - // build voices - foreach (var voiceId in _voiceById) - { - var voice = _voiceById[voiceId]; - if (_beatsOfVoice.ContainsKey(voiceId)) - { - // add beats to voices - foreach (var beatId in _beatsOfVoice[voiceId]) - { - if (beatId != InvalidId) - { - // important! we clone the beat because beats get reused - // in gp6, our model needs to have unique beats. - voice.AddBeat(_beatById[beatId].Clone()); - } - } - } - } - - // build masterbar automations - foreach (var barIndex in _masterTrackAutomations) - { - var automations = _masterTrackAutomations[barIndex]; - var masterBar = Score.MasterBars[Platform.Platform.ParseInt(barIndex)]; - for (int i = 0, j = automations.Count; i < j; i++) - { - var automation = automations[i]; - if (automation.Type == AutomationType.Tempo) - { - if (barIndex == "0") // // TODO find the correct first bar id - { - Score.Tempo = (int)(automation.Value); - if (automation.Text != null) - { - Score.TempoLabel = automation.Text; - } - } - - masterBar.TempoAutomation = automation; - } - } - } - } - - #endregion - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Importer/GpxFileSystem.cs b/Source/AlphaTab/Importer/GpxFileSystem.cs deleted file mode 100644 index b6e7eee37..000000000 --- a/Source/AlphaTab/Importer/GpxFileSystem.cs +++ /dev/null @@ -1,287 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.Collections; -using AlphaTab.IO; -using AlphaTab.Platform; - -namespace AlphaTab.Importer -{ - - /// - /// this public class represents a file within the GpxFileSystem - /// - class GpxFile - { - public string FileName { get; set; } - public int FileSize { get; set; } - public byte[] Data { get; set; } - } - - /// - /// This public class represents the file system structure - /// stored within a GPX container file. - /// - class GpxFileSystem - { - public const string HeaderBcFs = "BCFS"; - public const string HeaderBcFz = "BCFZ"; - public const string ScoreGpif = "score.gpif"; - public const string BinaryStylesheet = "BinaryStylesheet"; - - /// - /// You can set a file filter method using this setter. On parsing - /// the filestructure this function can determine based on the filename - /// whether this file will be available after loading. - /// This way we can reduce the amount of memory we store. - /// - public Func FileFilter { get; set; } - - /// - /// Gets the list of files stored in this FileSystem. - /// - public FastList Files { get; set; } - - /// - /// Creates a new GpxFileSystem instance - /// - public GpxFileSystem() - { - Files = new FastList(); - FileFilter = s => true; - } - - /// - /// Load a complete FileSystem to the memory. - /// - /// the binary source to read from. - /// - public void Load(IReadable s) - { - var src = new BitReader(s); - ReadBlock(src); - } - - /// - /// Reads the 4 byte header as a string. - /// - /// the BitInput to read from - /// a string with 4 characters representing the header. - public string ReadHeader(BitReader src) - { - return GetString(src.ReadBytes(4), 0, 4); - } - - /// - /// Decompresses the given bitinput using the GPX compression format. Only use this method - /// if you are sure the binary data is compressed using the GPX format. Otherwise unexpected - /// behavior can occure. - /// - /// the bitInput to read the data from - /// true if the header should NOT be included in the result byteset, otherwise false - /// the decompressed byte data. if skipHeader is set to false the BCFS header is included. - public byte[] Decompress(BitReader src, bool skipHeader = false) - { - var uncompressed = ByteBuffer.Empty(); - byte[] buffer; - var expectedLength = GetInteger(src.ReadBytes(4), 0); - - try - { - // as long we reach our expected length we try to decompress, a EOF might occure. - while (uncompressed.Length < expectedLength) - { - // compression flag - var flag = src.ReadBits(1); - - if (flag == 1) // compressed content - { - // get offset and size of the content we need to read. - // compressed does mean we already have read the data and need - // to copy it from our uncompressed buffer to the end - var wordSize = src.ReadBits(4); - var offset = src.ReadBitsReversed(wordSize); - var size = src.ReadBitsReversed(wordSize); - - // the offset is relative to the end - var sourcePosition = uncompressed.Length - offset; - var toRead = Math.Min(offset, size); - - // get the subbuffer storing the data and add it again to the end - buffer = uncompressed.GetBuffer(); - uncompressed.Write(buffer, (int) sourcePosition, toRead); - } - else // raw content - { - // on raw content we need to read the data from the source buffer - var size = src.ReadBitsReversed(2); - for (int i = 0; i < size; i++) - { - uncompressed.WriteByte((byte) src.ReadByte()); - } - } - } - } - catch (EndOfReaderException) - { - } - - buffer = uncompressed.GetBuffer(); - var resultOffset = skipHeader ? 4 : 0; - var resultSize = uncompressed.Length - resultOffset; - var result = new byte[(int)resultSize]; - Platform.Platform.BlockCopy(buffer, resultOffset, result, 0, (int)resultSize); - return result; - } - - /// - /// Reads a block from the given data source. - /// - /// the data source - /// - private void ReadBlock(BitReader data) - { - var header = ReadHeader(data); - if (header == HeaderBcFz) // compressed file? - { - // decompress the data and use this - // we will skip the header - ReadUncompressedBlock(Decompress(data, true)); - } - else if (header == HeaderBcFs) // uncompressed file? - { - ReadUncompressedBlock(data.ReadAll()); - } - else - { - throw new UnsupportedFormatException(); - } - } - - /// - /// Reads an uncompressed data block into the model. - /// - /// the data store to read from. - private void ReadUncompressedBlock(byte[] data) - { - // the uncompressed block contains a list of filesystem entires - // as long we have data we will try to read more entries - - // the first sector (0x1000 bytes) is empty (filled with 0xFF) - // so the first sector starts at 0x1000 - // (we already skipped the 4 byte header so we don't have to take care of this) - - var sectorSize = 0x1000; - var offset = sectorSize; - - // we always need 4 bytes (+3 including offset) to read the type - while ((offset + 3) < data.Length) - { - var entryType = GetInteger(data, offset); - - if (entryType == 2) // is a file? - { - // file structure: - // offset | type | size | what - // --------+----------+----------+------ - // 0x04 | string | 127byte | FileName (zero terminated) - // 0x83 | ? | 9byte | Unknown - // 0x8c | int | 4byte | FileSize - // 0x90 | ? | 4byte | Unknown - // 0x94 | int[] | n*4byte | Indices of the sector containing the data (end is marked with 0) - - // The sectors marked at 0x94 are absolutely positioned ( 1*0x1000 is sector 1, 2*0x1000 is sector 2,...) - - var file = new GpxFile(); - file.FileName = GetString(data, offset + 0x04, 127); - file.FileSize = GetInteger(data, offset + 0x8C); - - // store file if needed - var storeFile = FileFilter == null || FileFilter(file.FileName); - if (storeFile) - { - Files.Add(file); - } - - // we need to iterate the blocks because we need to move after the last datasector - - var dataPointerOffset = offset + 0x94; - var sector = 0; // this var is storing the sector index - var sectorCount = 0; // we're keeping count so we can calculate the offset of the array item - - // as long we have data blocks we need to iterate them, - var fileData = storeFile ? ByteBuffer.WithCapactiy(file.FileSize) : null; - while ((sector = GetInteger(data, (dataPointerOffset + (4 * (sectorCount++))))) != 0) - { - // the next file entry starts after the last data sector so we - // move the offset along - offset = sector * sectorSize; - - // write data only if needed - if (storeFile) - { - fileData.Write(data, offset, sectorSize); - } - } - - if (storeFile) - { - // trim data to filesize if needed - file.Data = new byte[(int)Math.Min(file.FileSize, fileData.Length)]; - // we can use the getBuffer here because we are intelligent and know not to read the empty data. - byte[] raw = fileData.ToArray(); - Platform.Platform.BlockCopy(raw, 0, file.Data, 0, file.Data.Length); - } - } - - // let's move to the next sector - offset += sectorSize; - } - } - - /// - /// Reads a zeroterminated ascii string from the given source - /// - /// the data source to read from - /// the offset to start reading from - /// the max length to read - /// the ascii string read from the datasource. - private string GetString(byte[] data, int offset, int length) - { - var buf = new StringBuilder(); - for (int i = 0; i < length; i++) - { - var code = data[offset + i] & 0xFF; - if (code == 0) break; // zero terminated string - buf.AppendChar(code); - } - return buf.ToString(); - } - - /// - /// Reads an 4 byte signed integer from the given source - /// - /// the data source to read from - /// offset the offset to start reading from - /// - private int GetInteger(byte[] data, int offset) - { - return (data[offset + 3] << 24) | (data[offset + 2] << 16) | (data[offset + 1] << 8) | data[offset]; - } - } -} diff --git a/Source/AlphaTab/Importer/GpxImporter.cs b/Source/AlphaTab/Importer/GpxImporter.cs deleted file mode 100644 index 7ca36affa..000000000 --- a/Source/AlphaTab/Importer/GpxImporter.cs +++ /dev/null @@ -1,86 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Model; -using AlphaTab.Platform; -using AlphaTab.Util; - -namespace AlphaTab.Importer -{ - /// - /// This ScoreImporter can read Guitar Pro 6 (gpx) files. - /// - class GpxImporter : ScoreImporter - { - public override string Name { get { return "Guitar Pro 6"; } } - - public override Score ReadScore() - { - // at first we need to load the binary file system - // from the GPX container - Logger.Info(Name, "Loading GPX filesystem"); - var fileSystem = new GpxFileSystem(); - fileSystem.FileFilter = s => s.EndsWith(GpxFileSystem.ScoreGpif) || s.EndsWith(GpxFileSystem.BinaryStylesheet); - fileSystem.Load(Data); - Logger.Info(Name, "GPX filesystem loaded"); - - string xml = null; - byte[] binaryStylesheet = null; - foreach (var entry in fileSystem.Files) - { - switch (entry.FileName) - { - case GpxFileSystem.ScoreGpif: - xml = Platform.Platform.ToString(entry.Data, GetSetting("encoding", "utf-8")); - break; - case GpxFileSystem.BinaryStylesheet: - binaryStylesheet = entry.Data; - break; - } - } - - // lets set the fileSystem to null, maybe the garbage collector will come along - // and kick the fileSystem binary data before we finish parsing - fileSystem.Files = null; - fileSystem = null; - - // the score.gpif file within this filesystem stores - // the score information as XML we need to parse. - Logger.Info(Name, "Start Parsing score.gpif"); - var parser = new GpifParser(); - parser.ParseXml(xml, Settings); - Logger.Info(Name, "score.gpif parsed"); - - var score = parser.Score; - - if (binaryStylesheet != null) - { - Logger.Info(Name, "Start Parsing BinaryStylesheet"); - var stylesheetParser = new BinaryStylesheetParser(); - stylesheetParser.Parse(binaryStylesheet); - if (stylesheetParser.Stylesheet != null) - { - stylesheetParser.Stylesheet.Apply(score); - } - Logger.Info(Name, "BinaryStylesheet parsed"); - } - - return score; - } - } -} diff --git a/Source/AlphaTab/Importer/MixTableChange.cs b/Source/AlphaTab/Importer/MixTableChange.cs deleted file mode 100644 index 5f9fcd614..000000000 --- a/Source/AlphaTab/Importer/MixTableChange.cs +++ /dev/null @@ -1,43 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -namespace AlphaTab.Importer -{ - /// - /// A mixtablechange describes several track changes. - /// - class MixTableChange - { - public int Volume { get; set; } - public int Balance { get; set; } - public int Instrument { get; set; } - public string TempoName { get; set; } - public int Tempo { get; set; } - public int Duration { get; set; } - - public MixTableChange() - { - Volume = -1; - Balance = -1; - Instrument = -1; - TempoName = null; - Tempo = -1; - Duration = 0; - } - } -} diff --git a/Source/AlphaTab/Importer/MusicXmlImporter.cs b/Source/AlphaTab/Importer/MusicXmlImporter.cs deleted file mode 100644 index 0e3b12876..000000000 --- a/Source/AlphaTab/Importer/MusicXmlImporter.cs +++ /dev/null @@ -1,1684 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.Collections; -using AlphaTab.Model; -using AlphaTab.Platform; -using AlphaTab.Xml; -using Note = AlphaTab.Model.Note; -using XmlNodeType = AlphaTab.Xml.XmlNodeType; - -namespace AlphaTab.Importer -{ - class MusicXmlImporter : ScoreImporter - { - public const string MergePartGroupsSetting = "musicXMLMergePartGroups"; - - private Score _score; - private FastDictionary _trackById; - private FastDictionary> _partGroups; - private string _currentPartGroup; - private int _trackFirstMeasureNumber; - private int _maxVoices; - private string _currentDirection; - private int _firstVoice; - - private FastList _tieStarts; - - public override string Name { get { return "MusicXML"; } } - - public override Score ReadScore() - { - _trackById = new FastDictionary(); - _partGroups = new FastDictionary>(); - _tieStarts = new FastList(); - - var xml = Platform.Platform.ToString(Data.ReadAll(), GetSetting("encoding", "utf-8")); - XmlDocument dom; - try - { - dom = new XmlDocument(xml); - } - catch (Exception) - { - throw new UnsupportedFormatException(); - } - - _score = new Score(); - _score.Tempo = 120; - ParseDom(dom); - - // merge partgroups into a single track with multiple staves - if (GetSetting(MergePartGroupsSetting, false)) - { - MergePartGroups(); - } - - _score.Finish(Settings); - // the structure of MusicXML does not allow live creation of the groups, - _score.RebuildRepeatGroups(); - - return _score; - } - - private void MergePartGroups() - { - var anyMerged = false; - foreach (var groupId in _partGroups) - { - var tracks = _partGroups[groupId]; - if (tracks.Count > 1) - { - MergeGroup(tracks); - anyMerged = true; - } - } - - // if any groups were merged, we need to rebuild the indexes - if (anyMerged) - { - for (int i = 0; i < _score.Tracks.Count; i++) - { - _score.Tracks[i].Index = i; - } - } - } - - private void MergeGroup(FastList partGroup) - { - var primaryTrack = partGroup[0]; - for (int i = 1; i < partGroup.Count; i++) - { - // merge staves over to primary track - var secondaryTrack = partGroup[i]; - foreach (var staff in secondaryTrack.Staves) - { - primaryTrack.AddStaff(staff); - } - - // remove track from score - var trackIndex = _score.Tracks.IndexOf(secondaryTrack); - _score.Tracks.RemoveAt(trackIndex); - } - } - - private void ParseDom(XmlDocument dom) - { - var root = dom.DocumentElement; - if (root == null) - { - throw new UnsupportedFormatException(); - } - - switch (root.LocalName) - { - case "score-partwise": - ParsePartwise(root); - break; - case "score-timewise": - //ParseTimewise(root); - break; - default: - throw new UnsupportedFormatException(); - } - } - - private void ParsePartwise(XmlNode element) - { - foreach (var c in element.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "work": - ParseWork(c); - break; - case "movement-title": - _score.Title = c.InnerText; - break; - case "identification": - ParseIdentification(c); - break; - case "part-list": - ParsePartList(c); - break; - case "part": - ParsePart(c); - break; - } - } - } - } - - private void ParseWork(XmlNode element) - { - foreach (var c in element.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "work-title": - _score.Title = c.InnerText; - break; - } - } - } - } - - private void ParsePart(XmlNode element) - { - var id = element.GetAttribute("id"); - if (!_trackById.ContainsKey(id)) - { - return; - } - - _firstVoice = -1; - - var track = _trackById[id]; - var isFirstMeasure = true; - foreach (var c in element.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "measure": - if (ParseMeasure(c, track, isFirstMeasure)) - { - isFirstMeasure = false; - } - break; - } - } - } - } - - private bool ParseMeasure(XmlNode element, Track track, bool isFirstMeasure) - { - if (element.GetAttribute("implicit") == "yes" && element.GetElementsByTagName("note").Length == 0) - { - return false; - } - - var barIndex = 0; - var barWidth = Platform.Platform.ParseInt(element.GetAttribute("width")); - if (barWidth == int.MinValue) - { - barWidth = 0; - } - - if (isFirstMeasure) - { - _divisionsPerQuarterNote = 0; - _trackFirstMeasureNumber = Platform.Platform.ParseInt(element.GetAttribute("number")); - if (_trackFirstMeasureNumber == int.MinValue) - { - _trackFirstMeasureNumber = 0; - } - barIndex = 0; - } - else - { - barIndex = Platform.Platform.ParseInt(element.GetAttribute("number")); - if (barIndex == int.MinValue) - { - return false; - } - barIndex -= _trackFirstMeasureNumber; - } - - // try to find out the number of staffs required - if (isFirstMeasure) - { - var attributes = element.GetElementsByTagName("attributes"); - if (attributes.Length > 0) - { - var stavesElements = attributes[0].GetElementsByTagName("staves"); - if (stavesElements.Length > 0) - { - var staves = Platform.Platform.ParseInt(stavesElements[0].InnerText); - track.EnsureStaveCount(staves); - } - } - } - - - // create empty bars to the current index - Bar[] bars = new Bar[track.Staves.Count]; - MasterBar masterBar = null; - for (int b = track.Staves[0].Bars.Count; b <= barIndex; b++) - { - for (int s = 0; s < track.Staves.Count; s++) - { - var bar = bars[s] = new Bar(); - if (track.Staves[s].Bars.Count > 0) - { - var previousBar = track.Staves[s].Bars[track.Staves[s].Bars.Count - 1]; - bar.Clef = previousBar.Clef; - } - masterBar = GetOrCreateMasterBar(barIndex); - track.Staves[s].AddBar(bar); - - for (int v = 0; v < _maxVoices; v++) - { - var emptyVoice = new Voice(); - bar.AddVoice(emptyVoice); - var emptyBeat = new Beat { IsEmpty = true }; - emptyBeat.ChordId = _currentChord; - emptyVoice.AddBeat(emptyBeat); - } - } - } - - var attributesParsed = false; - - foreach (var c in element.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "note": - ParseNoteBeat(c, bars); - break; - case "forward": - ParseForward(c, bars); - break; - case "direction": - ParseDirection(c, masterBar); - break; - case "attributes": - if (!attributesParsed) - { - ParseAttributes(c, bars, masterBar, track); - attributesParsed = true; - } - break; - case "harmony": - ParseHarmony(c, track); - break; - case "sound": - // TODO - break; - case "barline": - ParseBarline(c, masterBar); - break; - } - } - } - - return true; - } - - private Beat GetOrCreateBeat(XmlNode element, Bar[] bars, bool chord) - { - int voiceIndex = 0; - var voiceNodes = element.GetElementsByTagName("voice"); - if (voiceNodes.Length > 0) - { - voiceIndex = Platform.Platform.ParseInt(voiceNodes[0].InnerText) - 1; - - if (_firstVoice == -1) - { - _firstVoice = voiceIndex; - voiceIndex = 0; - } - } - - var previousBeatWasPulled = _previousBeatWasPulled; - _previousBeatWasPulled = false; - var staffElement = element.GetElementsByTagName("staff"); - int staff = 1; - if (staffElement.Length > 0) - { - staff = Platform.Platform.ParseInt(staffElement[0].InnerText); - - // in case we have a beam with a staff-jump we pull the note to the previous staff - if ((_isBeamContinue || previousBeatWasPulled) && _previousBeat.Voice.Bar.Staff.Index != staff - 1) - { - staff = _previousBeat.Voice.Bar.Staff.Index + 1; - _previousBeatWasPulled = true; - } - - var staffId = bars[0].Staff.Track.Index + "-" + staff; - if (!_voiceOfStaff.ContainsKey(staffId)) - { - _voiceOfStaff[staffId] = voiceIndex; - } - voiceIndex -= _voiceOfStaff[staffId]; - } - var bar = bars[staff - 1]; - - Beat beat; - var voice = GetOrCreateVoice(bar, voiceIndex); - if (chord || (voice.Beats.Count == 1 && voice.IsEmpty)) - { - beat = voice.Beats[voice.Beats.Count - 1]; - } - else - { - beat = new Beat(); - beat.IsEmpty = false; - voice.AddBeat(beat); - } - - _isBeamContinue = false; - _previousBeat = beat; - - return beat; - } - - private void ParseForward(XmlNode element, Bar[] bars) - { - var beat = GetOrCreateBeat(element, bars, false); - var durationInDivisions = Platform.Platform.ParseInt(element.FindChildElement("duration").InnerText); - - var duration = (durationInDivisions * (int)Duration.Quarter) / (float)_divisionsPerQuarterNote; - - var durations = new[] - { - (int) Duration.SixtyFourth, - (int) Duration.ThirtySecond, - (int) Duration.Sixteenth, - (int) Duration.Eighth, - (int) Duration.Quarter, - (int) Duration.Half, - (int) Duration.Whole - }; - foreach (var d in durations) - { - if (duration >= d) - { - beat.Duration = (Duration)d; - duration -= d; - break; - } - } - - if (duration > 0) - { - // TODO: Handle remaining duration - // (additional beats, dotted durations,...) - } - - beat.IsEmpty = false; - } - - private void ParseStaffDetails(XmlNode element, Track track) - { - foreach (var c in element.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "staff-lines": - foreach (var staff in track.Staves) - { - staff.Tuning = new int[Platform.Platform.ParseInt(c.InnerText)]; - } - break; - case "staff-tuning": - ParseStaffTuning(c, track); - break; - } - } - } - - foreach (var staff in track.Staves) - { - if (IsEmptyTuning(staff.Tuning)) - { - staff.Tuning = new int[0]; - } - } - } - - private void ParseStaffTuning(XmlNode element, Track track) - { - var line = Platform.Platform.ParseInt(element.GetAttribute("line")); - string tuningStep = "C"; - string tuningOctave = ""; - int tuningAlter = 0; - foreach (var c in element.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "tuning-step": - tuningStep = c.InnerText; - break; - case "tuning-alter": - tuningAlter = Platform.Platform.ParseInt(c.InnerText); - break; - case "tuning-octave": - tuningOctave = c.InnerText; - break; - } - } - } - - var tuning = TuningParser.GetTuningForText(tuningStep + tuningOctave) + tuningAlter; - foreach (var staff in track.Staves) - { - staff.Tuning[staff.Tuning.Length - line] = tuning; - } - } - - private string _currentChord; - private int _divisionsPerQuarterNote; - - private void ParseHarmony(XmlNode element, Track track) - { - string rootStep = null; - string rootAlter = ""; - string kind = null; - string kindText = null; - - foreach (var c in element.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "root": - foreach (var rootChild in c.ChildNodes) - { - if (rootChild.NodeType == XmlNodeType.Element) - { - switch (rootChild.LocalName) - { - case "root-step": - rootStep = rootChild.InnerText; - break; - case "root-alter": - switch (Platform.Platform.ParseInt(c.InnerText)) - { - case -2: - rootAlter = " bb"; - break; - case -1: - rootAlter = " b"; - break; - case 0: - rootAlter = ""; - break; - case 1: - rootAlter = " #"; - break; - case 2: - rootAlter = " ##"; - break; - } - break; - } - } - } - break; - case "kind": - kindText = c.GetAttribute("text"); - kind = c.InnerText; - break; - } - } - } - - var chord = new Chord(); - chord.Name = rootStep + rootAlter; - // TODO: find proper names for the rest - //switch (kind) - //{ - // // triads - // case "major": - // break; - // case "minor": - // chord.Name += "m"; - // break; - // // Sevenths - // case "augmented": - // break; - // case "diminished": - // break; - // case "dominant": - // break; - // case "major-seventh": - // chord.Name += "7M"; - // break; - // case "minor-seventh": - // chord.Name += "m7"; - // break; - // case "diminished-seventh": - // break; - // case "augmented-seventh": - // break; - // case "half-diminished": - // break; - // case "major-minor": - // break; - // // Sixths - // case "major-sixth": - // break; - // case "minor-sixth": - // break; - // // Ninths - // case "dominant-ninth": - // break; - // case "major-ninth": - // break; - // case "minor-ninth": - // break; - // // 11ths - // case "dominant-11th": - // break; - // case "major-11th": - // break; - // case "minor-11th": - // break; - // // 13ths - // case "dominant-13th": - // break; - // case "major-13th": - // break; - // case "minor-13th": - // break; - // // Suspended - // case "suspended-second": - // break; - // case "suspended-fourth": - // break; - // // Functional sixths - // case "Neapolitan": - // break; - // case "Italian": - // break; - // case "French": - // break; - // case "German": - // break; - // // Other - // case "pedal": - // break; - // case "power": - // break; - // case "Tristan": - // break; - //} - - //var degree = element.GetElementsByTagName("degree"); - //if (degree.Length > 0) - //{ - // var degreeValue = Platform.GetNodeValue(degree[0].GetElementsByTagName("degree-value")[0]); - // var degreeAlter = Platform.GetNodeValue(degree[0].GetElementsByTagName("degree-alter")[0]); - // var degreeType = Platform.GetNodeValue(degree[0].GetElementsByTagName("degree-type")[0]); - - // if (!string.IsNullOrEmpty(degreeType)) - // { - // chord.Name += degreeType; - // } - - // if (!string.IsNullOrEmpty(degreeValue)) - // { - // chord.Name += "#" + degreeValue; - // } - //} - - _currentChord = Platform.Platform.NewGuid(); - foreach (var staff in track.Staves) - { - staff.Chords[_currentChord] = chord; - } - } - - private void ParseBarline(XmlNode element, MasterBar masterBar) - { - foreach (var c in element.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "repeat": - ParseRepeat(c, masterBar); - break; - case "ending": - ParseEnding(c, masterBar); - break; - } - } - } - } - - private void ParseEnding(XmlNode element, MasterBar masterBar) - { - var number = Platform.Platform.ParseInt(element.GetAttribute("number")); - if (number > 0) - { - --number; - masterBar.AlternateEndings |= (byte)(0x01 << number); - } - } - - private void ParseRepeat(XmlNode element, MasterBar masterBar) - { - var direction = element.GetAttribute("direction"); - var times = Platform.Platform.ParseInt(element.GetAttribute("times")); - if (times < 0) - { - times = 2; - } - - if (direction == "backward") - { - masterBar.RepeatCount = times; - } - else if (direction == "forward") - { - masterBar.IsRepeatStart = true; - } - } - - private FastDictionary _voiceOfStaff = new FastDictionary(); - private bool _isBeamContinue; - private bool _previousBeatWasPulled; - private Beat _previousBeat; - - private void ParseNoteBeat(XmlNode element, Bar[] bars) - { - var chord = element.GetElementsByTagName("chord").Length > 0; - var beat = GetOrCreateBeat(element, bars, chord); - - if (beat.ChordId == null && _currentChord != null) - { - beat.ChordId = _currentChord; - _currentChord = null; - } - - if (_currentDirection != null) - { - beat.Text = _currentDirection; - _currentDirection = null; - } - - - var note = new Note(); - beat.Voice.IsEmpty = false; - beat.IsEmpty = false; - beat.AddNote(note); - - beat.Dots = 0; - foreach (var c in element.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "grace": - //var slash = e.GetAttribute("slash"); - //var makeTime = Platform.ParseInt(e.GetAttribute("make-time")); - //var stealTimePrevious = Platform.ParseInt(e.GetAttribute("steal-time-previous")); - //var stealTimeFollowing = Platform.ParseInt(e.GetAttribute("steal-time-following")); - beat.GraceType = GraceType.BeforeBeat; - beat.Duration = Duration.ThirtySecond; - break; - case "duration": - if (beat.IsRest) - { - // unit: divisions per quarter note - var duration = Platform.Platform.ParseInt(c.InnerText); - switch (duration) - { - case 1: - beat.Duration = Duration.Whole; - break; - case 2: - beat.Duration = Duration.Half; - break; - case 4: - beat.Duration = Duration.Quarter; - break; - case 8: - beat.Duration = Duration.Eighth; - break; - case 16: - beat.Duration = Duration.Sixteenth; - break; - case 32: - beat.Duration = Duration.ThirtySecond; - break; - case 64: - beat.Duration = Duration.SixtyFourth; - break; - default: - beat.Duration = Duration.Quarter; - break; - } - } - break; - case "tie": - ParseTied(c, note); - break; - case "cue": - // not supported - break; - case "instrument": - // not supported - break; - case "type": - beat.Duration = GetDuration(c.InnerText); - if (beat.GraceType != GraceType.None && beat.Duration < Duration.Sixteenth) - { - beat.Duration = Duration.Eighth; - } - break; - case "dot": - beat.Dots++; - break; - case "accidental": - ParseAccidental(c, note); - break; - case "time-modification": - ParseTimeModification(c, beat); - break; - case "stem": - // not supported - break; - case "notehead": - if (c.GetAttribute("parentheses") == "yes") - { - note.IsGhost = true; - } - break; - case "beam": - var beamMode = c.InnerText; - if (beamMode == "continue") - { - _isBeamContinue = true; - } - break; - case "notations": - ParseNotations(c, beat, note); - break; - case "lyric": - ParseLyric(c, beat); - break; - // "full-note" - case "pitch": - ParsePitch(c, note); - break; - case "unpitched": - ParseUnpitched(c, note); - break; - case "rest": - beat.IsEmpty = false; - beat.Notes = new FastList(); - break; - } - } - } - - // check if new note is duplicate on string - if (note.IsStringed) - { - for (int i = 0; i < beat.Notes.Count; i++) - { - if (beat.Notes[i].String == note.String && beat.Notes[i] != note) - { - beat.RemoveNote(note); - break; - } - } - } - } - - private Duration GetDuration(string text) - { - switch (text) - { - case "256th": - case "128th": - case "64th": - return Duration.SixtyFourth; - case "32nd": - return Duration.ThirtySecond; - case "16th": - return Duration.Sixteenth; - case "eighth": - return Duration.Eighth; - case "quarter": - return Duration.Quarter; - case "half": - return Duration.Half; - case "long": - case "breve": - case "whole": - return Duration.Whole; - } - - return Duration.Quarter; - } - - private void ParseLyric(XmlNode element, Beat beat) - { - foreach (var c in element.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "text": - if (!string.IsNullOrEmpty(beat.Text)) - { - beat.Text += " " + c.InnerText; - } - else - { - beat.Text = c.InnerText; - } - break; - } - } - } - } - - private void ParseAccidental(XmlNode element, Note note) - { - switch (element.InnerText) - { - case "sharp": - note.AccidentalMode = NoteAccidentalMode.ForceSharp; - break; - case "natural": - note.AccidentalMode = NoteAccidentalMode.ForceNatural; - break; - case "flat": - note.AccidentalMode = NoteAccidentalMode.ForceFlat; - break; - case "double-sharp": - break; - case "sharp-sharp": - break; - case "flat-flat": - break; - case "natural-sharp": - break; - case "natural-flat": - break; - case "quarter-flat": - break; - case "quarter-sharp": - break; - case "three-quarters-flat": - break; - case "three-quarters-sharp": - break; - } - } - - private void ParseTied(XmlNode element, Note note) - { - if (note.Beat.GraceType != GraceType.None) return; - - if (element.GetAttribute("type") == "start") - { - note.IsTieOrigin = true; - _tieStarts.Add(note); - } - else if (element.GetAttribute("type") == "stop" && _tieStarts.Count > 0) - { - note.TieDestination = _tieStarts[0]; - note.IsTieDestination = true; - _tieStarts.RemoveAt(0); - } - } - - private void ParseNotations(XmlNode element, Beat beat, Note note) - { - foreach (var c in element.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "articulations": - ParseArticulations(c, note); - break; - case "tied": - ParseTied(c, note); - break; - case "slide": - case "glissando": - if (c.GetAttribute("type") == "start") - { - note.SlideType = SlideType.Shift; - } - break; - case "dynamics": - ParseDynamics(c, beat); - break; - case "technical": - ParseTechnical(c, note); - break; - case "ornaments": - ParseOrnaments(c, note); - break; - case "slur": - if (c.GetAttribute("type") == "start") - { - beat.IsLegatoOrigin = true; - } - break; - } - } - } - } - - private void ParseOrnaments(XmlNode element, Note note) - { - foreach (var c in element.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "tremolo": - var tremoloSpeed = Platform.Platform.ParseInt(c.InnerText); - switch (tremoloSpeed) - { - case 1: - note.Beat.TremoloSpeed = Duration.Eighth; - break; - case 2: - note.Beat.TremoloSpeed = Duration.Sixteenth; - break; - case 3: - note.Beat.TremoloSpeed = Duration.ThirtySecond; - break; - } - break; - } - } - } - } - - private void ParseTechnical(XmlNode element, Note note) - { - foreach (var c in element.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "string": - note.String = Platform.Platform.ParseInt(c.InnerText); - if (note.String != int.MinValue) - { - note.String = note.Beat.Voice.Bar.Staff.Tuning.Length - note.String + 1; - } - break; - case "fret": - note.Fret = Platform.Platform.ParseInt(c.InnerText); - break; - case "down-bow": - note.Beat.PickStroke = PickStroke.Down; - break; - case "up-bow": - note.Beat.PickStroke = PickStroke.Up; - break; - } - } - } - - if (note.String == int.MinValue || note.Fret == int.MinValue) - { - note.String = -1; - note.Fret = -1; - } - } - - private void ParseArticulations(XmlNode element, Note note) - { - foreach (var c in element.ChildNodes) - { - switch (c.LocalName) - { - case "accent": - note.Accentuated = AccentuationType.Normal; - break; - case "strong-accent": - note.Accentuated = AccentuationType.Heavy; - break; - case "staccato": - case "detached-legato": - note.IsStaccato = true; - break; - } - } - } - - private void ParseDynamics(XmlNode element, Beat beat) - { - foreach (var c in element.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "p": - beat.Dynamic = DynamicValue.P; - break; - case "pp": - beat.Dynamic = DynamicValue.PP; - break; - case "ppp": - beat.Dynamic = DynamicValue.PPP; - break; - case "f": - beat.Dynamic = DynamicValue.F; - break; - case "ff": - beat.Dynamic = DynamicValue.FF; - break; - case "fff": - beat.Dynamic = DynamicValue.FFF; - break; - case "mp": - beat.Dynamic = DynamicValue.MP; - break; - case "mf": - beat.Dynamic = DynamicValue.MF; - break; - } - } - } - } - - private void ParseTimeModification(XmlNode element, Beat beat) - { - foreach (var c in element.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "actual-notes": - beat.TupletNumerator = Platform.Platform.ParseInt(c.InnerText); - break; - case "normal-notes": - beat.TupletDenominator = Platform.Platform.ParseInt(c.InnerText); - break; - //case "normal-type": - // break; - //case "normal-dot": - // break; - } - } - } - } - - private void ParseUnpitched(XmlNode element, Note note) - { - string step = null; - int semitones = 0; - int octave = 0; - foreach (var c in element.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "display-step": - step = c.InnerText; - break; - case "display-alter": - semitones = Platform.Platform.ParseInt(c.InnerText); - break; - case "display-octave": - // 0-9, 4 for middle C - octave = Platform.Platform.ParseInt(c.InnerText); - break; - } - } - } - - var value = octave * 12 + TuningParser.GetToneForText(step) + semitones; - - note.Octave = (value / 12); - note.Tone = value - (note.Octave * 12); - } - - private void ParsePitch(XmlNode element, Note note) - { - string step = null; - float semitones = 0; - int octave = 0; - foreach (var c in element.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "step": - step = c.InnerText; - break; - case "alter": - semitones = Platform.Platform.ParseFloat(c.InnerText); - if (double.IsNaN(semitones)) - { - semitones = 0; - } - break; - case "octave": - // 0-9, 4 for middle C - octave = Platform.Platform.ParseInt(c.InnerText); - break; - } - } - } - - var value = octave * 12 + TuningParser.GetToneForText(step) + (int)semitones; - - note.Octave = (value / 12); - note.Tone = value - (note.Octave * 12); - } - - private Voice GetOrCreateVoice(Bar bar, int index) - { - if (index < bar.Voices.Count) - { - return bar.Voices[index]; - } - - for (int i = bar.Voices.Count; i <= index; i++) - { - bar.AddVoice(new Voice()); - } - - _maxVoices = Math.Max(_maxVoices, bar.Voices.Count); - - return bar.Voices[index]; - } - - private void ParseDirection(XmlNode element, MasterBar masterBar) - { - foreach (var c in element.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "sound": - var tempo = c.GetAttribute("tempo"); - if (!string.IsNullOrEmpty(tempo)) - { - var tempoAutomation = new Automation(); - tempoAutomation.IsLinear = true; - tempoAutomation.Type = AutomationType.Tempo; - tempoAutomation.Value = Platform.Platform.ParseInt(tempo); - masterBar.TempoAutomation = tempoAutomation; - } - break; - case "direction-type": - var directionType = c.FirstElement; - switch (directionType.LocalName) - { - case "words": - _currentDirection = directionType.InnerText; - break; - case "metronome": - ParseMetronome(c.FirstElement, masterBar); - break; - } - break; - } - } - } - } - - private void ParseMetronome(XmlNode element, MasterBar masterBar) - { - var unit = Duration.Quarter; - int perMinute = 120; - foreach (var c in element.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "beat-unit": - unit = GetDuration(c.InnerText); - break; - case "per-minute": - perMinute = Platform.Platform.ParseInt(c.InnerText); - break; - } - } - } - - var tempoAutomation = masterBar.TempoAutomation = new Automation(); - tempoAutomation.Type = AutomationType.Tempo; - tempoAutomation.Value = perMinute * ((int)unit / 4); - } - - private void ParseAttributes(XmlNode element, Bar[] bars, MasterBar masterBar, Track track) - { - int number; - bool hasTime = false; - foreach (var c in element.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "divisions": - _divisionsPerQuarterNote = Platform.Platform.ParseInt(c.InnerText); - break; - case "key": - ParseKey(c, masterBar); - break; - case "time": - ParseTime(c, masterBar); - hasTime = true; - break; - case "clef": - number = Platform.Platform.ParseInt(c.GetAttribute("number")); - if (number == int.MinValue) - { - number = 1; - } - ParseClef(c, bars[number - 1]); - break; - case "staff-details": - ParseStaffDetails(c, track); - break; - case "transpose": - ParseTranspose(c, track); - break; - } - } - } - - if (!hasTime) - { - masterBar.TimeSignatureCommon = true; - } - } - - private void ParseTranspose(XmlNode element, Track track) - { - int semitones = 0; - foreach (var c in element.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "chromatic": - semitones += Platform.Platform.ParseInt(c.InnerText); - break; - case "octave-change": - semitones += Platform.Platform.ParseInt(c.InnerText) * 12; - break; - } - } - } - - foreach (var staff in track.Staves) - { - staff.TranspositionPitch = semitones; - } - } - - private void ParseClef(XmlNode element, Bar bar) - { - string sign = null; - int line = 0; - foreach (var c in element.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "sign": - sign = c.InnerText.ToLower(); - break; - case "line": - line = Platform.Platform.ParseInt(c.InnerText); - break; - case "clef-octave-change": - switch (Platform.Platform.ParseInt(c.InnerText)) - { - case -2: - bar.ClefOttava = Ottavia._15mb; - break; - case -1: - bar.ClefOttava = Ottavia._8vb; - break; - case 1: - bar.ClefOttava = Ottavia._8va; - break; - case 2: - bar.ClefOttava = Ottavia._15mb; - break; - } - break; - } - } - } - - switch (sign) - { - case "G": - bar.Clef = Clef.G2; - break; - case "F": - bar.Clef = Clef.F4; - break; - case "C": - if (line == 3) - { - bar.Clef = Clef.C3; - } - else - { - bar.Clef = Clef.C4; - } - break; - case "percussion": - bar.Clef = Clef.Neutral; - bar.Staff.StaffKind = StaffKind.Percussion; - break; - case "tab": - bar.Clef = Clef.G2; - bar.Staff.StaffKind = StaffKind.Tablature; - break; - default: - bar.Clef = Clef.G2; - break; - } - } - - private void ParseTime(XmlNode element, MasterBar masterBar) - { - if (element.GetAttribute("symbol") == "common") - { - masterBar.TimeSignatureCommon = true; - } - bool beatsParsed = false; - bool beatTypeParsed = false; - foreach (var c in element.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - var v = c.InnerText; - switch (c.LocalName) - { - case "beats": - if (!beatsParsed) - { - if (!v.Contains("+")) // compound TS - { - masterBar.TimeSignatureNumerator = Platform.Platform.ParseInt(v); - } - else - { - masterBar.TimeSignatureNumerator = 4; - } - beatsParsed = true; - } - break; - case "beat-type": - if (!beatTypeParsed) - { - if (!v.Contains("+")) // compound TS - { - masterBar.TimeSignatureDenominator = Platform.Platform.ParseInt(v); - } - else - { - masterBar.TimeSignatureDenominator = 4; - } - beatTypeParsed = true; - } - break; - } - } - } - } - - private void ParseKey(XmlNode element, MasterBar masterBar) - { - int fifths = int.MinValue; - int keyStep = int.MinValue; - int keyAlter = int.MinValue; - string mode = null; - foreach (var c in element.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "fifths": - fifths = Platform.Platform.ParseInt(c.InnerText); - break; - case "key-step": - keyStep = Platform.Platform.ParseInt(c.InnerText); - break; - case "key-alter": - keyAlter = Platform.Platform.ParseInt(c.InnerText); - break; - case "mode": - mode = c.InnerText; - break; - } - } - } - - if (-7 <= fifths && fifths <= 7) - { - // TODO: check if this is conrrect - masterBar.KeySignature = (KeySignature)fifths; - } - else - { - masterBar.KeySignature = 0; - // TODO: map keyStep/keyAlter to internal keysignature - } - - if (mode == "minor") - { - masterBar.KeySignatureType = KeySignatureType.Minor; - } - else - { - masterBar.KeySignatureType = KeySignatureType.Major; - } - } - - private MasterBar GetOrCreateMasterBar(int index) - { - if (index < _score.MasterBars.Count) - { - return _score.MasterBars[index]; - } - - for (int i = _score.MasterBars.Count; i <= index; i++) - { - var mb = new MasterBar(); - if (_score.MasterBars.Count > 0) - { - var prev = _score.MasterBars[_score.MasterBars.Count - 1]; - mb.TimeSignatureDenominator = prev.TimeSignatureDenominator; - mb.TimeSignatureNumerator = prev.TimeSignatureNumerator; - mb.KeySignature = prev.KeySignature; - mb.KeySignatureType = prev.KeySignatureType; - } - - - _score.AddMasterBar(mb); - } - - return _score.MasterBars[index]; - } - - private void ParseIdentification(XmlNode element) - { - foreach (var c in element.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "creator": - if (c.GetAttribute("type") == "composer") - { - _score.Music = c.InnerText; - } - break; - case "rights": - if (!string.IsNullOrEmpty(_score.Copyright)) - { - _score.Copyright += "\n"; - } - _score.Copyright += c.InnerText; - break; - } - } - } - } - - private void ParsePartList(XmlNode element) - { - foreach (var c in element.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "part-group": - ParsePartGroup(c); - break; - case "score-part": - ParseScorePart(c); - break; - } - } - } - - } - - private void ParsePartGroup(XmlNode element) - { - var type = element.GetAttribute("type"); - switch (type) - { - case "start": - _currentPartGroup = element.GetAttribute("number"); - _partGroups[_currentPartGroup] = new FastList(); - break; - case "stop": - _currentPartGroup = null; - break; - } - } - - private void ParseScorePart(XmlNode element) - { - string id = element.GetAttribute("id"); - - Track track = new Track(1); - var staff = track.Staves[0]; - staff.StaffKind = StaffKind.Score; - _trackById[id] = track; - _score.AddTrack(track); - - if (_currentPartGroup != null) - { - _partGroups[_currentPartGroup].Add(track); - } - - foreach (var c in element.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "part-name": - track.Name = c.InnerText; - break; - case "part-abbreviation": - track.ShortName = c.InnerText; - break; - case "midi-instrument": - ParseMidiInstrument(c, track); - break; - } - } - } - - if (IsEmptyTuning(track.Staves[0].Tuning)) - { - track.Staves[0].Tuning = new int[0]; - } - } - - private bool IsEmptyTuning(int[] tuning) - { - if (tuning == null) - { - return true; - } - - for (int i = 0; i < tuning.Length; i++) - { - if (tuning[i] != 0) - { - return false; - } - } - return true; - } - - private void ParseMidiInstrument(XmlNode element, Track track) - { - foreach (var c in element.ChildNodes) - { - if (c.NodeType == XmlNodeType.Element) - { - switch (c.LocalName) - { - case "midi-channel": - track.PlaybackInfo.PrimaryChannel = Platform.Platform.ParseInt(c.InnerText); - break; - case "midi-program": - track.PlaybackInfo.Program = Platform.Platform.ParseInt(c.InnerText); - break; - case "midi-volume": - track.PlaybackInfo.Volume = Platform.Platform.ParseInt(c.InnerText); - break; - } - } - } - } - - - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Importer/NoCompatibleReaderFoundException.cs b/Source/AlphaTab/Importer/NoCompatibleReaderFoundException.cs deleted file mode 100644 index 19d321553..000000000 --- a/Source/AlphaTab/Importer/NoCompatibleReaderFoundException.cs +++ /dev/null @@ -1,32 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Importer -{ - /// - /// An exception indicating no reader for importing a file could be found. - /// - public class NoCompatibleReaderFoundException : AlphaTabException - { - /// - /// Initializes a new instance of the class. - /// - public NoCompatibleReaderFoundException() : base("No compatible reader found") - { - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Importer/ScoreImporter.cs b/Source/AlphaTab/Importer/ScoreImporter.cs deleted file mode 100644 index b76c0812a..000000000 --- a/Source/AlphaTab/Importer/ScoreImporter.cs +++ /dev/null @@ -1,93 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.IO; -using AlphaTab.Model; - -namespace AlphaTab.Importer -{ - /// - /// This is the base public class for creating new song importers which - /// enable reading scores from any binary datasource - /// - public abstract class ScoreImporter - { - /// - /// The raw data to read from. - /// - protected IReadable Data; - /// - /// The settings to use during the import. - /// - protected Settings Settings; - - /** - * Gets all default ScoreImporters - * @return - */ - public static ScoreImporter[] BuildImporters() - { - return new ScoreImporter[] - { - new Gp3To5Importer(), - new GpxImporter(), - new Gp7Importer(), - new AlphaTexImporter(), - new MusicXmlImporter() - }; - } - - /// - /// Initializes the importer with the given data and settings. - /// - /// - /// - public void Init(IReadable data, Settings settings = null) - { - Data = data; - Settings = settings; - } - - /// - /// Gets the importer specific setting using the specified key. - /// - /// The key of the setting to load the value for. - /// The default value to load if no setting was specified. - /// The importer setting specified by the user, or the given defaultValue if the key was not contained. - protected T GetSetting(string key, T defaultValue = default(T)) - { - key = key.ToLower(); - if (Settings == null || Settings.ImporterSettings == null || !Settings.ImporterSettings.ContainsKey(key)) - { - return defaultValue; - } - - return (T)Settings.ImporterSettings[key]; - } - - /// - /// Get the human readable name of the importer. - /// - public abstract string Name { get; } - - /// - /// Reads the contained in the data. - /// - /// The score that was contained in the data. - public abstract Score ReadScore(); - } -} diff --git a/Source/AlphaTab/Importer/ScoreLoader.cs b/Source/AlphaTab/Importer/ScoreLoader.cs deleted file mode 100644 index b811e3920..000000000 --- a/Source/AlphaTab/Importer/ScoreLoader.cs +++ /dev/null @@ -1,78 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.Collections; -using AlphaTab.IO; -using AlphaTab.Model; -using AlphaTab.Platform; -using AlphaTab.Util; - -namespace AlphaTab.Importer -{ - /// - /// The ScoreLoader enables you easy loading of Scores using all - /// available importers - /// - public partial class ScoreLoader - { - /// - /// Loads the score from the given binary data. - /// - /// The binary data containing a score in any known file format. - /// The settings to use during importing. - /// The loaded score. - public static Score LoadScoreFromBytes(byte[] data, Settings settings = null) - { - var importers = ScoreImporter.BuildImporters(); - - Logger.Info("ScoreLoader", "Loading score from " + data.Length + " bytes using " + importers.Length + " importers"); - - Score score = null; - ByteBuffer bb = ByteBuffer.FromBuffer(data); - foreach (var importer in importers) - { - bb.Reset(); - try - { - Logger.Info("ScoreLoader", "Importing using importer " + importer.Name); - importer.Init(bb, settings); - score = importer.ReadScore(); - Logger.Info("ScoreLoader", "Score imported using " + importer.Name); - break; - } - catch (UnsupportedFormatException) - { - Logger.Info("ScoreLoader", importer.Name + " does not support the file"); - } - catch (Exception e) - { - Logger.Info("ScoreLoader", "Score import failed due to unexpected error: " + e); - throw; - } - } - - if (score != null) - { - return score; - } - - Logger.Error("ScoreLoader", "No compatible importer found for file"); - throw new NoCompatibleReaderFoundException(); - } - } -} diff --git a/Source/AlphaTab/Importer/UnsupportedFormatException.cs b/Source/AlphaTab/Importer/UnsupportedFormatException.cs deleted file mode 100644 index 2142436ae..000000000 --- a/Source/AlphaTab/Importer/UnsupportedFormatException.cs +++ /dev/null @@ -1,35 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Importer -{ - /// - /// The exception thrown by a in case the - /// binary data does not contain a reader compatible structure. - /// - public class UnsupportedFormatException : AlphaTabException - { - /// - /// Initializes a new instance of the class. - /// - /// The message that describes the error. - public UnsupportedFormatException(string message = "Unsupported format") - : base(message) - { - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Model/AccentuationType.cs b/Source/AlphaTab/Model/AccentuationType.cs deleted file mode 100644 index 3868c84dd..000000000 --- a/Source/AlphaTab/Model/AccentuationType.cs +++ /dev/null @@ -1,38 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Model -{ - /// - /// Lists all types of note acceuntations - /// - public enum AccentuationType - { - /// - /// No accentuation - /// - None, - /// - /// Normal accentuation - /// - Normal, - /// - /// Heavy accentuation - /// - Heavy - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Model/AccidentalType.cs b/Source/AlphaTab/Model/AccidentalType.cs deleted file mode 100644 index 4fe3149c6..000000000 --- a/Source/AlphaTab/Model/AccidentalType.cs +++ /dev/null @@ -1,54 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Model -{ - /// - /// Defines all possible accidentals for notes. - /// - public enum AccidentalType - { - /// - /// No accidental - /// - None, - /// - /// Naturalize - /// - Natural, - /// - /// Sharp - /// - Sharp, - /// - /// Flat - /// - Flat, - /// - /// Natural for smear bends - /// - NaturalQuarterNoteUp, - /// - /// Sharp for smear bends - /// - SharpQuarterNoteUp, - /// - /// Flat for smear bends - /// - FlatQuarterNoteUp - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Model/Automation.cs b/Source/AlphaTab/Model/Automation.cs deleted file mode 100644 index ca11a7d3d..000000000 --- a/Source/AlphaTab/Model/Automation.cs +++ /dev/null @@ -1,76 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -namespace AlphaTab.Model -{ - /// - /// Automations are used to change the behaviour of a song. - /// - public class Automation - { - /// - /// Gets or sets whether the automation is applied linear. - /// - public bool IsLinear { get; set; } - /// - /// Gets or sets the type of the automation. - /// - public AutomationType Type { get; set; } - /// - /// Gets or sets the target value of the automation. - /// - public float Value { get; set; } - /// - /// Gets or sets the relative position of of the automation. - /// - public float RatioPosition { get; set; } - /// - /// Gets or sets the additional text of the automation. s - /// - public string Text { get; set; } - - internal static Automation BuildTempoAutomation(bool isLinear, float ratioPosition, float value, int reference) - { - if (reference < 1 || reference > 5) reference = 2; - - var references = new[] {1f, 0.5f, 1.0f, 1.5f, 2.0f, 3.0f}; - var automation = new Automation(); - automation.Type = AutomationType.Tempo; - automation.IsLinear = isLinear; - automation.RatioPosition = ratioPosition; - automation.Value = value*references[reference]; - return automation; - } - - internal static void CopyTo(Automation src, Automation dst) - { - dst.IsLinear = src.IsLinear; - dst.RatioPosition = src.RatioPosition; - dst.Text = src.Text; - dst.Type = src.Type; - dst.Value = src.Value; - } - - internal Automation Clone() - { - var a = new Automation(); - CopyTo(this, a); - return a; - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Model/AutomationType.cs b/Source/AlphaTab/Model/AutomationType.cs deleted file mode 100644 index 86ee4a0f3..000000000 --- a/Source/AlphaTab/Model/AutomationType.cs +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Model -{ - /// - /// This public enumeration lists all types of automations. - /// - public enum AutomationType - { - /// - /// Tempo change. - /// - Tempo, - /// - /// Colume change. - /// - Volume, - /// - /// Instrument change. - /// - Instrument, - /// - /// Balance change. - /// - Balance - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Model/Bar.cs b/Source/AlphaTab/Model/Bar.cs deleted file mode 100644 index 9e2ddbe98..000000000 --- a/Source/AlphaTab/Model/Bar.cs +++ /dev/null @@ -1,137 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Collections; - -namespace AlphaTab.Model -{ - /// - /// A bar is a single block within a track, also known as Measure. - /// - public class Bar - { - /// - /// This is a global counter for all beats. We use it - /// at several locations for lookup tables. - /// - private static int GlobalBarId = 0; - - /// - /// Gets or sets the unique id of this bar. - /// - public int Id { get; set; } - /// - /// Gets or sets the zero-based index of this bar within the staff. - /// - public int Index { get; set; } - - /// - /// Gets or sets the next bar that comes after this bar. - /// - public Bar NextBar { get; set; } - /// - /// Gets or sets the previous bar that comes before this bar. - /// - public Bar PreviousBar { get; set; } - - /// - /// Gets or sets the clef on this bar. - /// - public Clef Clef { get; set; } - - /// - /// Gets or sets the ottava applied to the clef. - /// - public Ottavia ClefOttava { get; set; } - - /// - /// Gets or sets the reference to the parent staff. - /// - public Staff Staff { get; set; } - - /// - /// Gets or sets the list of voices contained in this bar. - /// - public FastList Voices { get; set; } - - /// - /// Gets or sets the simile mark on this bar. - /// - public SimileMark SimileMark { get; set; } - - /// - /// Gets the masterbar for this bar. - /// - public MasterBar MasterBar => Staff.Track.Score.MasterBars[Index]; - - /// - /// Gets a value indicating whether all voices in this bar are empty and therefore the whole bar is empty. - /// - public bool IsEmpty - { - get - { - for (int i = 0, j = Voices.Count; i < j; i++) - { - if (!Voices[i].IsEmpty) - { - return false; - } - } - return true; - } - } - - /// - /// Initializes a new instance of the class. - /// - public Bar() - { - Id = GlobalBarId++; - Voices = new FastList(); - Clef = Clef.G2; - ClefOttava = Ottavia.Regular; - SimileMark = SimileMark.None; - } - - internal static void CopyTo(Bar src, Bar dst) - { - dst.Id = src.Id; - dst.Index = src.Index; - dst.Clef = src.Clef; - dst.ClefOttava = src.ClefOttava; - dst.SimileMark = src.SimileMark; - } - - internal void AddVoice(Voice voice) - { - voice.Bar = this; - voice.Index = Voices.Count; - Voices.Add(voice); - } - - internal void Finish(Settings settings) - { - for (int i = 0, j = Voices.Count; i < j; i++) - { - var voice = Voices[i]; - voice.Finish(settings); - } - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Model/Beat.cs b/Source/AlphaTab/Model/Beat.cs deleted file mode 100644 index 5a2179ef5..000000000 --- a/Source/AlphaTab/Model/Beat.cs +++ /dev/null @@ -1,915 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Audio; -using AlphaTab.Collections; -using AlphaTab.Util; - -namespace AlphaTab.Model -{ - /// - /// A beat is a single block within a bar. A beat is a combination - /// of several notes played at the same time. - /// - public class Beat - { - /// - /// This is a global counter for all beats. We use it - /// at several locations for lookup tables. - /// - private static int GlobalBeatId = 0; - - /// - /// Gets or sets the unique id of this beat. - /// - public int Id { get; set; } - - /// - /// Gets or sets the zero-based index of this beat within the voice. - /// - public int Index { get; set; } - - /// - /// Gets or sets the previous beat within the whole song. - /// - public Beat PreviousBeat { get; set; } - /// - /// Gets or sets the next beat within the whole song. - /// - public Beat NextBeat { get; set; } - - /// - /// Gets a value indicating whether this beat is the last beat in the voice. - /// - public bool IsLastOfVoice => Index == Voice.Beats.Count - 1; - - /// - /// Gets or sets the reference to the parent voice this beat belongs to. - /// - public Voice Voice { get; set; } - - /// - /// Gets or sets the list of notes contained in this beat. - /// - public FastList Notes { get; set; } - - /// - /// Gets the lookup where the notes per string are registered. - /// If this staff contains string based notes this lookup allows fast access. - /// - public FastDictionary NoteStringLookup { get; } - - /// - /// Gets or sets a value indicating whether this beat is considered empty. - /// - public bool IsEmpty { get; set; } - - /// - /// Gets or sets which whammy bar style should be used for this bar. - /// - public BendStyle WhammyStyle { get; set; } - - /// - /// Gets or sets the ottava applied to this beat. - /// - public Ottavia Ottava { get; set; } - - /// - /// Gets or sets the fermata applied to this beat. - /// - public Fermata Fermata { get; set; } - - /// - /// Gets a value indicating whether this beat starts a legato slur. - /// - public bool IsLegatoOrigin { get; set; } - - /// - /// Gets a value indicating whether this beat ends a legato slur. - /// - public bool IsLegatoDestination => PreviousBeat != null && PreviousBeat.IsLegatoOrigin; - - /// - /// Gets or sets the note with the lowest pitch in this beat. Only visible notes are considered. - /// - public Note MinNote { get; set; } - /// - /// Gets or sets the note with the highest pitch in this beat. Only visible notes are considered. - /// - public Note MaxNote { get; set; } - /// - /// Gets or sets the note with the highest string number in this beat. Only visible notes are considered. - /// - public Note MaxStringNote { get; set; } - /// - /// Gets or sets the note with the lowest string number in this beat. Only visible notes are considered. - /// - public Note MinStringNote { get; set; } - - /// - /// Gets or sets the duration of this beat. - /// - public Duration Duration { get; set; } - - /// - /// Gets or sets whether this beat starts a slur. - /// - public bool IsSlurOrigin { get; set; } - /// - /// Gets or sets whether this beat ends or continues a slur. - /// - public bool IsSlurDestination => SlurOrigin != null; - /// - /// Gets or sets the slur origin beat - /// - public Beat SlurOrigin { get; set; } - /// - /// Gets or sets the slur destination beat. - /// - public Beat SlurDestination { get; set; } - - /// - /// Gets or sets whether this beat is considered as rest. - /// - public bool IsRest => IsEmpty || Notes.Count == 0; - - /// - /// Gets or sets whether any note in this beat has a let-ring applied. - /// - public bool IsLetRing { get; set; } - - /// - /// Gets or sets whether any note in this beat has a palm-mute paplied. - /// - public bool IsPalmMute { get; set; } - - /// - /// Gets or sets a list of all automations on this beat. - /// - public FastList Automations { get; set; } - - /// - /// Gets or sets the number of dots applied to the duration of this beat. - /// - public int Dots { get; set; } - - /// - /// Gets or sets a value indicating whether this beat is fade-in. - /// - public bool FadeIn { get; set; } - /// - /// Gets or sets the lyrics shown on this beat. - /// - public string[] Lyrics { get; set; } - - /// - /// Gets or sets a value indicating whether the beat is played in rasgueado style. - /// - public bool HasRasgueado { get; set; } - - /// - /// Gets or sets a value indicating whether the notes on this beat are played with a pop-style (bass). - /// - public bool Pop { get; set; } - /// - /// Gets or sets a value indicating whether the notes on this beat are played with a slap-style (bass). - /// - public bool Slap { get; set; } - /// - /// Gets or sets a value indicating whether the notes on this beat are played with a tap-style (bass). - /// - public bool Tap { get; set; } - - /// - /// Gets or sets the text annotation shown on this beat. - /// - public string Text { get; set; } - - /// - /// Gets or sets the brush type applied to the notes of this beat. - /// - public BrushType BrushType { get; set; } - - /// - /// Gets or sets the duration of the brush between the notes in midi ticks. - /// - public int BrushDuration { get; set; } - - /// - /// Gets or sets the tuplet denominator. - /// - public int TupletDenominator { get; set; } - /// - /// Gets or sets the tuplet numerator. - /// - public int TupletNumerator { get; set; } - - /// - /// Gets or sets whether there is a tuplet applied to the duration of this beat. - /// - public bool HasTuplet => !(TupletDenominator == -1 && TupletNumerator == -1) && - !(TupletDenominator == 1 && TupletNumerator == 1); - - /// - /// Gets or sets whether this beat continues a whammy effect. - /// - public bool IsContinuedWhammy { get; set; } - - /// - /// Gets or sets the whammy bar style of this beat. - /// - public WhammyType WhammyBarType { get; set; } - - /// - /// Gets or sets the points defining the whammy bar usage. - /// - public FastList WhammyBarPoints { get; set; } - - /// - /// Gets or sets the highest point with for the highest whammy bar value. - /// - public BendPoint MaxWhammyPoint { get; set; } - /// - /// Gets or sets the highest point with for the lowest whammy bar value. - /// - public BendPoint MinWhammyPoint { get; set; } - - /// - /// Gets a value indicating whether a whammy bar is used on this beat. - /// - public bool HasWhammyBar => WhammyBarType != WhammyType.None; - - /// - /// Gets or sets the vibrato effect used on this beat. - /// - public VibratoType Vibrato { get; set; } - - /// - /// Gets or sets the ID of the chord used on this beat. - /// - public string ChordId { get; set; } - - /// - /// Gets a value indicating whether a chord is used on this beat. - /// - public bool HasChord => ChordId != null; - - /// - /// Gets the chord used on this beat. - /// - public Chord Chord => Voice.Bar.Staff.Chords[ChordId]; - - /// - /// Gets or sets the grace style of this beat. - /// - public GraceType GraceType { get; set; } - - /// - /// Gets or sets the pickstroke applied on this beat. - /// - public PickStroke PickStroke { get; set; } - - /// - /// Gets whether a tremolo effect is played on this beat. - /// - public bool IsTremolo => TremoloSpeed != null; - - /// - /// Gets or sets the speed of the tremolo effect. - /// - public Duration? TremoloSpeed { get; set; } - - /// - /// Gets or sets whether a crescendo/decrescendo is applied on this beat. - /// - public CrescendoType Crescendo { get; set; } - - /// - /// The timeline position of the voice within the current bar as it is displayed. (unit: midi ticks) - /// - /// - /// This might differ from the actual playback time due to special grace types. - /// - public int DisplayStart { get; set; } - - /// - /// The timeline position of the voice within the current bar as it is played. (unit: midi ticks) - /// - /// - /// This might differ from the actual playback time due to special grace types. - /// - public int PlaybackStart { get; set; } - - /// - /// Gets or sets the duration that is used for the display of this beat. It defines the size/width of the beat in - /// the music sheet. (unit: midi ticks). - /// - public int DisplayDuration { get; set; } - - /// - /// Gets or sets the duration that the note is played during the audio generation. - /// - public int PlaybackDuration { get; set; } - - /// - /// Gets the absolute display start time within the song. - /// - public int AbsoluteDisplayStart => Voice.Bar.MasterBar.Start + DisplayStart; - - /// - /// Gets the absolute playback start time within the song. - /// - public int AbsolutePlaybackStart => Voice.Bar.MasterBar.Start + PlaybackStart; - - /// - /// Gets or sets the dynamics applied to this beat. - /// - public DynamicValue Dynamic { get; set; } - - /// - /// Gets or sets a value indicating whether the beam direction should be inverted. - /// - public bool InvertBeamDirection { get; set; } - - /// - /// Initializes a new instance of the class. - /// - public Beat() - { - Id = GlobalBeatId++; - WhammyBarType = WhammyType.None; - WhammyBarPoints = new FastList(); - Notes = new FastList(); - BrushType = BrushType.None; - Vibrato = VibratoType.None; - GraceType = GraceType.None; - PickStroke = PickStroke.None; - Duration = Duration.Quarter; - TremoloSpeed = null; - Automations = new FastList(); - Dots = 0; - DisplayStart = 0; - DisplayDuration = 0; - PlaybackStart = 0; - PlaybackDuration = 0; - TupletDenominator = -1; - TupletNumerator = -1; - Dynamic = DynamicValue.F; - Crescendo = CrescendoType.None; - InvertBeamDirection = false; - Ottava = Ottavia.Regular; - NoteStringLookup = new FastDictionary(); - WhammyStyle = BendStyle.Default; - IsSlurOrigin = false; - } - - internal static void CopyTo(Beat src, Beat dst) - { - dst.Id = src.Id; - dst.Index = src.Index; - dst.IsEmpty = src.IsEmpty; - dst.Duration = src.Duration; - dst.Dots = src.Dots; - dst.FadeIn = src.FadeIn; - if (src.Lyrics != null) - { - dst.Lyrics = new string[src.Lyrics.Length]; - for (int i = 0; i < src.Lyrics.Length; i++) - { - dst.Lyrics[i] = src.Lyrics[i]; - } - } - dst.Pop = src.Pop; - dst.HasRasgueado = src.HasRasgueado; - dst.Slap = src.Slap; - dst.Tap = src.Tap; - dst.Text = src.Text; - dst.BrushType = src.BrushType; - dst.BrushDuration = src.BrushDuration; - dst.TupletDenominator = src.TupletDenominator; - dst.TupletNumerator = src.TupletNumerator; - dst.Vibrato = src.Vibrato; - dst.ChordId = src.ChordId; - dst.GraceType = src.GraceType; - dst.PickStroke = src.PickStroke; - dst.TremoloSpeed = src.TremoloSpeed; - dst.Crescendo = src.Crescendo; - dst.DisplayStart = src.DisplayStart; - dst.DisplayDuration = src.DisplayDuration; - dst.PlaybackStart = src.PlaybackStart; - dst.PlaybackDuration = src.PlaybackDuration; - dst.Dynamic = src.Dynamic; - dst.IsLegatoOrigin = src.IsLegatoOrigin; - dst.InvertBeamDirection = src.InvertBeamDirection; - dst.WhammyBarType = src.WhammyBarType; - dst.IsContinuedWhammy = src.IsContinuedWhammy; - dst.Ottava = src.Ottava; - dst.WhammyStyle = src.WhammyStyle; - } - - internal Beat Clone() - { - var beat = new Beat(); - var id = beat.Id; - for (int i = 0, j = WhammyBarPoints.Count; i < j; i++) - { - beat.AddWhammyBarPoint(WhammyBarPoints[i].Clone()); - } - for (int i = 0, j = Notes.Count; i < j; i++) - { - beat.AddNote(Notes[i].Clone()); - } - CopyTo(this, beat); - for (int i = 0, j = Automations.Count; i < j; i++) - { - beat.Automations.Add(Automations[i].Clone()); - } - beat.Id = id; - return beat; - } - - internal void AddWhammyBarPoint(BendPoint point) - { - WhammyBarPoints.Add(point); - if (MaxWhammyPoint == null || point.Value > MaxWhammyPoint.Value) - { - MaxWhammyPoint = point; - } - if (MinWhammyPoint == null || point.Value < MinWhammyPoint.Value) - { - MinWhammyPoint = point; - } - - if (WhammyBarType == WhammyType.None) - { - WhammyBarType = WhammyType.Custom; - } - } - - internal void RemoveWhammyBarPoint(int index) - { - // check index - if (index < 0 || index >= WhammyBarPoints.Count) return; - - // remove point - WhammyBarPoints.RemoveAt(index); - var point = WhammyBarPoints[index]; - - // update maxWhammy point if required - if (point == MaxWhammyPoint) - { - MaxWhammyPoint = null; - foreach (var currentPoint in WhammyBarPoints) - { - if (MaxWhammyPoint == null || currentPoint.Value > MaxWhammyPoint.Value) - { - MaxWhammyPoint = currentPoint; - } - } - } - if (point == MinWhammyPoint) - { - MinWhammyPoint = null; - foreach (var currentPoint in WhammyBarPoints) - { - if (MinWhammyPoint == null || currentPoint.Value < MinWhammyPoint.Value) - { - MinWhammyPoint = currentPoint; - } - } - } - } - - internal void AddNote(Note note) - { - note.Beat = this; - note.Index = Notes.Count; - Notes.Add(note); - if (note.IsStringed) - { - NoteStringLookup[note.String] = note; - } - } - - internal void RemoveNote(Note note) - { - var index = Notes.IndexOf(note); - if (index >= 0) - { - Notes.RemoveAt(index); - } - } - - internal Automation GetAutomation(AutomationType type) - { - for (int i = 0, j = Automations.Count; i < j; i++) - { - var automation = Automations[i]; - if (automation.Type == type) - { - return automation; - } - } - return null; - } - - internal Note GetNoteOnString(int @string) - { - if (NoteStringLookup.ContainsKey(@string)) - { - return NoteStringLookup[@string]; - } - return null; - } - - private int CalculateDuration() - { - var ticks = Duration.ToTicks(); - if (Dots == 2) - { - ticks = MidiUtils.ApplyDot(ticks, true); - } - else if (Dots == 1) - { - ticks = MidiUtils.ApplyDot(ticks, false); - } - - if (TupletDenominator > 0 && TupletNumerator >= 0) - { - ticks = MidiUtils.ApplyTuplet(ticks, TupletNumerator, TupletDenominator); - } - - return ticks; - } - - internal void UpdateDurations() - { - var ticks = CalculateDuration(); - PlaybackDuration = ticks; - DisplayDuration = ticks; - - switch (GraceType) - { - case GraceType.BeforeBeat: - case GraceType.OnBeat: - switch (Duration) - { - case Duration.Eighth: - PlaybackDuration = Duration.ThirtySecond.ToTicks(); - break; - case Duration.Sixteenth: - PlaybackDuration = Duration.SixtyFourth.ToTicks(); - break; - case Duration.ThirtySecond: - PlaybackDuration = Duration.OneHundredTwentyEighth.ToTicks(); - break; - } - break; - case GraceType.BendGrace: - PlaybackDuration /= 2; - break; - default: - - var previous = PreviousBeat; - if (previous != null && previous.GraceType == GraceType.BendGrace) - { - PlaybackDuration = previous.PlaybackDuration; - } - else - { - while (previous != null && (previous.GraceType == GraceType.OnBeat)) - { - // if the previous beat is a on-beat grace it steals the duration from this beat - PlaybackDuration -= previous.PlaybackDuration; - previous = previous.PreviousBeat; - } - } - - break; - } - - - //// It can happen that the first beat of the next bar shifts into this - //// beat due to before-beat grace. In this case we need to - //// reduce the duration of this beat. - //// Within the same bar the start of the next beat is always directly after the current. - - //if (NextBeat != null && NextBeat.Voice.Bar != Voice.Bar) - //{ - // var next = NextBeat; - // while (next != null && next.GraceType == GraceType.BeforeBeat) - // { - // PlaybackDuration -= next.CalculateDuration(); - // next = next.NextBeat; - // } - //} - } - - internal void Finish(Settings settings) - { - var displayMode = settings == null ? DisplayMode.GuitarPro : settings.DisplayMode; - var isGradual = Text == "grad" || Text == "grad."; - if (isGradual && displayMode == DisplayMode.SongBook) - { - Text = ""; - } - - var needCopyBeatForBend = false; - MinNote = null; - MaxNote = null; - MinStringNote = null; - MaxStringNote = null; - - var visibleNotes = 0; - - for (int i = 0, j = Notes.Count; i < j; i++) - { - var note = Notes[i]; - note.Finish(settings); - if (note.IsLetRing) - { - IsLetRing = true; - } - if (note.IsPalmMute) - { - IsPalmMute = true; - } - - if (note.IsSlurOrigin) - { - IsSlurOrigin = true; - } - if (displayMode == DisplayMode.SongBook && note.HasBend && GraceType != GraceType.BendGrace) - { - if (!note.IsTieOrigin) - { - switch (note.BendType) - { - case BendType.Bend: - case BendType.PrebendRelease: - case BendType.PrebendBend: - needCopyBeatForBend = true; - break; - } - } - - if (isGradual || note.BendStyle == BendStyle.Gradual) - { - isGradual = true; - note.BendStyle = BendStyle.Gradual; - needCopyBeatForBend = false; - } - else - { - note.BendStyle = BendStyle.Fast; - } - } - - if (note.IsVisible) - { - visibleNotes++; - if (MinNote == null || note.RealValue < MinNote.RealValue) - { - MinNote = note; - } - - if (MaxNote == null || note.RealValue > MaxNote.RealValue) - { - MaxNote = note; - } - - if (MinStringNote == null || note.String < MinStringNote.String) - { - MinStringNote = note; - } - - if (MaxStringNote == null || note.String > MaxStringNote.String) - { - MaxStringNote = note; - } - } - } - - if (Notes.Count > 0 && visibleNotes == 0) - { - IsEmpty = true; - } - - if (IsSlurOrigin) - { - IsSlurOrigin = true; - SlurDestination = NextBeat; - if (!IsSlurDestination) - { - SlurOrigin = this; - if (SlurDestination != null) - { - SlurDestination.SlurOrigin = this; - } - } - else - { - SlurOrigin.SlurDestination = SlurDestination; - if (SlurDestination != null) - { - SlurDestination.SlurOrigin = SlurOrigin; - } - } - } - - // we need to clean al letring/palmmute flags for rests - // in case the effect is not continued on this beat - if (!IsRest && (!IsLetRing || !IsPalmMute)) - { - var currentBeat = PreviousBeat; - while (currentBeat != null && currentBeat.IsRest) - { - if (!IsLetRing) - { - currentBeat.IsLetRing = false; - } - if (!IsPalmMute) - { - currentBeat.IsPalmMute = false; - } - - currentBeat = currentBeat.PreviousBeat; - } - } - // if beat is a rest implicitely take over letring/palmmute - // from the previous beat gets cleaned later in case we flagged it wrong. - else if (IsRest && PreviousBeat != null && settings != null && settings.DisplayMode == DisplayMode.GuitarPro) - { - if (PreviousBeat.IsLetRing) - { - IsLetRing = true; - } - - if (PreviousBeat.IsPalmMute) - { - IsPalmMute = true; - } - } - - - // try to detect what kind of bend was used and cleans unneeded points if required - // Guitar Pro 6 and above (gpif.xml) uses exactly 4 points to define all whammys - if (WhammyBarPoints.Count > 0 && WhammyBarType == WhammyType.Custom) - { - if (displayMode == DisplayMode.SongBook) - { - WhammyStyle = isGradual ? BendStyle.Gradual : BendStyle.Fast; - } - - var isContinuedWhammy = IsContinuedWhammy = PreviousBeat != null && PreviousBeat.HasWhammyBar; - if (WhammyBarPoints.Count == 4) - { - var origin = WhammyBarPoints[0]; - var middle1 = WhammyBarPoints[1]; - var middle2 = WhammyBarPoints[2]; - var destination = WhammyBarPoints[3]; - - // the middle points are used for holds, anything else is a new feature we do not support yet - if (middle1.Value == middle2.Value) - { - // constant decrease or increase - if (origin.Value < middle1.Value && middle1.Value < destination.Value || - origin.Value > middle1.Value && middle1.Value > destination.Value) - { - if (origin.Value != 0 && !isContinuedWhammy) - { - WhammyBarType = WhammyType.PrediveDive; - } - else - { - WhammyBarType = WhammyType.Dive; - } - - WhammyBarPoints.RemoveAt(2); - WhammyBarPoints.RemoveAt(1); - } - // down-up or up-down - else if (origin.Value > middle1.Value && middle1.Value < destination.Value || - origin.Value < middle1.Value && middle1.Value > destination.Value) - { - WhammyBarType = WhammyType.Dip; - if (middle1.Offset == middle2.Offset || displayMode == DisplayMode.SongBook) - { - WhammyBarPoints.RemoveAt(2); - } - } - else if (origin.Value == middle1.Value && middle1.Value == destination.Value) - { - if (origin.Value != 0 && !isContinuedWhammy) - { - WhammyBarType = WhammyType.Predive; - } - else - { - WhammyBarType = WhammyType.Hold; - } - WhammyBarPoints.RemoveAt(2); - WhammyBarPoints.RemoveAt(1); - } - else - { - Logger.Warning("Model", "Unsupported whammy type detected, fallback to custom"); - } - } - else - { - Logger.Warning("Model", "Unsupported whammy type detected, fallback to custom"); - } - } - } - - UpdateDurations(); - - if (needCopyBeatForBend) - { - // if this beat is a simple bend convert it to a grace beat - // and generate a placeholder beat with tied notes - - var cloneBeat = Clone(); - cloneBeat.Id = GlobalBeatId++; - for (int i = 0, j = cloneBeat.Notes.Count; i < j; i++) - { - var cloneNote = cloneBeat.Notes[i]; - // remove bend on cloned note - cloneNote.BendType = BendType.None; - cloneNote.MaxBendPoint = null; - cloneNote.BendPoints = new FastList(); - cloneNote.BendStyle = BendStyle.Default; - cloneNote.Id = Note.GlobalNoteId++; - - // if the note has a bend which is continued on the next note - // we need to convert this note into a hold bend - var note = Notes[i]; - if (note.HasBend && note.IsTieOrigin) - { - var tieDestination = Note.NextNoteOnSameLine(note); - if (tieDestination != null && tieDestination.HasBend) - { - cloneNote.BendType = BendType.Hold; - var lastPoint = note.BendPoints[note.BendPoints.Count - 1]; - cloneNote.AddBendPoint(new BendPoint(0, lastPoint.Value)); - cloneNote.AddBendPoint(new BendPoint(BendPoint.MaxPosition, lastPoint.Value)); - } - } - - // mark as tied note - cloneNote.IsTieDestination = true; - } - - GraceType = GraceType.BendGrace; - UpdateDurations(); - - Voice.InsertBeat(this, cloneBeat); - } - - Fermata = Voice.Bar.MasterBar.GetFermata(this); - } - - /// - /// Checks whether the current beat is timewise before the given beat. - /// - /// - /// - internal bool IsBefore(Beat beat) - { - return Voice.Bar.Index < beat.Voice.Bar.Index || - (beat.Voice.Bar.Index == Voice.Bar.Index && Index < beat.Index); - } - - /// - /// Checks whether the current beat is timewise after the given beat. - /// - /// - /// - internal bool IsAfter(Beat beat) - { - return Voice.Bar.Index > beat.Voice.Bar.Index || - (beat.Voice.Bar.Index == Voice.Bar.Index && Index > beat.Index); - } - - internal bool HasNoteOnString(int noteString) - { - return NoteStringLookup.ContainsKey(noteString); - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Model/BendPoint.cs b/Source/AlphaTab/Model/BendPoint.cs deleted file mode 100644 index 9e3c5c269..000000000 --- a/Source/AlphaTab/Model/BendPoint.cs +++ /dev/null @@ -1,69 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -namespace AlphaTab.Model -{ - /// - /// A single point of a bending graph. Used to - /// describe WhammyBar and String Bending effects. - /// - public class BendPoint - { - /// - /// The maximum offset for points - /// - public const int MaxPosition = 60; - /// - /// The maximum value for points. - /// - public const int MaxValue = 12; - - /// - /// Gets or sets offset of the point relative to the note duration (0-60) - /// - public int Offset { get; set; } - /// - /// Gets or sets the 1/4 note value offsets for the bend. - /// - public int Value { get; set; } - - /// - /// Initializes a new instance of the class. - /// - /// The offset. - /// The value. - public BendPoint(int offset = 0, int value = 0) - { - Offset = offset; - Value = value; - } - - internal static void CopyTo(BendPoint src, BendPoint dst) - { - dst.Offset = src.Offset; - dst.Value = src.Value; - } - - internal BendPoint Clone() - { - var point = new BendPoint(); - CopyTo(this, point); - return point; - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Model/BendStyle.cs b/Source/AlphaTab/Model/BendStyle.cs deleted file mode 100644 index e08e2da0a..000000000 --- a/Source/AlphaTab/Model/BendStyle.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace AlphaTab.Model -{ - /// - /// Lists the different bend styles - /// - public enum BendStyle - { - /// - /// The bends are as described by the bend points - /// - Default, - /// - /// The bends are gradual over the beat duration. - /// - Gradual, - /// - /// The bends are done fast before the next note. - /// - Fast - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Model/BendType.cs b/Source/AlphaTab/Model/BendType.cs deleted file mode 100644 index 10ae12dbd..000000000 --- a/Source/AlphaTab/Model/BendType.cs +++ /dev/null @@ -1,49 +0,0 @@ -namespace AlphaTab.Model -{ - /// - /// Lists all types of bends - /// - public enum BendType - { - /// - /// No bend at all - /// - None, - /// - /// Individual points define the bends in a flexible manner. - /// This system was mainly used in Guitar Pro 3-5 - /// - Custom, - /// - /// Simple Bend from an unbended string to a higher note. - /// - Bend, - /// - /// Release of a bend that was started on an earlier note. - /// - Release, - /// - /// A bend that starts from an unbended string, - /// and also releases the bend after some time. - /// - BendRelease, - /// - /// Holds a bend that was started on an earlier note - /// - Hold, - /// - /// A bend that is already started before the note is played then it is held until the end. - /// - Prebend, - /// - /// A bend that is already started before the note is played and - /// bends even further, then it is held until the end. - /// - PrebendBend, - /// - /// A bend that is already started before the note is played and - /// then releases the bend to a lower note where it is held until the end. - /// - PrebendRelease, - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Model/BrushType.cs b/Source/AlphaTab/Model/BrushType.cs deleted file mode 100644 index 369394b54..000000000 --- a/Source/AlphaTab/Model/BrushType.cs +++ /dev/null @@ -1,46 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Model -{ - /// - /// Lists all types of how to brush multiple notes on a beat. - /// - public enum BrushType - { - /// - /// No brush. - /// - None, - /// - /// Normal brush up. - /// - BrushUp, - /// - /// Normal brush down. - /// - BrushDown, - /// - /// Arpeggio up. - /// - ArpeggioUp, - /// - /// Arpeggio down. - /// - ArpeggioDown - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Model/Chord.cs b/Source/AlphaTab/Model/Chord.cs deleted file mode 100644 index e2503d1f0..000000000 --- a/Source/AlphaTab/Model/Chord.cs +++ /dev/null @@ -1,66 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Collections; - -namespace AlphaTab.Model -{ - /// - /// A chord definition. - /// - public class Chord - { - /// - /// Gets or sets the name of the chord - /// - public string Name { get; set; } - - /// - /// Indicates the first fret of the chord diagram. - /// - public int FirstFret { get; set; } - - /// - /// Gets or sets the frets played on the individual strings for this chord. - /// - The order in this list goes from the highest string to the lowest string. - /// - -1 indicates that the string is not played. - /// - public FastList Strings { get; set; } - - /// - /// Gets or sets a list of frets where the finger should hold a barre - /// - public FastList BarreFrets { get; set; } - - /// - /// Initializes a new instance of the class. - /// - public Chord() - { - Strings = new FastList(); - BarreFrets = new FastList(); - } - - internal static void CopyTo(Chord src, Chord dst) - { - dst.FirstFret = src.FirstFret; - dst.Name = src.Name; - dst.Strings = src.Strings.Clone(); - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Model/Clef.cs b/Source/AlphaTab/Model/Clef.cs deleted file mode 100644 index 216317d4f..000000000 --- a/Source/AlphaTab/Model/Clef.cs +++ /dev/null @@ -1,46 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Model -{ - /// - /// This public enumeration lists all supported Clefs. - /// - public enum Clef - { - /// - /// Neutral clef. - /// - Neutral, - /// - /// C3 clef - /// - C3, - /// - /// C4 clef - /// - C4, - /// - /// F4 clef - /// - F4, - /// - /// G2 clef - /// - G2 - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Model/CrescendoType.cs b/Source/AlphaTab/Model/CrescendoType.cs deleted file mode 100644 index 4b098ba8b..000000000 --- a/Source/AlphaTab/Model/CrescendoType.cs +++ /dev/null @@ -1,38 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Model -{ - /// - /// Lists all Crescendo and Decrescendo types. - /// - public enum CrescendoType - { - /// - /// No crescendo applied. - /// - None, - /// - /// Normal crescendo applied. - /// - Crescendo, - /// - /// Normal decrescendo applied. - /// - Decrescendo - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Model/Duration.cs b/Source/AlphaTab/Model/Duration.cs deleted file mode 100644 index 7e01eb28b..000000000 --- a/Source/AlphaTab/Model/Duration.cs +++ /dev/null @@ -1,70 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Model -{ - /// - /// Lists all durations of a beat. - /// - public enum Duration - { - /// - /// A quadruple whole note duration - /// - QuadrupleWhole = -4, - /// - /// A double whole note duration - /// - DoubleWhole = -2, - /// - /// A whole note duration - /// - Whole = 1, - /// - /// A 1/2 note duration - /// - Half = 2, - /// - /// A 1/4 note duration - /// - Quarter = 4, - /// - /// A 1/8 note duration - /// - Eighth = 8, - /// - /// A 1/16 note duration - /// - Sixteenth = 16, - /// - /// A 1/32 note duration - /// - ThirtySecond = 32, - /// - /// A 1/64 note duration - /// - SixtyFourth = 64, - /// - /// A 1/128 note duration - /// - OneHundredTwentyEighth = 128, - /// - /// A 1/256 note duration - /// - TwoHundredFiftySixth = 256 - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Model/DynamicValue.cs b/Source/AlphaTab/Model/DynamicValue.cs deleted file mode 100644 index 014a1f8ea..000000000 --- a/Source/AlphaTab/Model/DynamicValue.cs +++ /dev/null @@ -1,60 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Model -{ - // ReSharper disable InconsistentNaming - /// - /// Lists all dynamics. - /// - public enum DynamicValue - { - /// - /// pianississimo (very very soft) - /// - PPP, - /// - /// pianissimo (very soft) - /// - PP, - /// - /// piano (soft) - /// - P, - /// - /// mezzo-piano (half soft) - /// - MP, - /// - /// mezzo-forte (half loud) - /// - MF, - /// - /// forte (loud) - /// - F, - /// - /// fortissimo (very loud) - /// - FF, - /// - /// fortississimo (very very loud) - /// - FFF - } - // ReSharper restore InconsistentNaming -} \ No newline at end of file diff --git a/Source/AlphaTab/Model/Fermata.cs b/Source/AlphaTab/Model/Fermata.cs deleted file mode 100644 index a80ae1a5f..000000000 --- a/Source/AlphaTab/Model/Fermata.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace AlphaTab.Model -{ - /// - /// Represents a fermata. - /// - public class Fermata - { - /// - /// Gets or sets the type of fermata. - /// - public FermataType Type { get; set; } - - /// - /// Gets or sets the actual lenght of the fermata. - /// - public float Length { get; set; } - - internal static void CopyTo(Fermata src, Fermata dst) - { - dst.Type = src.Type; - dst.Length = src.Length; - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Model/FermataType.cs b/Source/AlphaTab/Model/FermataType.cs deleted file mode 100644 index f058a0f04..000000000 --- a/Source/AlphaTab/Model/FermataType.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace AlphaTab.Model -{ - /// - /// Lists all types of fermatas - /// - public enum FermataType - { - /// - /// A short fermata (triangle symbol) - /// - Short, - /// - /// A medium fermata (round symbol) - /// - Medium, - /// - /// A long fermata (rectangular symbol) - /// - Long - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Model/Fingers.cs b/Source/AlphaTab/Model/Fingers.cs deleted file mode 100644 index 261a9dc10..000000000 --- a/Source/AlphaTab/Model/Fingers.cs +++ /dev/null @@ -1,54 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Model -{ - /// - /// Lists all fingers. - /// - public enum Fingers - { - /// - /// Unknown type (not documented) - /// - Unknown = -2, - /// - /// No finger, dead note - /// - NoOrDead = -1, - /// - /// The thumb - /// - Thumb = 0, - /// - /// The index finger - /// - IndexFinger = 1, - /// - /// The middle finger - /// - MiddleFinger = 2, - /// - /// The annular finger - /// - AnnularFinger = 3, - /// - /// The little finger - /// - LittleFinger = 4 - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Model/GraceType.cs b/Source/AlphaTab/Model/GraceType.cs deleted file mode 100644 index 12db99538..000000000 --- a/Source/AlphaTab/Model/GraceType.cs +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Model -{ - /// - /// Lists all types of grace notes - /// - public enum GraceType - { - /// - /// No grace, normal beat. - /// - None, - /// - /// The beat contains on-beat grace notes. - /// - OnBeat, - /// - /// The beat contains before-beat grace notes. - /// - BeforeBeat, - /// - /// The beat contains very special bend-grace notes used in SongBook style displays. - /// - BendGrace - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Model/HarmonicType.cs b/Source/AlphaTab/Model/HarmonicType.cs deleted file mode 100644 index c9700f1a6..000000000 --- a/Source/AlphaTab/Model/HarmonicType.cs +++ /dev/null @@ -1,54 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Model -{ - /// - /// Lists all harmonic types. - /// - public enum HarmonicType - { - /// - /// No harmonics. - /// - None, - /// - /// Natural harmonic - /// - Natural, - /// - /// Artificial harmonic - /// - Artificial, - /// - /// Pinch harmonics - /// - Pinch, - /// - /// Tap harmonics - /// - Tap, - /// - /// Semi harmonics - /// - Semi, - /// - /// Feedback harmonics - /// - Feedback - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Model/KeySignatureType.cs b/Source/AlphaTab/Model/KeySignatureType.cs deleted file mode 100644 index fd1238bf6..000000000 --- a/Source/AlphaTab/Model/KeySignatureType.cs +++ /dev/null @@ -1,100 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Model -{ - /// - /// This public enumeration lists all available key signatures - /// - public enum KeySignature - { - /// - /// Cb (7 flats) - /// - Cb = -7, - /// - /// Gb (6 flats) - /// - Gb = -6, - /// - /// Db (5 flats) - /// - Db = -5, - /// - /// Ab (4 flats) - /// - Ab = -4, - /// - /// Eb (3 flats) - /// - Eb = -3, - /// - /// Bb (2 flats) - /// - Bb = -2, - /// - /// F (1 flat) - /// - F = -1, - /// - /// C (no signs) - /// - C = 0, - /// - /// G (1 sharp) - /// - G = 1, - /// - /// D (2 sharp) - /// - D = 2, - /// - /// A (3 sharp) - /// - A = 3, - /// - /// E (4 sharp) - /// - E = 4, - /// - /// B (5 sharp) - /// - B = 5, - /// - /// F# (6 sharp) - /// - FSharp = 6, - /// - /// C# (8 sharp) - /// - CSharp = 7 - } - /// - /// This public enumeration lists all available types of KeySignatures - /// - public enum KeySignatureType - { - /// - /// Major - /// - Major, - /// - /// Minor - /// - Minor - } -} diff --git a/Source/AlphaTab/Model/Lyrics.cs b/Source/AlphaTab/Model/Lyrics.cs deleted file mode 100644 index dd6532df8..000000000 --- a/Source/AlphaTab/Model/Lyrics.cs +++ /dev/null @@ -1,173 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Collections; - -namespace AlphaTab.Model -{ - /// - /// Represents the lyrics of a song. - /// - public class Lyrics - { - private const int CharCodeLF = '\n'; - private const int CharCodeTab = '\t'; - private const int CharCodeCR = '\r'; - private const int CharCodeSpace = ' '; - private const int CharCodeBrackedClose = ']'; - private const int CharCodeBrackedOpen = '['; - private const int CharCodeDash = '-'; - - /// - /// Gets or sets he start bar on which the lyrics should begin. - /// - public int StartBar { get; set; } - /// - /// Gets or sets the raw lyrics text in Guitar Pro format. - /// (spaces split word syllables, plus merge syllables, [..] are comments) - /// - public string Text { get; set; } - - /// - /// Gets or sets the prepared chunks of the lyrics to apply to beats. - /// - public string[] Chunks { get; set; } - - internal void Finish() - { - var chunks = new FastList(); - Parse(Text, 0, chunks); - Chunks = chunks.ToArray(); - } - - private void Parse(string str, int p, FastList chunks) - { - if (string.IsNullOrEmpty(str)) return; - var state = LyricsState.Begin; - var next = LyricsState.Begin; - var skipSpace = false; - var start = 0; - - while (p < str.Length) - { - int c = str[p]; - switch (state) - { - case LyricsState.IgnoreSpaces: - switch (c) - { - case CharCodeLF: - case CharCodeCR: - case CharCodeTab: - break; - case CharCodeSpace: - if (!skipSpace) - { - state = next; - continue; - } - break; - default: - skipSpace = false; - state = next; - continue; - } - break; - case LyricsState.Begin: - switch (c) - { - case CharCodeBrackedOpen: - state = LyricsState.Comment; - break; - default: - start = p; - state = LyricsState.Text; - continue; - } - break; - case LyricsState.Comment: - switch (c) - { - case CharCodeBrackedClose: - state = LyricsState.Begin; - break; - } - break; - - case LyricsState.Text: - switch (c) - { - case CharCodeDash: - state = LyricsState.Dash; - break; - case CharCodeCR: - case CharCodeLF: - case CharCodeSpace: - var txt = str.Substring(start, p - start); - chunks.Add(PrepareChunk(txt)); - - state = LyricsState.IgnoreSpaces; - next = LyricsState.Begin; - break; - } - break; - - case LyricsState.Dash: - switch (c) - { - case CharCodeDash: - break; - default: - var txt = str.Substring(start, p - start); - chunks.Add(PrepareChunk(txt)); - - skipSpace = true; - state = LyricsState.IgnoreSpaces; - next = LyricsState.Begin; - continue; - } - break; - } - - p++; - } - - if (state == LyricsState.Text) - { - if (p != start) - { - chunks.Add(str.Substring(start, p - start)); - } - } - } - - private string PrepareChunk(string txt) - { - return txt.Replace("+", " "); - } - - enum LyricsState - { - IgnoreSpaces, - Begin, - Text, - Comment, - Dash - } - } -} diff --git a/Source/AlphaTab/Model/MasterBar.cs b/Source/AlphaTab/Model/MasterBar.cs deleted file mode 100644 index 74f733e2b..000000000 --- a/Source/AlphaTab/Model/MasterBar.cs +++ /dev/null @@ -1,198 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Audio; -using AlphaTab.Collections; - -namespace AlphaTab.Model -{ - /// - /// The MasterBar stores information about a bar which affects - /// all tracks. - /// - public class MasterBar - { - /// - /// The maximum alternate endings. (1 byte with 8 bitflags) - /// - public const int MaxAlternateEndings = 8; - - /// - /// Gets or sets the bitflag for the alternate endings. Each bit defines for which repeat counts - /// the bar is played. - /// - public byte AlternateEndings { get; set; } - - /// - /// Gets or sets the next masterbar in the song. - /// - public MasterBar NextMasterBar { get; set; } - - /// - /// Gets or sets the next masterbar in the song. - /// - public MasterBar PreviousMasterBar { get; set; } - - /// - /// Gets the zero based index of the masterbar. - /// - public int Index { get; set; } - - /// - /// Gets or sets the key signature used on all bars. - /// - public KeySignature KeySignature { get; set; } - /// - /// Gets or sets the type of key signature (major/minor) - /// - public KeySignatureType KeySignatureType { get; set; } - - /// - /// Gets or sets whether a double bar is shown for this masterbar. - /// - public bool IsDoubleBar { get; set; } - - /// - /// Gets or sets whether a repeat section starts on this masterbar. - /// - public bool IsRepeatStart { get; set; } - /// - /// Gets or sets whether a repeat section ends on this masterbar. - /// - public bool IsRepeatEnd => RepeatCount > 0; - /// - /// Gets or sets the number of repeats for the current repeat section. - /// - public int RepeatCount { get; set; } - - /// - /// Gets or sets the repeat group this bar belongs to. - /// - public RepeatGroup RepeatGroup { get; set; } - - /// - /// Gets or sets the time signature numerator. - /// - public int TimeSignatureNumerator { get; set; } - /// - /// Gets or sets the time signature denominiator. - /// - public int TimeSignatureDenominator { get; set; } - /// - /// Gets or sets whether this is bar has a common time signature. - /// - public bool TimeSignatureCommon { get; set; } - - /// - /// Gets or sets the triplet feel that is valid for this bar. - /// - public TripletFeel TripletFeel { get; set; } - - /// - /// Gets or sets the new section information for this bar. - /// - public Section Section { get; set; } - - /// - /// Gets a value indicating whether a new section starts on this bar. - /// - public bool IsSectionStart => Section != null; - - /// - /// Gets or sets the tempo automation for this bar. - /// - public Automation TempoAutomation { get; set; } - - /// - /// Gets or sets the reference to the score this song belongs to. - /// - public Score Score { get; set; } - - /// - /// Gets or sets the fermatas for this bar. The key is the offset of the fermata in midi ticks. - /// - public FastDictionary Fermata { get; set; } - - /// - /// The timeline position of the voice within the whole score. (unit: midi ticks) - /// - public int Start { get; set; } - - /// - /// Initializes a new instance of the class. - /// - public MasterBar() - { - TimeSignatureDenominator = 4; - TimeSignatureNumerator = 4; - TripletFeel = TripletFeel.NoTripletFeel; - KeySignatureType = KeySignatureType.Major; - TimeSignatureCommon = false; - Fermata = new FastDictionary(); - } - - internal static void CopyTo(MasterBar src, MasterBar dst) - { - dst.AlternateEndings = src.AlternateEndings; - dst.Index = src.Index; - dst.KeySignature = src.KeySignature; - dst.KeySignatureType = src.KeySignatureType; - dst.IsDoubleBar = src.IsDoubleBar; - dst.IsRepeatStart = src.IsRepeatStart; - dst.RepeatCount = src.RepeatCount; - dst.TimeSignatureNumerator = src.TimeSignatureNumerator; - dst.TimeSignatureDenominator = src.TimeSignatureDenominator; - dst.TimeSignatureCommon = src.TimeSignatureCommon; - dst.TripletFeel = src.TripletFeel; - dst.Start = src.Start; - } - - /// - /// Calculates the time spent in this bar. (unit: midi ticks) - /// - /// - public int CalculateDuration() - { - return TimeSignatureNumerator * MidiUtils.ValueToTicks(TimeSignatureDenominator); - } - - /// - /// Adds a fermata to the masterbar. - /// - /// The offset of the fermata within the bar in midi ticks. - /// The fermata. - internal void AddFermata(int offset, Fermata fermata) - { - Fermata[offset] = fermata; - } - - /// - /// Gets the fermata for a given beat. - /// - /// The beat to get the fermata for. - /// - internal Fermata GetFermata(Beat beat) - { - if (Fermata.ContainsKey(beat.PlaybackStart)) - { - return Fermata[beat.PlaybackStart]; - } - return null; - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Model/ModelUtils.cs b/Source/AlphaTab/Model/ModelUtils.cs deleted file mode 100644 index 59a6f7b86..000000000 --- a/Source/AlphaTab/Model/ModelUtils.cs +++ /dev/null @@ -1,143 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Audio; - -namespace AlphaTab.Model -{ - /// - /// This public class contains some utilities for working with model public classes - /// - static class ModelUtils - { - public static int GetIndex(this Duration duration) - { - var index = 0; - var value = (int)duration; - if (value < 0) - { - return index; - } - - return (int)Platform.Platform.Log2((int)duration); - } - - public static bool KeySignatureIsFlat(int ks) - { - return ks < 0; - } - - public static bool KeySignatureIsNatural(int ks) - { - return ks == 0; - } - - public static bool KeySignatureIsSharp(int ks) - { - return ks > 0; - } - - public static void ApplyPitchOffsets(Settings settings, Score score) - { - for (var i = 0; i < score.Tracks.Count; i++) - { - if (i < settings.DisplayTranspositionPitches.Length) - { - foreach (var staff in score.Tracks[i].Staves) - { - staff.DisplayTranspositionPitch = -settings.DisplayTranspositionPitches[i]; - } - } - if (i < settings.TranspositionPitches.Length) - { - foreach (var staff in score.Tracks[i].Staves) - { - staff.TranspositionPitch = -settings.TranspositionPitches[i]; - } - } - } - } - - public static string FingerToString(Settings settings, Beat beat, Fingers finger, bool leftHand) - { - if (settings.ForcePianoFingering || GeneralMidi.IsPiano(beat.Voice.Bar.Staff.Track.PlaybackInfo.Program)) - { - switch (finger) - { - case Fingers.Unknown: - case Fingers.NoOrDead: - return null; - case Fingers.Thumb: - return "1"; - case Fingers.IndexFinger: - return "2"; - case Fingers.MiddleFinger: - return "3"; - case Fingers.AnnularFinger: - return "4"; - case Fingers.LittleFinger: - return "5"; - default: - return null; - } - } - else if (leftHand) - { - switch (finger) - { - case Fingers.Unknown: - case Fingers.NoOrDead: - return "0"; - case Fingers.Thumb: - return "T"; - case Fingers.IndexFinger: - return "1"; - case Fingers.MiddleFinger: - return "2"; - case Fingers.AnnularFinger: - return "3"; - case Fingers.LittleFinger: - return "4"; - default: - return null; - } - } - else - { - switch (finger) - { - case Fingers.Unknown: - case Fingers.NoOrDead: - return null; - case Fingers.Thumb: - return "p"; - case Fingers.IndexFinger: - return "i"; - case Fingers.MiddleFinger: - return "m"; - case Fingers.AnnularFinger: - return "a"; - case Fingers.LittleFinger: - return "c"; - default: - return null; - } - } - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Model/Note.cs b/Source/AlphaTab/Model/Note.cs deleted file mode 100644 index acda31678..000000000 --- a/Source/AlphaTab/Model/Note.cs +++ /dev/null @@ -1,967 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Collections; -using AlphaTab.Platform; -using AlphaTab.Rendering.Utils; -using AlphaTab.Util; - -namespace AlphaTab.Model -{ - /// - /// A note is a single played sound on a fretted instrument. - /// It consists of a fret offset and a string on which the note is played on. - /// It also can be modified by a lot of different effects. - /// - public class Note - { - /// - /// This is a global counter for all notes. We use it - /// at several locations for lookup tables. - /// - internal static int GlobalNoteId = 0; - - /// - /// Gets or sets the unique id of this note. - /// - public int Id { get; set; } - /// - /// Gets or sets the zero-based index of this note within the beat. - /// - public int Index { get; set; } - - /// - /// Gets or sets the accentuation of this note. - /// - public AccentuationType Accentuated { get; set; } - - /// - /// Gets or sets the bend type for this note. - /// - public BendType BendType { get; set; } - - /// - /// Gets or sets the bend style for this note. - /// - public BendStyle BendStyle { get; set; } - - /// - /// Gets or sets the note from which this note continues the bend. - /// - public Note BendOrigin { get; set; } - - /// - /// Gets or sets whether this note continues a bend from a previous note. - /// - public bool IsContinuedBend { get; set; } - - /// - /// Gets or sets a list of the points defining the bend behavior. - /// - public FastList BendPoints { get; set; } - - /// - /// Gets or sets the bend point with the highest bend value. - /// - public BendPoint MaxBendPoint { get; set; } - - /// - /// Gets a value indicating whether this note is bended. - /// - public bool HasBend => BendType != BendType.None; - - #region Stringed Instruments - - /// - /// Gets a value indicating whether this note is defined via a string on the instrument. . - /// - public bool IsStringed => Fret >= 0 && String >= 0; - - /// - /// Gets or sets the fret on which this note is played on the instrument. - /// - public int Fret { get; set; } - - /// - /// Gets or sets the string number where the note is placed. - /// 1 is the lowest string on the guitar and the bottom line on the tablature. - /// It then increases the the number of strings on available on the track. - /// - public int String { get; set; } - - #endregion - - #region Piano Instruments - - /// - /// Gets a value indicating whether the value of this note is defined via octave and tone. - /// - public bool IsPiano => !IsStringed && (Octave >= 0 && Tone >= 0); - - /// - /// Gets or sets the octave on which this note is played. - /// - public int Octave { get; set; } - /// - /// Gets or sets the tone of this note within the octave. - /// - public int Tone { get; set; } - - #endregion - - #region Percussion - - /// - /// Gets a value indicating whether this note is a percussion note. - /// - public bool IsPercussion => !IsStringed && (Element >= 0 && Variation >= 0); - - /// - /// Gets or sets the percusson element. - /// - public int Element { get; set; } - - /// - /// Gets or sets the variation of this note. - /// - public int Variation { get; set; } - - #endregion - - /// - /// Gets or sets whether this note is visible on the music sheet. - /// - public bool IsVisible { get; set; } - - /// - /// Gets or sets whether this note starts a hammeron or pulloff. - /// - public bool IsHammerPullOrigin { get; set; } - /// - /// Gets a value indicating whether this note ends a hammeron or pulloff. - /// - public bool IsHammerPullDestination => HammerPullOrigin != null; - - /// - /// Gets the origin of the hammeron/pulloff of this note. - /// - public Note HammerPullOrigin { get; set; } - - /// - /// Gets the destination for the hammeron/pullof started by this note. - /// - public Note HammerPullDestination { get; set; } - - /// - /// Gets or sets whether this note starts a slur. - /// - public bool IsSlurOrigin { get; set; } - /// - /// Gets or sets whether a slur finished or continues on this note. - /// - public bool IsSlurDestination => SlurOrigin != null; - - /// - /// Gets or sets the origin of the slur this note contributes to. - /// - public Note SlurOrigin { get; set; } - - /// - /// Gets or sets the destination of the slur this note contributes to. - /// - public Note SlurDestination { get; set; } - - /// - /// Gets or sets whether this note has an harmonic effect. - /// - public bool IsHarmonic => HarmonicType != HarmonicType.None; - - /// - /// Gets or sets the harmonic type applied to this note. - /// - public HarmonicType HarmonicType { get; set; } - - /// - /// Gets or sets the value defining the harmonic pitch. - /// - public float HarmonicValue { get; set; } - - /// - /// Gets or sets whether the note is a ghost note and shown in parenthesis. Also this will make the note a bit more silent. - /// - public bool IsGhost { get; set; } - /// - /// Gets or sets whether this note has a let-ring effect. - /// - public bool IsLetRing { get; set; } - /// - /// Gets or sets the destination note for the let-ring effect. - /// - public Note LetRingDestination { get; set; } - - /// - /// Gets or sets whether this note has a palm-mute effect. - /// - public bool IsPalmMute { get; set; } - /// - /// Gets or sets the destination note for the palm-mute effect. - /// - public Note PalmMuteDestination { get; set; } - - /// - /// Gets or sets whether the note is shown and played as dead note. - /// - public bool IsDead { get; set; } - - /// - /// Gets or sets whether the note is played as staccato. - /// - public bool IsStaccato { get; set; } - - /// - /// Gets or sets the slide type this note is played with. - /// - public SlideType SlideType { get; set; } - /// - /// Gets or sets the target note for several slide types. - /// - public Note SlideTarget { get; set; } - - /// - /// Gets or sets whether a vibrato is played on the note. - /// - public VibratoType Vibrato { get; set; } - - /// - /// Gets or sets the origin of the tied if this note is tied. - /// - public Note TieOrigin { get; set; } - /// - /// Gets or sets the desination of the tie. - /// - public Note TieDestination { get; set; } - /// - /// Gets or sets whether this note is ends a tied note. - /// - public bool IsTieDestination { get; set; } - /// - /// Gets or sets whether this note starts or continues a tied note. - /// - public bool IsTieOrigin { get; set; } - - /// - /// Gets or sets the fingers used for this note on the left hand. - /// - public Fingers LeftHandFinger { get; set; } - /// - /// Gets or sets the fingers used for this note on the right hand. - /// - public Fingers RightHandFinger { get; set; } - - /// - /// Gets or sets whether this note has fingering defined. - /// - public bool IsFingering { get; set; } - - /// - /// Gets or sets the target note value for the trill effect. - /// - public int TrillValue { get; set; } - /// - /// Gets the fret for the trill. - /// - public int TrillFret => TrillValue - StringTuning; - /// - /// Gets a value indicating whether this note has a trill effect. - /// - public bool IsTrill => TrillValue >= 0; - /// - /// Gets or sets the speed of the trill effect. - /// - public Duration TrillSpeed { get; set; } - - /// - /// Gets or sets the percentual duration of the note relative to the overall beat duration . - /// - public double DurationPercent { get; set; } - - /// - /// Gets or sets how accidetnals for this note should be handled. - /// - public NoteAccidentalMode AccidentalMode { get; set; } - - /// - /// Gets or sets the reference to the parent beat to which this note belongs to. - /// - public Beat Beat { get; set; } - - /// - /// Gets or sets the dynamics for this note. - /// - public DynamicValue Dynamic { get; set; } - - /// - /// Gets the base note value for the string of this note. - /// - public int StringTuning => Beat.Voice.Bar.Staff.Capo + GetStringTuning(Beat.Voice.Bar.Staff, String); - - internal static int GetStringTuning(Staff staff, int noteString) - { - if (staff.Tuning.Length > 0) - return staff.Tuning[staff.Tuning.Length - (noteString - 1) - 1]; - return 0; - } - - /// - /// Gets the absolute value of this note for playback. - /// - public int RealValue - { - get - { - if (IsPercussion) - { - return PercussionMapper.MidiFromElementVariation(this); - } - if (IsStringed) - { - if (HarmonicType == HarmonicType.Natural) - { - return HarmonicPitch + StringTuning - Beat.Voice.Bar.Staff.TranspositionPitch; - } - else - { - return Fret + StringTuning - Beat.Voice.Bar.Staff.TranspositionPitch + HarmonicPitch; - } - } - if (IsPiano) - { - return Octave * 12 + Tone - Beat.Voice.Bar.Staff.TranspositionPitch; - } - - return 0; - } - } - - /// - /// Gets or sets the harmonic pitch value for this note. - /// - public int HarmonicPitch - { - get - { - if (HarmonicType == HarmonicType.None || !IsStringed) return 0; - - var value = HarmonicValue; - - // add semitones to reach corresponding harmonic frets - if (value.IsAlmostEqualTo(2.4f)) - { - return 36; - } - else if (value.IsAlmostEqualTo(2.7f)) - { - // Fret 3 2nd octave + minor seventh - return 34; - } - else if (value < 3) - { - // no natural harmonics below fret 3 - return 0; - } - else if (value <= 3.5 /*3.2*/) - { - // Fret 3 2nd octave + fifth - return 31; - } - else if (value <= 4) - { - return 28; - } - else if (value <= 5) - { - return 24; - } - else if (value <= 6 /* 5.8 */) - { - return 34; - } - else if (value <= 7) - { - return 19; - } - else if (value <= 8.5 /*8.2*/) - { - return 36; - } - else if (value <= 9) - { - return 28; - } - else if (value <= 10 /*9.6*/) - { - return 34; - } - else if (value <= 11) - { - return 0; - } - else if (value <= 12) - { - return 12; - } - else if (value < 14) - { - // fret 13,14 stay - return 0; - } - else if (value <= 15 /*14.7*/) - { - return 34; - } - else if (value <= 16) - { - return 28; - } - else if (value <= 17) - { - return 36; - } - else if (value <= 18) - { - return 0; - } - else if (value <= 19) - { - return 19; - } - else if (value <= 21) - { - // 20,21 stay - return 0; - } - else if (value <= 22 /* 21.7 */) - { - return 36; - } - else if (value <= 24) - { - return 24; - } - - return 0; - } - } - - /// - /// Gets the absolute value of this note considering - /// offsets by bends and ottavia - /// - public int DisplayValue - { - get - { - var noteValue = DisplayValueWithoutBend; - - if (HasBend) - { - noteValue += BendPoints[0].Value / 2; - } - else if (BendOrigin != null) - { - noteValue += BendOrigin.BendPoints[BendOrigin.BendPoints.Count - 1].Value / 2; - } - else if (IsTieDestination && TieOrigin.BendOrigin != null) - { - noteValue += TieOrigin.BendOrigin.BendPoints[TieOrigin.BendOrigin.BendPoints.Count - 1].Value / 2; - } - else if (Beat.HasWhammyBar) - { - noteValue += Beat.WhammyBarPoints[0].Value / 2; - } - else if (Beat.IsContinuedWhammy) - { - noteValue += Beat.PreviousBeat.WhammyBarPoints[Beat.PreviousBeat.WhammyBarPoints.Count - 1].Value / 2; - } - - - return noteValue; - } - } - - /// - /// Gets the absolute value of this note considering all effects beside bends. - /// - public int DisplayValueWithoutBend - { - get - { - var noteValue = RealValue; - - if (HarmonicType != HarmonicType.Natural && HarmonicType != HarmonicType.None) - { - noteValue -= HarmonicPitch; - } - - switch (Beat.Ottava) - { - case Ottavia._15ma: - noteValue -= 24; - break; - case Ottavia._8va: - noteValue -= 12; - break; - case Ottavia.Regular: - break; - case Ottavia._8vb: - noteValue += 12; - break; - case Ottavia._15mb: - noteValue += 24; - break; - } - - switch (Beat.Voice.Bar.ClefOttava) - { - case Ottavia._15ma: - noteValue -= 24; - break; - case Ottavia._8va: - noteValue -= 12; - break; - case Ottavia.Regular: - break; - case Ottavia._8vb: - noteValue += 12; - break; - case Ottavia._15mb: - noteValue += 24; - break; - } - - return noteValue - Beat.Voice.Bar.Staff.DisplayTranspositionPitch; - } - } - - /// - /// Gets or sets whether the note has a offset of a quartertone caused by bends. - /// - public bool HasQuarterToneOffset - { - get - { - if (HasBend) - { - return (BendPoints[0].Value % 2) != 0; - } - if (BendOrigin != null) - { - return (BendOrigin.BendPoints[BendOrigin.BendPoints.Count - 1].Value % 2) != 0; - } - if (Beat.HasWhammyBar) - { - return (Beat.WhammyBarPoints[0].Value % 2) != 0; - } - if (Beat.IsContinuedWhammy) - { - return (Beat.PreviousBeat.WhammyBarPoints[Beat.PreviousBeat.WhammyBarPoints.Count - 1].Value % 2) != 0; - } - return false; - } - } - - - /// - /// Initializes a new instance of the class. - /// - public Note() - { - Id = GlobalNoteId++; - BendType = BendType.None; - BendStyle = BendStyle.Default; - BendPoints = new FastList(); - Dynamic = DynamicValue.F; - - Accentuated = AccentuationType.None; - Fret = int.MinValue; - HarmonicType = HarmonicType.None; - SlideType = SlideType.None; - Vibrato = VibratoType.None; - - LeftHandFinger = Fingers.Unknown; - RightHandFinger = Fingers.Unknown; - - TrillValue = -1; - TrillSpeed = Duration.ThirtySecond; - DurationPercent = 1; - - Octave = -1; - Tone = -1; - - Fret = -1; - String = -1; - - Element = -1; - Variation = -1; - IsVisible = true; - } - - internal static void CopyTo(Note src, Note dst) - { - dst.Id = src.Id; - dst.Accentuated = src.Accentuated; - dst.Fret = src.Fret; - dst.String = src.String; - dst.IsHammerPullOrigin = src.IsHammerPullOrigin; - dst.IsSlurOrigin = src.IsSlurOrigin; - dst.HarmonicValue = src.HarmonicValue; - dst.HarmonicType = src.HarmonicType; - dst.IsGhost = src.IsGhost; - dst.IsLetRing = src.IsLetRing; - dst.IsPalmMute = src.IsPalmMute; - dst.IsDead = src.IsDead; - dst.IsStaccato = src.IsStaccato; - dst.SlideType = src.SlideType; - dst.Vibrato = src.Vibrato; - dst.IsTieOrigin = src.IsTieOrigin; - dst.IsTieDestination = src.IsTieDestination; - dst.LeftHandFinger = src.LeftHandFinger; - dst.RightHandFinger = src.RightHandFinger; - dst.IsFingering = src.IsFingering; - dst.TrillValue = src.TrillValue; - dst.TrillSpeed = src.TrillSpeed; - dst.DurationPercent = src.DurationPercent; - dst.AccidentalMode = src.AccidentalMode; - dst.Dynamic = src.Dynamic; - dst.Octave = src.Octave; - dst.Tone = src.Tone; - dst.Element = src.Element; - dst.Variation = src.Variation; - dst.BendType = src.BendType; - dst.BendStyle = src.BendStyle; - dst.IsContinuedBend = src.IsContinuedBend; - dst.IsVisible = src.IsVisible; - } - - internal Note Clone() - { - var n = new Note(); - var id = n.Id; - CopyTo(this, n); - for (int i = 0, j = BendPoints.Count; i < j; i++) - { - n.AddBendPoint(BendPoints[i].Clone()); - } - n.Id = id; - return n; - } - - internal void AddBendPoint(BendPoint point) - { - BendPoints.Add(point); - if (MaxBendPoint == null || point.Value > MaxBendPoint.Value) - { - MaxBendPoint = point; - } - - if (BendType == BendType.None) - { - BendType = BendType.Custom; - } - } - - internal void Finish(Settings settings) - { - var nextNoteOnLine = new Util.Lazy(() => NextNoteOnSameLine(this)); - var prevNoteOnLine = new Util.Lazy(() => PreviousNoteOnSameLine(this)); - - var isSongBook = settings != null && settings.DisplayMode == DisplayMode.SongBook; - - // connect ties - if (IsTieDestination) - { - if (prevNoteOnLine.Value == null) - { - IsTieDestination = false; - } - else - { - TieOrigin = prevNoteOnLine.Value; - TieOrigin.IsTieOrigin = true; - TieOrigin.TieDestination = this; - Fret = TieOrigin.Fret; - Octave = TieOrigin.Octave; - Tone = TieOrigin.Tone; - - if (TieOrigin.HasBend) - { - BendOrigin = TieOrigin; - } - } - - // implicit let ring - if (isSongBook && TieOrigin.IsLetRing) - { - IsLetRing = true; - } - } - - // connect letring - if (IsLetRing) - { - if (nextNoteOnLine.Value == null || !nextNoteOnLine.Value.IsLetRing) - { - LetRingDestination = this; - } - else - { - LetRingDestination = nextNoteOnLine.Value; - } - - if (isSongBook && IsTieDestination && !TieOrigin.HasBend) - { - IsVisible = false; - } - } - - - // connect palmmute - if (IsPalmMute) - { - if (nextNoteOnLine.Value == null || !nextNoteOnLine.Value.IsPalmMute) - { - PalmMuteDestination = this; - } - else - { - PalmMuteDestination = nextNoteOnLine.Value; - } - } - - if (IsHammerPullOrigin || SlideType == SlideType.Legato) - { - IsSlurOrigin = true; - SlurDestination = nextNoteOnLine.Value; - if (!IsSlurDestination) - { - SlurOrigin = this; - if (SlurDestination != null) - { - SlurDestination.SlurOrigin = this; - } - } - else - { - SlurOrigin.SlurDestination = SlurDestination; - if (SlurDestination != null) - { - SlurDestination.SlurOrigin = SlurOrigin; - } - } - } - - // set hammeron/pulloffs - if (IsHammerPullOrigin) - { - if (nextNoteOnLine.Value == null) - { - IsHammerPullOrigin = false; - } - else - { - HammerPullDestination = nextNoteOnLine.Value; - HammerPullDestination.HammerPullOrigin = this; - } - } - - // set slides - switch (SlideType) - { - case SlideType.Shift: - case SlideType.Legato: - SlideTarget = nextNoteOnLine.Value; - if (SlideTarget == null) - { - SlideType = SlideType.None; - } - break; - } - - // try to detect what kind of bend was used and cleans unneeded points if required - // Guitar Pro 6 and above (gpif.xml) uses exactly 4 points to define all bends - if (BendPoints.Count > 0 && BendType == BendType.Custom) - { - var isContinuedBend = IsContinuedBend = TieOrigin != null && TieOrigin.HasBend; - if (BendPoints.Count == 4) - { - var origin = BendPoints[0]; - var middle1 = BendPoints[1]; - var middle2 = BendPoints[2]; - var destination = BendPoints[3]; - - // the middle points are used for holds, anything else is a new feature we do not support yet - if (middle1.Value == middle2.Value) - { - // bend higher? - if (destination.Value > origin.Value) - { - if (middle1.Value > destination.Value) - { - BendType = BendType.BendRelease; - } - else if (!isContinuedBend && origin.Value > 0) - { - BendType = BendType.PrebendBend; - BendPoints.RemoveAt(2); - BendPoints.RemoveAt(1); - } - else - { - BendType = BendType.Bend; - BendPoints.RemoveAt(2); - BendPoints.RemoveAt(1); - } - } - // release? - else if (destination.Value < origin.Value) - { - // origin must be > 0 otherwise it's no release, we cannot bend negative - if (isContinuedBend) - { - BendType = BendType.Release; - BendPoints.RemoveAt(2); - BendPoints.RemoveAt(1); - } - else - { - BendType = BendType.PrebendRelease; - BendPoints.RemoveAt(2); - BendPoints.RemoveAt(1); - } - } - // hold? - else - { - if (middle1.Value > origin.Value) - { - BendType = BendType.BendRelease; - } - else if (origin.Value > 0 && !isContinuedBend) - { - BendType = BendType.Prebend; - BendPoints.RemoveAt(2); - BendPoints.RemoveAt(1); - } - else - { - BendType = BendType.Hold; - BendPoints.RemoveAt(2); - BendPoints.RemoveAt(1); - } - } - } - else - { - Logger.Warning("Model", "Unsupported bend type detected, fallback to custom"); - } - } - else if (BendPoints.Count == 2) - { - var origin = BendPoints[0]; - var destination = BendPoints[1]; - - // bend higher? - if (destination.Value > origin.Value) - { - if (!isContinuedBend && origin.Value > 0) - { - BendType = BendType.PrebendBend; - } - else - { - BendType = BendType.Bend; - } - } - // release? - else if (destination.Value < origin.Value) - { - // origin must be > 0 otherwise it's no release, we cannot bend negative - if (isContinuedBend) - { - BendType = BendType.Release; - } - else - { - BendType = BendType.PrebendRelease; - } - } - // hold? - else - { - BendType = BendType.Hold; - } - } - } - else if (BendPoints.Count == 0) - { - BendType = BendType.None; - } - } - - private const int MaxOffsetForSameLineSearch = 3; - internal static Note NextNoteOnSameLine(Note note) - { - var nextBeat = note.Beat.NextBeat; - // keep searching in same bar - while (nextBeat != null && nextBeat.Voice.Bar.Index <= note.Beat.Voice.Bar.Index + MaxOffsetForSameLineSearch) - { - var noteOnString = nextBeat.GetNoteOnString(note.String); - if (noteOnString != null) - { - return noteOnString; - } - else - { - nextBeat = nextBeat.NextBeat; - } - } - - return null; - } - - internal static Note PreviousNoteOnSameLine(Note note) - { - var previousBeat = note.Beat.PreviousBeat; - - // keep searching in same bar - while (previousBeat != null && previousBeat.Voice.Bar.Index >= note.Beat.Voice.Bar.Index - MaxOffsetForSameLineSearch) - { - var noteOnString = previousBeat.GetNoteOnString(note.String); - if (noteOnString != null) - { - return noteOnString; - } - else - { - previousBeat = previousBeat.PreviousBeat; - } - } - - return null; - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Model/NoteAccidentalMode.cs b/Source/AlphaTab/Model/NoteAccidentalMode.cs deleted file mode 100644 index feffffbf5..000000000 --- a/Source/AlphaTab/Model/NoteAccidentalMode.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace AlphaTab.Model -{ - /// - /// Lists the modes how accidentals are handled for notes - /// - public enum NoteAccidentalMode - { - /// - /// Accidentals are calculated automatically. - /// - Default, - /// - /// If the default behavior calculates a Sharp, use flat instead (and vice versa). - /// - SwapAccidentals, - /// - /// This will move the note one line down and applies a Naturalize. - /// - ForceNatural, - /// - /// This will move the note one line down and applies a Sharp. - /// - ForceSharp, - /// - /// This will move the note one line up and applies a Flat. - /// - ForceFlat, - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Model/Ottavia.cs b/Source/AlphaTab/Model/Ottavia.cs deleted file mode 100644 index 50aaad9e6..000000000 --- a/Source/AlphaTab/Model/Ottavia.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace AlphaTab.Model -{ - /// - /// Lists all ottavia. - /// - public enum Ottavia - { - /// - /// 2 octaves higher - /// - _15ma, - /// - /// 1 octave higher - /// - _8va, - /// - /// Normal - /// - Regular, - /// - /// 1 octave lower - /// - _8vb, - /// - /// 2 octaves lower. - /// - _15mb - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Model/PickStroke.cs b/Source/AlphaTab/Model/PickStroke.cs deleted file mode 100644 index 4264cd873..000000000 --- a/Source/AlphaTab/Model/PickStroke.cs +++ /dev/null @@ -1,38 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Model -{ - /// - /// Lists all types of pick strokes. - /// - public enum PickStroke - { - /// - /// No pickstroke used. - /// - None, - /// - /// Pickstroke up. - /// - Up, - /// - /// Pickstroke down - /// - Down - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Model/PlaybackInformation.cs b/Source/AlphaTab/Model/PlaybackInformation.cs deleted file mode 100644 index c94d5ce9d..000000000 --- a/Source/AlphaTab/Model/PlaybackInformation.cs +++ /dev/null @@ -1,85 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -namespace AlphaTab.Model -{ - /// - /// This public class stores the midi specific information of a track needed - /// for playback. - /// - public class PlaybackInformation - { - /// - /// Gets or sets the volume (0-16) - /// - public int Volume { get; set; } - /// - /// Gets or sets the balance (0-16; 8=center) - /// - public int Balance { get; set; } - - /// - /// Gets or sets the midi port to use. - /// - public int Port { get; set; } - /// - /// Gets or sets the midi program to use. - /// - public int Program { get; set; } - /// - /// Gets or sets the primary channel for all normal midi events. - /// - public int PrimaryChannel { get; set; } - /// - /// Gets or sets the secondary channel for special midi events. - /// - public int SecondaryChannel { get; set; } - - /// - /// Gets or sets whether the track is muted. - /// - public bool IsMute { get; set; } - - /// - /// Gets or sets whether the track is playing alone. - /// - public bool IsSolo { get; set; } - - /// - /// Initializes a new instance of the class. - /// - public PlaybackInformation() - { - Volume = 15; - Balance = 8; - Port = 1; - } - - internal static void CopyTo(PlaybackInformation src, PlaybackInformation dst) - { - dst.Volume = src.Volume; - dst.Balance = src.Balance; - dst.Port = src.Port; - dst.Program = src.Program; - dst.PrimaryChannel = src.PrimaryChannel; - dst.SecondaryChannel = src.SecondaryChannel; - dst.IsMute = src.IsMute; - dst.IsSolo = src.IsSolo; - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Model/RenderStylesheet.cs b/Source/AlphaTab/Model/RenderStylesheet.cs deleted file mode 100644 index 9fdcae5df..000000000 --- a/Source/AlphaTab/Model/RenderStylesheet.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace AlphaTab.Model -{ - /// - /// This class represents the rendering stylesheet. - /// It contains settings which control the display of the score when rendered. - /// - public class RenderStylesheet - { - /// - /// Gets or sets whether dynamics are hidden. - /// - public bool HideDynamics { get; set; } - - /// - /// Initializes a new instance of the class. - /// - public RenderStylesheet() - { - HideDynamics = false; - } - - internal static void CopyTo(RenderStylesheet src, RenderStylesheet dst) - { - dst.HideDynamics = src.HideDynamics; - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Model/RepeatGroup.cs b/Source/AlphaTab/Model/RepeatGroup.cs deleted file mode 100644 index 0c810f878..000000000 --- a/Source/AlphaTab/Model/RepeatGroup.cs +++ /dev/null @@ -1,92 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Collections; - -namespace AlphaTab.Model -{ - /// - /// This public class can store the information about a group of measures which are repeated - /// - public class RepeatGroup - { - /// - /// All masterbars repeated within this group - /// - public FastList MasterBars { get; set; } - - /// - /// a list of masterbars which open the group. - /// - public FastList Openings { get; set; } - - /// - /// a list of masterbars which close the group. - /// - public FastList Closings { get; set; } - - /// - /// true if the repeat group was opened well - /// - public bool IsOpened { get; set; } - - /// - /// true if the repeat group was closed well - /// - public bool IsClosed { get; set; } - - /// - /// Initializes a new instance of the class. - /// - public RepeatGroup() - { - MasterBars = new FastList(); - Openings = new FastList(); - Closings = new FastList(); - IsClosed = false; - } - - internal void AddMasterBar(MasterBar masterBar) - { - if (Openings.Count == 0) - { - Openings.Add(masterBar); - } - - MasterBars.Add(masterBar); - masterBar.RepeatGroup = this; - - if (masterBar.IsRepeatEnd) - { - Closings.Add(masterBar); - IsClosed = true; - if (!IsOpened) - { - MasterBars[0].IsRepeatStart = true; - IsOpened = true; - } - } - // a new item after the header was closed? -> repeat alternative reopens the group - else if (IsClosed) - { - IsClosed = false; - Openings.Add(masterBar); - } - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Model/Score.cs b/Source/AlphaTab/Model/Score.cs deleted file mode 100644 index 51dfb338b..000000000 --- a/Source/AlphaTab/Model/Score.cs +++ /dev/null @@ -1,188 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Collections; - -namespace AlphaTab.Model -{ - /// - /// The score is the root node of the complete - /// model. It stores the basic information of - /// a song and stores the sub components. - /// - public class Score - { - private RepeatGroup _currentRepeatGroup; - - /// - /// The album of this song. - /// - public string Album { get; set; } - - /// - /// The artist who performs this song. - /// - public string Artist { get; set; } - - /// - /// The owner of the copyright of this song. - /// - public string Copyright { get; set; } - - /// - /// Additional instructions - /// - public string Instructions { get; set; } - - /// - /// The author of the music. - /// - public string Music { get; set; } - - /// - /// Some additional notes about the song. - /// - public string Notices { get; set; } - - /// - /// The subtitle of the song. - /// - public string SubTitle { get; set; } - - /// - /// The title of the song. - /// - public string Title { get; set; } - - /// - /// The author of the song lyrics - /// - public string Words { get; set; } - - /// - /// The author of this tablature. - /// - public string Tab { get; set; } - - /// - /// Gets or sets the global tempo of the song in BPM. The tempo might change via . - /// - public int Tempo { get; set; } - /// - /// Gets or sets the name/label of the tempo. - /// - public string TempoLabel { get; set; } - - /// - /// Gets or sets a list of all masterbars contained in this song. - /// - public FastList MasterBars { get; set; } - - /// - /// Gets or sets a list of all tracks contained in this song. - /// - public FastList Tracks { get; set; } - - /// - /// Gets or sets the rendering stylesheet for this song. - /// - public RenderStylesheet Stylesheet { get; set; } - - /// - /// Initializes a new instance of the class. - /// - public Score() - { - MasterBars = new FastList(); - Tracks = new FastList(); - _currentRepeatGroup = new RepeatGroup(); - Album = Artist = Copyright = Instructions = Music = Notices = SubTitle = Title = Words = Tab = TempoLabel = ""; - Tempo = 120; - Stylesheet = new RenderStylesheet(); - } - - internal static void CopyTo(Score src, Score dst) - { - dst.Album = src.Album; - dst.Artist = src.Artist; - dst.Copyright = src.Copyright; - dst.Instructions = src.Instructions; - dst.Music = src.Music; - dst.Notices = src.Notices; - dst.SubTitle = src.SubTitle; - dst.Title = src.Title; - dst.Words = src.Words; - dst.Tab = src.Tab; - dst.Tempo = src.Tempo; - dst.TempoLabel = src.TempoLabel; - } - - internal void RebuildRepeatGroups() - { - var currentGroup = new RepeatGroup(); - foreach (var bar in MasterBars) - { - // if the group is closed only the next upcoming header can - // reopen the group in case of a repeat alternative, so we - // remove the current group - if (bar.IsRepeatStart || (_currentRepeatGroup.IsClosed && bar.AlternateEndings <= 0)) - { - currentGroup = new RepeatGroup(); - } - currentGroup.AddMasterBar(bar); - } - } - - internal void AddMasterBar(MasterBar bar) - { - bar.Score = this; - bar.Index = MasterBars.Count; - if (MasterBars.Count != 0) - { - bar.PreviousMasterBar = MasterBars[MasterBars.Count - 1]; - bar.PreviousMasterBar.NextMasterBar = bar; - bar.Start = bar.PreviousMasterBar.Start + bar.PreviousMasterBar.CalculateDuration(); - } - - // if the group is closed only the next upcoming header can - // reopen the group in case of a repeat alternative, so we - // remove the current group - if (bar.IsRepeatStart || (_currentRepeatGroup.IsClosed && bar.AlternateEndings <= 0)) - { - _currentRepeatGroup = new RepeatGroup(); - } - _currentRepeatGroup.AddMasterBar(bar); - MasterBars.Add(bar); - } - - internal void AddTrack(Track track) - { - track.Score = this; - track.Index = Tracks.Count; - Tracks.Add(track); - } - - internal void Finish(Settings settings) - { - for (int i = 0, j = Tracks.Count; i < j; i++) - { - Tracks[i].Finish(settings); - } - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Model/Section.cs b/Source/AlphaTab/Model/Section.cs deleted file mode 100644 index d767e3679..000000000 --- a/Source/AlphaTab/Model/Section.cs +++ /dev/null @@ -1,50 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -namespace AlphaTab.Model -{ - /// - /// This public class is used to describe the beginning of a - /// section within a song. It acts like a marker. - /// - public class Section - { - /// - /// Gets or sets the marker ID for this section. - /// - public string Marker { get; set; } - /// - /// Gets or sets the descriptional text of this section. - /// - public string Text { get; set; } - - /// - /// Initializes a new instance of the class. - /// - public Section() - { - Text = Marker = ""; - } - - internal static void CopyTo(Section src, Section dst) - { - dst.Marker = src.Marker; - dst.Text = src.Text; - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Model/SimileMark.cs b/Source/AlphaTab/Model/SimileMark.cs deleted file mode 100644 index 6e3f995b8..000000000 --- a/Source/AlphaTab/Model/SimileMark.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace AlphaTab.Model -{ - /// - /// Lists all simile mark types as they are assigned to bars. - /// - public enum SimileMark - { - /// - /// No simile mark is applied - /// - None, - /// - /// A simple simile mark. The previous bar is repeated. - /// - Simple, - /// - /// A double simile mark. This value is assigned to the first - /// bar of the 2 repeat bars. - /// - FirstOfDouble, - /// - /// A double simile mark. This value is assigned to the second - /// bar of the 2 repeat bars. - /// - SecondOfDouble - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Model/SlideType.cs b/Source/AlphaTab/Model/SlideType.cs deleted file mode 100644 index 4d8eea476..000000000 --- a/Source/AlphaTab/Model/SlideType.cs +++ /dev/null @@ -1,62 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Model -{ - /// - /// This public enum lists all different types of finger slides on a string. - /// - public enum SlideType - { - /// - /// No slide. - /// - None, - /// - /// Shift slide to next note on same string - /// - Shift, - /// - /// Legato slide to next note on same string. - /// - Legato, - /// - /// Slide into the note from below on the same string. - /// - IntoFromBelow, - /// - /// Slide into the note from above on the same string. - /// - IntoFromAbove, - /// - /// Slide out from the note from upwards on the same string. - /// - OutUp, - /// - /// Slide out from the note from downwards on the same string. - /// - OutDown, - /// - /// Pickslide down on this note - /// - PickSlideDown, - /// - /// Pickslide up on this note - /// - PickSlideUp - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Model/Staff.cs b/Source/AlphaTab/Model/Staff.cs deleted file mode 100644 index e0856ed41..000000000 --- a/Source/AlphaTab/Model/Staff.cs +++ /dev/null @@ -1,129 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Collections; - -namespace AlphaTab.Model -{ - /// - /// This class describes a single staff within a track. There are instruments like pianos - /// where a single track can contain multiple staffs. - /// - public class Staff - { - /// - /// Gets or sets the zero-based index of this staff within the track. - /// - public int Index { get; set; } - - /// - /// Gets or sets the reference to the track this staff belongs to. - /// - public Track Track { get; set; } - - /// - /// Gets or sets a list of all bars contained in this staff. - /// - public FastList Bars { get; set; } - - /// - /// Gets or sets a list of all chords defined for this staff. refers to entries in this lookup. - /// - public FastDictionary Chords { get; set; } - - /// - /// Gets or sets the fret on which a capo is set. s - /// - public int Capo { get; set; } - - /// - /// Gets or sets the number of semitones this track should be - /// transposed. This applies to rendering and playback. - /// - public int TranspositionPitch { get; set; } - - /// - /// Gets or sets the number of semitones this track should be - /// transposed. This applies only to rendering. - /// - public int DisplayTranspositionPitch { get; set; } - - /// - /// Get or set the guitar tuning of the guitar. This tuning also indicates the number of strings shown in the - /// guitar tablature. Unlike the property this array directly represents - /// the order of the tracks shown in the tablature. The first item is the most top tablature line. - /// - public int[] Tuning { get; set; } - - /// - /// Gets or sets the name of the tuning. - /// - public string TuningName { get; set; } - - /// - /// Gets a value indicating whether this staff contains string based notes. - /// - public bool IsStringed => Tuning.Length > 0; - - /// - /// Gets or sets the staff kind. - /// - public StaffKind StaffKind { get; set; } - - /// - /// Initializes a new instance of the class. - /// - public Staff() - { - Bars = new FastList(); - Tuning = new int[0]; - Chords = new FastDictionary(); - StaffKind = StaffKind.Mixed; - } - - internal static void CopyTo(Staff src, Staff dst) - { - dst.Capo = src.Capo; - dst.Index = src.Index; - dst.Tuning = Platform.Platform.CloneArray(src.Tuning); - dst.TranspositionPitch = src.TranspositionPitch; - dst.DisplayTranspositionPitch = src.DisplayTranspositionPitch; - dst.StaffKind = src.StaffKind; - } - - internal void Finish(Settings settings) - { - for (int i = 0, j = Bars.Count; i < j; i++) - { - Bars[i].Finish(settings); - } - } - - internal void AddBar(Bar bar) - { - var bars = Bars; - bar.Staff = this; - bar.Index = bars.Count; - if (bars.Count > 0) - { - bar.PreviousBar = bars[bars.Count - 1]; - bar.PreviousBar.NextBar = bar; - } - bars.Add(bar); - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Model/StaffKind.cs b/Source/AlphaTab/Model/StaffKind.cs deleted file mode 100644 index 8996b4d5a..000000000 --- a/Source/AlphaTab/Model/StaffKind.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace AlphaTab.Model -{ - /// - /// Represents the different kinds of staffs. - /// - public enum StaffKind - { - /// - /// The staff should be shown as guitar tablature. - /// - Tablature, - /// - /// The staff should be shown as normal music notation without tabs. - /// - Score, - /// - /// The staff should be shown as percussion tabs. - /// - Percussion, - /// - /// The staff should be shown as mixed tab/music notaiton - /// - Mixed - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Model/Track.cs b/Source/AlphaTab/Model/Track.cs deleted file mode 100644 index 030827f27..000000000 --- a/Source/AlphaTab/Model/Track.cs +++ /dev/null @@ -1,160 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Collections; -using AlphaTab.Platform.Model; - -namespace AlphaTab.Model -{ - /// - /// This public class describes a single track or instrument of score. - /// It is bascially a list of staffs containing individual music notation kinds. - /// - public class Track - { - private const int ShortNameMaxLength = 10; - - /// - /// Gets or sets the zero-based index of this track. - /// - public int Index { get; set; } - - /// - /// Gets or sets the reference this track belongs to. - /// - public Score Score { get; set; } - - /// - /// Gets or sets the list of staffs that are defined for this track. - /// - public FastList Staves { get; set; } - - /// - /// Gets or sets the playback information for this track. - /// - public PlaybackInformation PlaybackInfo { get; set; } - - /// - /// Gets or sets the display color defined for this track. - /// - public Color Color { get; set; } - - /// - /// Gets or sets the long name of this track. - /// - public string Name { get; set; } - - /// - /// Gets or sets the short name of this track. - /// - public string ShortName { get; set; } - - /// - /// Initializes a new instance of the class. - /// - /// The stave count. - public Track(int staveCount) - { - Staves = new FastList(); - EnsureStaveCount(staveCount); - PlaybackInfo = new PlaybackInformation(); - Name = ""; - ShortName = ""; - Color = new Color(200, 0, 0); - } - - internal void EnsureStaveCount(int staveCount) - { - while (Staves.Count < staveCount) - { - AddStaff(new Staff()); - } - } - - internal void AddStaff(Staff staff) - { - staff.Index = Staves.Count; - staff.Track = this; - Staves.Add(staff); - } - - internal static void CopyTo(Track src, Track dst) - { - dst.Name = src.Name; - dst.ShortName = src.ShortName; - dst.Index = src.Index; - dst.Color.Raw = src.Color.Raw; - dst.Color.RGBA = src.Color.RGBA; - } - - internal void Finish(Settings settings) - { - if (string.IsNullOrEmpty(ShortName)) - { - ShortName = Name; - if (ShortName.Length > ShortNameMaxLength) - ShortName = ShortName.Substring(0, ShortNameMaxLength); - } - - for (int i = 0, j = Staves.Count; i < j; i++) - { - Staves[i].Finish(settings); - } - } - - internal void ApplyLyrics(FastList lyrics) - { - foreach (var lyric in lyrics) - { - lyric.Finish(); - } - - var staff = Staves[0]; - - for (var li = 0; li < lyrics.Count; li++) - { - var lyric = lyrics[li]; - if (lyric.StartBar >= 0) - { - var beat = staff.Bars[lyric.StartBar].Voices[0].Beats[0]; - for (int ci = 0; ci < lyric.Chunks.Length && beat != null; ci++) - { - // skip rests and empty beats - while (beat != null && (beat.IsEmpty || beat.IsRest)) - { - beat = beat.NextBeat; - } - - // mismatch between chunks and beats might lead to missing beats - if (beat != null) - { - // initialize lyrics list for beat if required - if (beat.Lyrics == null) - { - beat.Lyrics = new string[lyrics.Count]; - } - // assign chunk - beat.Lyrics[li] = lyric.Chunks[ci]; - beat = beat.NextBeat; - } - } - } - } - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Model/TripletFeel.cs b/Source/AlphaTab/Model/TripletFeel.cs deleted file mode 100644 index 7d1c12c9f..000000000 --- a/Source/AlphaTab/Model/TripletFeel.cs +++ /dev/null @@ -1,56 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Model -{ - // ReSharper disable InconsistentNaming - /// - /// This public enumeration lists all feels of triplets. - /// - public enum TripletFeel - { - /// - /// No triplet feel - /// - NoTripletFeel, - /// - /// Triplet 16th - /// - Triplet16th, - /// - /// Triplet 8th - /// - Triplet8th, - /// - /// Dotted 16th - /// - Dotted16th, - /// - /// Dotte d8th - /// - Dotted8th, - /// - /// Scottish 16th - /// - Scottish16th, - /// - /// Scottish 8th - /// - Scottish8th - } - // ReSharper restore InconsistentNaming -} \ No newline at end of file diff --git a/Source/AlphaTab/Model/Tuning.cs b/Source/AlphaTab/Model/Tuning.cs deleted file mode 100644 index a65925e57..000000000 --- a/Source/AlphaTab/Model/Tuning.cs +++ /dev/null @@ -1,210 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Collections; - -namespace AlphaTab.Model -{ - /// - /// This public class represents a predefined string tuning. - /// - public class Tuning - { - private static FastList _sevenStrings; - private static FastList _sixStrings; - private static FastList _fiveStrings; - private static FastList _fourStrings; - private static FastDictionary _defaultTunings; - - internal static string GetTextForTuning(int tuning, bool includeOctave) - { - var octave = tuning / 12; - var note = tuning % 12; - var notes = new[] { "C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab", "A", "Bb", "B" }; - var result = notes[note]; - if (includeOctave) - { - result += (octave - 1); - } - - return result; - } - - /// - /// Gets the default tuning for the given string count. - /// - /// The string count. - /// The tuning for the given string count or null if the string count is not defined. - public static Tuning GetDefaultTuningFor(int stringCount) - { - if (_defaultTunings.ContainsKey(stringCount)) - return _defaultTunings[stringCount]; - return null; - } - - /// - /// Gets a list of all tuning presets for a given stirng count. - /// - /// The string count. - /// The list of known tunings for the given string count or an empty list if the string count is not defined. - public static FastList GetPresetsFor(int stringCount) - { - switch (stringCount) - { - case 7: - return _sevenStrings; - case 6: - return _sixStrings; - case 5: - return _fiveStrings; - case 4: - return _fourStrings; - } - return new FastList(); - } - - static Tuning() - { - Initialize(); - } - - private static void Initialize() - { - _sevenStrings = new FastList(); - _sixStrings = new FastList(); - _fiveStrings = new FastList(); - _fourStrings = new FastList(); - _defaultTunings = new FastDictionary(); - - _defaultTunings[7] = new Tuning("Guitar 7 strings", new[] { 64, 59, 55, 50, 45, 40, 35 }, true); - _sevenStrings.Add(_defaultTunings[7]); - - _defaultTunings[6] = new Tuning("Guitar Standard Tuning", new[] { 64, 59, 55, 50, 45, 40 }, true); - _sixStrings.Add(_defaultTunings[6]); - - _sixStrings.Add(new Tuning("Guitar Tune down ½ step", new[] { 63, 58, 54, 49, 44, 39 }, false)); - _sixStrings.Add(new Tuning("Guitar Tune down 1 step", new[] { 62, 57, 53, 48, 43, 38 }, false)); - _sixStrings.Add(new Tuning("Guitar Tune down 2 step", new[] { 60, 55, 51, 46, 41, 36 }, false)); - _sixStrings.Add(new Tuning("Guitar Dropped D Tuning", new[] { 64, 59, 55, 50, 45, 38 }, false)); - _sixStrings.Add(new Tuning("Guitar Dropped D Tuning variant", new[] { 64, 57, 55, 50, 45, 38 }, false)); - _sixStrings.Add(new Tuning("Guitar Double Dropped D Tuning", new[] { 62, 59, 55, 50, 45, 38 }, false)); - _sixStrings.Add(new Tuning("Guitar Dropped E Tuning", new[] { 66, 61, 57, 52, 47, 40 }, false)); - _sixStrings.Add(new Tuning("Guitar Dropped C Tuning", new[] { 62, 57, 53, 48, 43, 36 }, false)); - - _sixStrings.Add(new Tuning("Guitar Open C Tuning", new[] { 64, 60, 55, 48, 43, 36 }, false)); - _sixStrings.Add(new Tuning("Guitar Open Cm Tuning", new[] { 63, 60, 55, 48, 43, 36 }, false)); - _sixStrings.Add(new Tuning("Guitar Open C6 Tuning", new[] { 64, 57, 55, 48, 43, 36 }, false)); - _sixStrings.Add(new Tuning("Guitar Open Cmaj7 Tuning", new[] { 64, 59, 55, 52, 43, 36 }, false)); - _sixStrings.Add(new Tuning("Guitar Open D Tuning", new[] { 62, 57, 54, 50, 45, 38 }, false)); - _sixStrings.Add(new Tuning("Guitar Open Dm Tuning", new[] { 62, 57, 53, 50, 45, 38 }, false)); - _sixStrings.Add(new Tuning("Guitar Open D5 Tuning", new[] { 62, 57, 50, 50, 45, 38 }, false)); - _sixStrings.Add(new Tuning("Guitar Open D6 Tuning", new[] { 62, 59, 54, 50, 45, 38 }, false)); - _sixStrings.Add(new Tuning("Guitar Open Dsus4 Tuning", new[] { 62, 57, 55, 50, 45, 38 }, false)); - _sixStrings.Add(new Tuning("Guitar Open E Tuning", new[] { 64, 59, 56, 52, 47, 40 }, false)); - _sixStrings.Add(new Tuning("Guitar Open Em Tuning", new[] { 64, 59, 55, 52, 47, 40 }, false)); - _sixStrings.Add(new Tuning("Guitar Open Esus11 Tuning", new[] { 64, 59, 55, 52, 45, 40 }, false)); - _sixStrings.Add(new Tuning("Guitar Open F Tuning", new[] { 65, 60, 53, 48, 45, 41 }, false)); - _sixStrings.Add(new Tuning("Guitar Open G Tuning", new[] { 62, 59, 55, 50, 43, 38 }, false)); - _sixStrings.Add(new Tuning("Guitar Open Gm Tuning", new[] { 62, 58, 55, 50, 43, 38 }, false)); - _sixStrings.Add(new Tuning("Guitar Open G6 Tuning", new[] { 64, 59, 55, 50, 43, 38 }, false)); - _sixStrings.Add(new Tuning("Guitar Open Gsus4 Tuning", new[] { 62, 60, 55, 50, 43, 38 }, false)); - _sixStrings.Add(new Tuning("Guitar Open A Tuning", new[] { 64, 61, 57, 52, 45, 40 }, false)); - _sixStrings.Add(new Tuning("Guitar Open Am Tuning", new[] { 64, 60, 57, 52, 45, 40 }, false)); - _sixStrings.Add(new Tuning("Guitar Nashville Tuning", new[] { 64, 59, 67, 62, 57, 52 }, false)); - _sixStrings.Add(new Tuning("Bass 6 Strings Tuning", new[] { 48, 43, 38, 33, 28, 23 }, false)); - _sixStrings.Add(new Tuning("Lute or Vihuela Tuning", new[] { 64, 59, 54, 50, 45, 40 }, false)); - - _defaultTunings[5] = new Tuning("Bass 5 Strings Tuning", new[] { 43, 38, 33, 28, 23 }, true); - _fiveStrings.Add(_defaultTunings[5]); - _fiveStrings.Add(new Tuning("Banjo Dropped C Tuning", new[] { 62, 59, 55, 48, 67 }, false)); - _fiveStrings.Add(new Tuning("Banjo Open D Tuning", new[] { 62, 57, 54, 50, 69 }, false)); - _fiveStrings.Add(new Tuning("Banjo Open G Tuning", new[] { 62, 59, 55, 50, 67 }, false)); - _fiveStrings.Add(new Tuning("Banjo G Minor Tuning", new[] { 62, 58, 55, 50, 67 }, false)); - _fiveStrings.Add(new Tuning("Banjo G Modal Tuning", new[] { 62, 57, 55, 50, 67 }, false)); - - _defaultTunings[4] = new Tuning("Bass Standard Tuning", new[] { 43, 38, 33, 28 }, true); - _fourStrings.Add(_defaultTunings[4]); - _fourStrings.Add(new Tuning("Bass Tune down ½ step", new[] { 42, 37, 32, 27 }, false)); - _fourStrings.Add(new Tuning("Bass Tune down 1 step", new[] { 41, 36, 31, 26 }, false)); - _fourStrings.Add(new Tuning("Bass Tune down 2 step", new[] { 39, 34, 29, 24 }, false)); - _fourStrings.Add(new Tuning("Bass Dropped D Tuning", new[] { 43, 38, 33, 26 }, false)); - _fourStrings.Add(new Tuning("Ukulele C Tuning", new[] { 45, 40, 36, 43 }, false)); - _fourStrings.Add(new Tuning("Ukulele G Tuning", new[] { 52, 47, 43, 38 }, false)); - _fourStrings.Add(new Tuning("Mandolin Standard Tuning", new[] { 64, 57, 50, 43 }, false)); - _fourStrings.Add(new Tuning("Mandolin or Violin Tuning", new[] { 76, 69, 62, 55 }, false)); - _fourStrings.Add(new Tuning("Viola Tuning", new[] { 69, 62, 55, 48 }, false)); - _fourStrings.Add(new Tuning("Cello Tuning", new[] { 57, 50, 43, 36 }, false)); - } - - /// - /// Tries to find a known tuning by a given list of tuning values. - /// - /// The values defining the tuning. - /// The known tuning. - public static Tuning FindTuning(int[] strings) - { - var tunings = GetPresetsFor(strings.Length); - for (int t = 0, tc = tunings.Count; t < tc; t++) - { - var tuning = tunings[t]; - var equals = true; - for (int i = 0, j = strings.Length; i < j; i++) - { - if (strings[i] != tuning.Tunings[i]) - { - equals = false; - break; - } - } - - if (equals) - { - return tuning; - } - } - - return null; - } - - /// - /// Gets or sets whether this is the standard tuning for this number of strings. - /// - public bool IsStandard { get; set; } - /// - /// Gets or sets the name of the tuning. - /// - public string Name { get; set; } - /// - /// Gets or sets the values for each string of the instrument. - /// - public int[] Tunings { get; set; } - - /// - /// Initializes a new instance of the class. - /// - /// The name. - /// The tuning. - /// if set to true [is standard]. - public Tuning(string name, int[] tuning, bool isStandard) - { - IsStandard = isStandard; - Name = name; - Tunings = tuning; - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Model/TuningParser.cs b/Source/AlphaTab/Model/TuningParser.cs deleted file mode 100644 index d519be24c..000000000 --- a/Source/AlphaTab/Model/TuningParser.cs +++ /dev/null @@ -1,152 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -namespace AlphaTab.Model -{ - class TuningParseResult - { - public string Note { get; set; } - public int NoteValue { get; set; } - public int Octave { get; set; } - - public int RealValue - { - get - { - return (Octave * 12) + NoteValue; - } - } - } - - static class TuningParser - { - /// - /// Checks if the given string is a tuning inticator. - /// Checks if the given string is a tuning inticator. - /// - /// - public static bool IsTuning(string name) - { - return Parse(name) != null; - } - - public static TuningParseResult Parse(string name) - { - string note = ""; - string octave = ""; - - for (int i = 0; i < name.Length; i++) - { - var c = (int)name[i]; - if (Platform.Platform.IsCharNumber(c, false)) - { - // number without note? - if (string.IsNullOrEmpty(note)) - { - return null; - } - octave += Platform.Platform.StringFromCharCode(c); - } - else if ((c >= 0x41 && c <= 0x5A) || (c >= 0x61 && c <= 0x7A) || c == 0x23) - { - note += Platform.Platform.StringFromCharCode(c); - } - else - { - return null; - } - } - - if (string.IsNullOrEmpty(octave) || string.IsNullOrEmpty(note)) - { - return null; - } - - var result = new TuningParseResult(); - result.Octave = Platform.Platform.ParseInt(octave) + 1; - result.Note = note.ToLower(); - result.NoteValue = GetToneForText(result.Note); - return result; - } - - public static int GetTuningForText(string str) - { - var result = Parse(str); - if (result == null) - { - return -1; - } - - return result.RealValue; - } - - public static int GetToneForText(string note) - { - int b; - switch (note.ToLower()) - { - case "c": - b = 0; - break; - case "c#": - case "db": - b = 1; - break; - case "d": - b = 2; - break; - case "d#": - case "eb": - b = 3; - break; - case "e": - b = 4; - break; - case "f": - b = 5; - break; - case "f#": - case "gb": - b = 6; - break; - case "g": - b = 7; - break; - case "g#": - case "ab": - b = 8; - break; - case "a": - b = 9; - break; - case "a#": - case "bb": - b = 10; - break; - case "b": - b = 11; - break; - default: - return 0; - } - - return b; - } - - } -} diff --git a/Source/AlphaTab/Model/VibratoType.cs b/Source/AlphaTab/Model/VibratoType.cs deleted file mode 100644 index 5c0e9b496..000000000 --- a/Source/AlphaTab/Model/VibratoType.cs +++ /dev/null @@ -1,38 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Model -{ - /// - /// This public enum lists all vibrato types that can be performed. - /// - public enum VibratoType - { - /// - /// No vibrato. - /// - None, - /// - /// A slight vibrato. - /// - Slight, - /// - /// A wide vibrato. - /// - Wide - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Model/Voice.cs b/Source/AlphaTab/Model/Voice.cs deleted file mode 100644 index 0c04bf4c3..000000000 --- a/Source/AlphaTab/Model/Voice.cs +++ /dev/null @@ -1,240 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Audio; -using AlphaTab.Collections; - -namespace AlphaTab.Model -{ - /// - /// A voice represents a group of beats - /// that can be played during a bar. - /// - public class Voice - { - private FastDictionary _beatLookup; - - /// - /// Gets or sets the zero-based index of this voice within the bar. - /// - public int Index { get; set; } - /// - /// Gets or sets the reference to the bar this voice belongs to. - /// - public Bar Bar { get; set; } - - /// - /// Gets or sets the list of beats contained in this voice. - /// - public FastList Beats { get; set; } - - /// - /// Gets or sets a value indicating whether this voice is empty. - /// - public bool IsEmpty - { - get; set; - } - - /// - /// Initializes a new instance of the class. - /// - public Voice() - { - Beats = new FastList(); - IsEmpty = true; - } - - internal static void CopyTo(Voice src, Voice dst) - { - dst.Index = src.Index; - dst.IsEmpty = src.IsEmpty; - } - - internal void InsertBeat(Beat after, Beat newBeat) - { - newBeat.NextBeat = after.NextBeat; - if (newBeat.NextBeat != null) - { - newBeat.NextBeat.PreviousBeat = newBeat; - } - newBeat.PreviousBeat = after; - newBeat.Voice = this; - after.NextBeat = newBeat; - Beats.InsertAt(after.Index + 1, newBeat); - } - - - internal void AddBeat(Beat beat) - { - beat.Voice = this; - beat.Index = Beats.Count; - Beats.Add(beat); - if (!beat.IsEmpty) - { - IsEmpty = false; - } - } - - private void Chain(Beat beat) - { - if (Bar == null) return; - - if (beat.Index < Beats.Count - 1) - { - beat.NextBeat = Beats[beat.Index + 1]; - beat.NextBeat.PreviousBeat = beat; - } - else if (beat.IsLastOfVoice && beat.Voice.Bar.NextBar != null) - { - var nextVoice = Bar.NextBar.Voices[Index]; - if (nextVoice.Beats.Count > 0) - { - beat.NextBeat = nextVoice.Beats[0]; - beat.NextBeat.PreviousBeat = beat; - } - else - { - beat.NextBeat.PreviousBeat = beat; - } - } - } - - internal void AddGraceBeat(Beat beat) - { - if (Beats.Count == 0) - { - AddBeat(beat); - return; - } - - // remove last beat - var lastBeat = Beats[Beats.Count - 1]; - Beats.RemoveAt(Beats.Count - 1); - - // insert grace beat - AddBeat(beat); - // reinsert last beat - AddBeat(lastBeat); - - IsEmpty = false; - } - - internal Beat GetBeatAtDisplayStart(int displayStart) - { - if (_beatLookup.ContainsKey(displayStart)) - { - return _beatLookup[displayStart]; - } - - return null; - } - - internal void Finish(Settings settings) - { - _beatLookup = new FastDictionary(); - for (var index = 0; index < Beats.Count; index++) - { - var beat = Beats[index]; - beat.Index = index; - Chain(beat); - } - - var currentDisplayTick = 0; - var currentPlaybackTick = 0; - for (var i = 0; i < Beats.Count; i++) - { - var beat = Beats[i]; - beat.Index = i; - beat.Finish(settings); - - if (beat.GraceType == GraceType.None || beat.GraceType == GraceType.BendGrace) - { - beat.DisplayStart = currentDisplayTick; - beat.PlaybackStart = currentPlaybackTick; - currentDisplayTick += beat.DisplayDuration; - currentPlaybackTick += beat.PlaybackDuration; - } - else - { - // find note which is not a grace note - Beat nonGrace = beat; - int numberOfGraceBeats = 0; - while (nonGrace != null && nonGrace.GraceType != GraceType.None) - { - nonGrace = nonGrace.NextBeat; - numberOfGraceBeats++; - } - - var graceDuration = Duration.Eighth; - int stolenDuration = 0; - if (numberOfGraceBeats == 1) - { - graceDuration = Duration.Eighth; - stolenDuration = Duration.ThirtySecond.ToTicks(); - } - else if (numberOfGraceBeats == 2) - { - graceDuration = Duration.Sixteenth; - stolenDuration = Duration.SixtyFourth.ToTicks(); - } - else - { - graceDuration = Duration.ThirtySecond; - stolenDuration = Duration.OneHundredTwentyEighth.ToTicks(); - } - - - // grace beats have 1/4 size of the non grace beat following them - var perGraceDuration = nonGrace == null ? Duration.ThirtySecond.ToTicks() : (nonGrace.DisplayDuration / 4) / numberOfGraceBeats; - - // move all grace beats - for (int j = 0; j < numberOfGraceBeats; j++) - { - var graceBeat = Beats[j + i]; - if (beat.PreviousBeat == null || beat.PreviousBeat.GraceType == GraceType.None) - { - graceBeat.Duration = graceDuration; - graceBeat.UpdateDurations(); - } - graceBeat.DisplayStart = currentDisplayTick - (numberOfGraceBeats - j + 1) * perGraceDuration; - graceBeat.DisplayDuration = perGraceDuration; - } - - if (beat.PreviousBeat != null && beat.GraceType == GraceType.BeforeBeat) - { - beat.PreviousBeat.PlaybackDuration -= stolenDuration; - } - - beat.PlaybackStart = currentPlaybackTick; - switch (beat.GraceType) - { - case GraceType.BeforeBeat: - beat.PlaybackStart -= beat.PlaybackDuration; - break; - case GraceType.OnBeat: - currentPlaybackTick += beat.PlaybackDuration; - break; - } - } - - _beatLookup[beat.DisplayStart] = beat; - } - } - } -} diff --git a/Source/AlphaTab/Model/WhammyType.cs b/Source/AlphaTab/Model/WhammyType.cs deleted file mode 100644 index 67a0776e9..000000000 --- a/Source/AlphaTab/Model/WhammyType.cs +++ /dev/null @@ -1,39 +0,0 @@ -namespace AlphaTab.Model -{ - /// - /// Lists all types of whammy bars - /// - public enum WhammyType - { - /// - /// No whammy at all - /// - None, - /// - /// Individual points define the whammy in a flexible manner. - /// This system was mainly used in Guitar Pro 3-5 - /// - Custom, - /// - /// Simple dive to a lower or higher note. - /// - Dive, - /// - /// A dive to a lower or higher note and releasing it back to normal. - /// - Dip, - /// - /// Continue to hold the whammy at the position from a previous whammy. - /// - Hold, - /// - /// Dive to a lower or higher note before playing it. - /// - Predive, - /// - /// Dive to a lower or higher note before playing it, then change to another - /// note. - /// - PrediveDive - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Platform/ICanvas.cs b/Source/AlphaTab/Platform/ICanvas.cs deleted file mode 100644 index 870011852..000000000 --- a/Source/AlphaTab/Platform/ICanvas.cs +++ /dev/null @@ -1,78 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Platform.Model; -using AlphaTab.Rendering; -using AlphaTab.Rendering.Glyphs; -using Color = AlphaTab.Platform.Model.Color; - -namespace AlphaTab.Platform -{ - /// - /// This is the base public interface for canvas implementations on different plattforms. - /// - interface ICanvas : IPathCanvas - { - RenderingResources Resources { get; set; } - - Color Color { get; set; } - - float LineWidth { get; set; } - - void FillRect(float x, float y, float w, float h); - void StrokeRect(float x, float y, float w, float h); - void FillCircle(float x, float y, float radius); - - Font Font { get; set; } - TextAlign TextAlign { get; set; } - TextBaseline TextBaseline { get; set; } - - void BeginGroup(string identifier); - void EndGroup(); - - void FillText(string text, float x, float y); - float MeasureText(string text); - void FillMusicFontSymbol(float x, float y, float scale, MusicFontSymbol symbol); - void FillMusicFontSymbols(float x, float y, float scale, MusicFontSymbol[] symbols); - - object OnPreRender(); - void BeginRender(float width, float height); - object EndRender(); - object OnRenderFinished(); - - void BeginRotate(float centerX, float centerY, float angle); - void EndRotate(); - } - - /// - /// This is the path drawing API for canvas implementations - /// - // NOTE: For a full HTML based rendering we need to get rid of those - interface IPathCanvas - { - void BeginPath(); - void ClosePath(); - void Fill(); - void Stroke(); - - void MoveTo(float x, float y); - void LineTo(float x, float y); - void BezierCurveTo(float cp1x, float cp1y, float cp2x, float cp2y, float x, float y); - void QuadraticCurveTo(float cpx, float cpy, float x, float y); - } -} diff --git a/Source/AlphaTab/Platform/Model/Color.cs b/Source/AlphaTab/Platform/Model/Color.cs deleted file mode 100644 index 2629b6f6a..000000000 --- a/Source/AlphaTab/Platform/Model/Color.cs +++ /dev/null @@ -1,96 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -namespace AlphaTab.Platform.Model -{ - /// - /// A color object which allows accessing each color component individually. - /// - public class Color - { - /// - /// Gets the hex string for black. - /// - public const string BlackRgb = "#000000"; - - /// - /// Initializes a new instance of the class. - /// - /// The red component. - /// The green component. - /// The blue component. - /// The alpha component. - public Color(byte r, byte g, byte b, byte a = 0xFF) - { - Raw = (a << 24) | (r << 16) | (g << 8) | b; - if (A == 0xFF) - { - RGBA = "#" + Platform.ToHexString(R, 2) + Platform.ToHexString(G, 2) + Platform.ToHexString(B, 2); - } - else - { - RGBA = "rgba(" + R + "," + G + "," + B + "," + (A / 255.0) + ")"; - } - } - - /// - /// Gets or sets the raw RGBA value. - /// - public int Raw - { - get; - set; - } - - /// - /// Gets or sets the alpha component of the color. - /// - public byte A => (byte)((Raw >> 24) & 0xFF); - - /// - /// Gets or sets the red component of the color. - /// - public byte R => (byte)((Raw >> 16) & 0xFF); - - /// - /// Gets or sets the green component of the color. - /// - public byte G => (byte)((Raw >> 8) & 0xFF); - - /// - /// Gets or sets the blue component of the color. - /// - public byte B => (byte)(Raw & 0xFF); - - /// - /// Gets the RGBA hex string to use in CSS areas. - /// - public string RGBA - { - get; internal set; - } - - internal static Color Random(byte opacity = 100) - { - return new Color((byte)Platform.Random(255), - (byte)Platform.Random(255), - (byte)Platform.Random(255), - opacity); - } - } -} diff --git a/Source/AlphaTab/Platform/Model/Font.cs b/Source/AlphaTab/Platform/Model/Font.cs deleted file mode 100644 index bc3059e17..000000000 --- a/Source/AlphaTab/Platform/Model/Font.cs +++ /dev/null @@ -1,99 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Collections; - -namespace AlphaTab.Platform.Model -{ - /// - /// This container public class can store the definition for a font and it's style. - /// - public class Font - { - private readonly string _css; - - /// - /// Gets or sets the font family name. - /// - public string Family { get; set; } - /// - /// Gets or sets the font size in pixels. - /// - public float Size { get; set; } - /// - /// Gets or sets the font style. - /// - public FontStyle Style { get; set; } - - /// - /// Gets a value indicating whether the font is bold. - /// - public bool IsBold => (Style & FontStyle.Bold) != 0; - - /// - /// Gets a value indicating whether the font is italic. - /// - public bool IsItalic => (Style & FontStyle.Italic) != 0; - - /// - /// Initializes a new instance of the class. - /// - /// The family. - /// The size. - /// The style. - public Font(string family, float size, FontStyle style = FontStyle.Plain) - { - Family = family; - Size = size; - Style = style; - _css = ToCssString(); - } - - internal Font Clone() - { - return new Font(Family, Size, Style); - } - - internal string ToCssString(float scale = 1) - { - if (_css != null && scale == 1) - { - return _css; - } - - var buf = new StringBuilder(); - - if (IsBold) - { - buf.Append("bold "); - } - if (IsItalic) - { - buf.Append("italic "); - } - - buf.Append(Size * scale); - buf.Append("px "); - buf.Append("'"); - buf.Append(Family); - buf.Append("'"); - - return buf.ToString(); - } - } -} diff --git a/Source/AlphaTab/Platform/Model/FontStyle.cs b/Source/AlphaTab/Platform/Model/FontStyle.cs deleted file mode 100644 index df8ed010a..000000000 --- a/Source/AlphaTab/Platform/Model/FontStyle.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; - -namespace AlphaTab.Platform.Model -{ - /// - /// Lists all flags for font styles. - /// - [Flags] - public enum FontStyle - { - /// - /// No flags. - /// - Plain = 0, - /// - /// Font is bold - /// - Bold = 1, - /// - /// Font is italic. - /// - Italic = 2 - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Platform/Model/TextAlign.cs b/Source/AlphaTab/Platform/Model/TextAlign.cs deleted file mode 100644 index ad81f9518..000000000 --- a/Source/AlphaTab/Platform/Model/TextAlign.cs +++ /dev/null @@ -1,38 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Platform.Model -{ - /// - /// This public enum lists all different text alignments - /// - public enum TextAlign - { - /// - /// Text is left aligned. - /// - Left, - /// - /// Text is centered. - /// - Center, - /// - /// Text is right aligned. - /// - Right - } -} diff --git a/Source/AlphaTab/Platform/Model/TextBaseline.cs b/Source/AlphaTab/Platform/Model/TextBaseline.cs deleted file mode 100644 index 76cdd3632..000000000 --- a/Source/AlphaTab/Platform/Model/TextBaseline.cs +++ /dev/null @@ -1,38 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Platform.Model -{ - /// - /// This public enum lists all base line modes - /// - public enum TextBaseline - { - /// - /// Text is aligned on top. - /// - Top, - /// - /// Text is aligned middle - /// - Middle, - /// - /// Text is aligend on the bottom. - /// - Bottom - } -} diff --git a/Source/AlphaTab/Platform/Platform.cs b/Source/AlphaTab/Platform/Platform.cs deleted file mode 100644 index ae22c3128..000000000 --- a/Source/AlphaTab/Platform/Platform.cs +++ /dev/null @@ -1,110 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using AlphaTab.Collections; -using AlphaTab.Xml; - -namespace AlphaTab.Platform -{ - static partial class Platform - { - public static bool IsStringNumber(string s, bool allowSign = true) - { - if (s.Length == 0) return false; - var c = s[0]; - return IsCharNumber(c, allowSign); - } - - public static bool IsCharNumber(int c, bool allowSign = true) - { - return (allowSign && c == 0x2D) || (c >= 0x30 && c <= 0x39); - } - - public static bool IsWhiteSpace(int c) - { - return c == 0x20 || c == 0x0B || c == 0x0D || c == 0x0A || c == 0x09; - } - - public static bool IsAlmostEqualTo(this float a, float b) - { - return Math.Abs(a - b) < 0.00001f; - } - - public static string ToHexString(int n, int digits = 0) - { - var s = ""; - const string hexChars = "0123456789ABCDEF"; - do - { - s = StringFromCharCode((int)hexChars[(n & 15)]) + s; - n >>= 4; - } while (n > 0); - - while (s.Length < digits) - { - s = "0" + s; - } - - return s; - } - - public static uint ToUInt32(int i) - { - return (uint)i; - } - - public static short ToInt16(int i) - { - return (short)i; - } - - public static ushort ToUInt16(int i) - { - return (ushort)i; - } - - public static byte ToUInt8(int i) - { - return (byte)i; - } - - - private static string DetectEncoding(byte[] data) - { - if (data.Length > 2 && data[0] == 0xFE && data[1] == 0xFF) - { - return "utf-16be"; - } - if (data.Length > 2 && data[0] == 0xFF && data[1] == 0xFE) - { - return "utf-16le"; - } - if (data.Length > 4 && data[0] == 0x00 && data[1] == 0x00 && data[2] == 0xFE && data[3] == 0xFF) - { - return "utf-32be"; - } - if (data.Length > 4 && data[0] == 0xFF && data[1] == 0xFE && data[2] == 0x00 && data[3] == 0x00) - { - return "utf-32le"; - } - - return null; - } - } -} diff --git a/Source/AlphaTab/Platform/Std.cs b/Source/AlphaTab/Platform/Std.cs deleted file mode 100644 index 35d2b7c89..000000000 --- a/Source/AlphaTab/Platform/Std.cs +++ /dev/null @@ -1,62 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2017, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using AlphaTab.Collections; -using AlphaTab.Xml; - -namespace AlphaTab.Platform -{ - public static partial class Std - { - public static bool IsStringNumber(string s, bool allowSign = true) - { - if (s.Length == 0) return false; - var c = s[0]; - return IsCharNumber(c, allowSign); - } - - public static bool IsCharNumber(int c, bool allowSign = true) - { - return (allowSign && c == 0x2D) || (c >= 0x30 && c <= 0x39); - } - - public static bool IsWhiteSpace(int c) - { - return c == 0x20 || c == 0x0B || c == 0x0D || c == 0x0A || c == 0x09; - } - - public static string ToHexString(int n, int digits = 0) - { - var s = ""; - const string hexChars = "0123456789ABCDEF"; - do - { - s = StringFromCharCode((int)hexChars[(n & 15)]) + s; - n >>= 4; - } while (n > 0); - - while (s.Length < digits) - { - s = "0" + s; - } - - return s; - } - } -} diff --git a/Source/AlphaTab/Platform/Svg/CssFontSvgCanvas.cs b/Source/AlphaTab/Platform/Svg/CssFontSvgCanvas.cs deleted file mode 100644 index c7950fb8a..000000000 --- a/Source/AlphaTab/Platform/Svg/CssFontSvgCanvas.cs +++ /dev/null @@ -1,73 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Rendering.Glyphs; - -namespace AlphaTab.Platform.Svg -{ - /// - /// This SVG canvas renders the music symbols by adding a CSS class 'at' to all elements. - /// - class CssFontSvgCanvas : SvgCanvas - { - public override void FillMusicFontSymbol(float x, float y, float scale, MusicFontSymbol symbol) - { - if (symbol == MusicFontSymbol.None) return; - Buffer.Append("&#" + (int)symbol + ";"); - } - - public override void FillMusicFontSymbols(float x, float y, float scale, MusicFontSymbol[] symbols) - { - var s = ""; - foreach (var symbol in symbols) - { - if (symbol != MusicFontSymbol.None) - { - s += "&#" + (int)symbol + ";"; - } - } - - Buffer.Append("" + s + ""); - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Platform/Svg/FontSizes.cs b/Source/AlphaTab/Platform/Svg/FontSizes.cs deleted file mode 100644 index 389b2e61b..000000000 --- a/Source/AlphaTab/Platform/Svg/FontSizes.cs +++ /dev/null @@ -1,79 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.Platform.Model; - -namespace AlphaTab.Platform.Svg -{ - /// - /// This public class stores text widths for several fonts and allows width calculation - /// - class FontSizes - { - // NOTE: use tools/FontMeasureMent.html to generate those arrays - // TODO: probably use a regression function for this - public static byte[] TimesNewRoman = { 3, 4, 5, 6, 6, 9, 9, 2, 4, 4, 6, 6, 3, 4, 3, 3, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 3, 3, 6, 6, 6, 5, 10, 8, 7, 7, 8, 7, 6, 7, 8, 4, 4, 8, 7, 10, 8, 8, 7, 8, 7, 5, 8, 8, 7, 11, 8, 8, 7, 4, 3, 4, 5, 6, 4, 5, 5, 5, 5, 5, 4, 5, 6, 3, 3, 6, 3, 9, 6, 6, 6, 5, 4, 4, 4, 5, 6, 7, 6, 6, 5, 5, 2, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 6, 6, 6, 6, 2, 5, 4, 8, 4, 6, 6, 0, 8, 6, 4, 6, 3, 3, 4, 5, 5, 4, 4, 3, 3, 6, 8, 8, 8, 5, 8, 8, 8, 8, 8, 8, 11, 7, 7, 7, 7, 7, 4, 4, 4, 4, 8, 8, 8, 8, 8, 8, 8, 6, 8, 8, 8, 8, 8, 8, 6, 5, 5, 5, 5, 5, 5, 5, 8, 5, 5, 5, 5, 5, 3, 3, 3, 3, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 6, 6 }; - public static byte[] Arial11Pt = { 3, 3, 4, 6, 6, 10, 7, 2, 4, 4, 4, 6, 3, 4, 3, 3, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 3, 3, 6, 6, 6, 6, 11, 7, 7, 8, 8, 7, 7, 9, 8, 3, 6, 7, 6, 9, 8, 9, 7, 9, 8, 7, 7, 8, 7, 10, 7, 7, 7, 3, 3, 3, 5, 6, 4, 6, 6, 6, 6, 6, 3, 6, 6, 2, 2, 6, 2, 9, 6, 6, 6, 6, 4, 6, 3, 6, 6, 8, 6, 6, 6, 4, 3, 4, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 6, 6, 6, 6, 3, 6, 4, 8, 4, 6, 6, 0, 8, 6, 4, 6, 4, 4, 4, 6, 6, 4, 4, 4, 4, 6, 9, 9, 9, 7, 7, 7, 7, 7, 7, 7, 11, 8, 7, 7, 7, 7, 3, 3, 3, 3, 8, 8, 9, 9, 9, 9, 9, 6, 9, 8, 8, 8, 8, 7, 7, 7, 6, 6, 6, 6, 6, 6, 10, 6, 6, 6, 6, 6, 3, 3, 3, 3, 6, 6, 6, 6, 6, 6, 6, 6, 7, 6, 6, 6, 6, 6, 6 }; - - public const int ControlChars = 0x20; - - public static float MeasureString(string s, SupportedFonts f, float size, FontStyle style) - { - byte[] data; - int dataSize; - float factor = 1; - if ((style & FontStyle.Italic) != 0) - { - factor *= 1.2f; - } - if ((style & FontStyle.Bold) != 0) - { - factor *= 1.2f; - } - - if (f == SupportedFonts.TimesNewRoman) - { - data = TimesNewRoman; - dataSize = 11; - } - else if (f == SupportedFonts.Arial) - { - data = Arial11Pt; - dataSize = 11; - } - else - { - data = new byte[] { 8 }; - dataSize = 11; - } - - var stringSize = 0f; - - for (int i = 0; i < s.Length; i++) - { - var code = Math.Min(data.Length - 1, s[i] - ControlChars); - if (code >= 0) - { - stringSize += ((data[code] * size) / dataSize); - } - } - - return stringSize * factor; - } - } -} diff --git a/Source/AlphaTab/Platform/Svg/SupportedFonts.cs b/Source/AlphaTab/Platform/Svg/SupportedFonts.cs deleted file mode 100644 index f17d86868..000000000 --- a/Source/AlphaTab/Platform/Svg/SupportedFonts.cs +++ /dev/null @@ -1,28 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Platform.Svg -{ - /// - /// The supported fonts by the FontSizes public class - /// - enum SupportedFonts - { - TimesNewRoman, - Arial - } -} diff --git a/Source/AlphaTab/Platform/Svg/SvgCanvas.cs b/Source/AlphaTab/Platform/Svg/SvgCanvas.cs deleted file mode 100644 index bcfcfd38a..000000000 --- a/Source/AlphaTab/Platform/Svg/SvgCanvas.cs +++ /dev/null @@ -1,260 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using AlphaTab.Collections; -using AlphaTab.Platform.Model; -using AlphaTab.Rendering; -using AlphaTab.Rendering.Glyphs; - -namespace AlphaTab.Platform.Svg -{ - /// - /// A canvas implementation storing SVG data - /// - abstract class SvgCanvas : ICanvas, IPathCanvas - { - protected const float BlurCorrection = 0; - - protected StringBuilder Buffer; - private StringBuilder _currentPath; - private bool _currentPathIsEmpty; - - public Color Color { get; set; } - public float LineWidth { get; set; } - public Font Font { get; set; } - public TextAlign TextAlign { get; set; } - public TextBaseline TextBaseline { get; set; } - public RenderingResources Resources { get; set; } - - public SvgCanvas() - { - _currentPath = new StringBuilder(); - _currentPathIsEmpty = true; - Color = new Color(255, 255, 255); - LineWidth = 1; - Font = new Font("Arial", 10); - TextAlign = TextAlign.Left; - TextBaseline = TextBaseline.Top; - } - - public virtual void BeginRender(float width, float height) - { - Buffer = new StringBuilder(); - - Buffer.Append("\n"); - _currentPath = new StringBuilder(); - _currentPathIsEmpty = true; - } - - public void BeginGroup(string identifier) - { - Buffer.Append(""); - } - - public void EndGroup() - { - Buffer.Append(""); - } - - public virtual object EndRender() - { - Buffer.Append(""); - return Buffer.ToString(); - } - - public void FillRect(float x, float y, float w, float h) - { - if (w > 0) - { - Buffer.Append("\n"); - } - } - - public void StrokeRect(float x, float y, float w, float h) - { - Buffer.Append("\n"); - } - - public void BeginPath() - { - } - - public void ClosePath() - { - _currentPath.Append(" z"); - } - - public void MoveTo(float x, float y) - { - _currentPath.Append(" M" + (x - BlurCorrection) + "," + (y - BlurCorrection)); - } - - public void LineTo(float x, float y) - { - _currentPathIsEmpty = false; - _currentPath.Append(" L" + (x - BlurCorrection) + "," + (y - BlurCorrection)); - } - - public void QuadraticCurveTo(float cpx, float cpy, float x, float y) - { - _currentPathIsEmpty = false; - _currentPath.Append(" Q" + cpx + "," + cpy + "," + x + "," + y); - } - - public void BezierCurveTo(float cp1x, float cp1y, float cp2x, float cp2y, float x, float y) - { - _currentPathIsEmpty = false; - _currentPath.Append(" C" + cp1x + "," + cp1y + "," + cp2x + "," + cp2y + "," + x + "," + y); - } - - public void FillCircle(float x, float y, float radius) - { - _currentPathIsEmpty = false; - // - // M0,250 A1,1 0 0,0 500,250 A1,1 0 0,0 0,250 z - _currentPath.Append(" M" + (x - radius) + "," + y + " A1,1 0 0,0 " + (x + radius) + "," + y + " A1,1 0 0,0 " + (x - radius) + "," + y + " z"); - Fill(); - } - - public void Fill() - { - if (!_currentPathIsEmpty) - { - Buffer.Append(""); - - } - _currentPath = new StringBuilder(); - _currentPathIsEmpty = true; - } - - public void Stroke() - { - if (!_currentPathIsEmpty) - { - var s = ""; - Buffer.Append(s); - } - _currentPath = new StringBuilder(); - _currentPathIsEmpty = true; - } - - public void FillText(string text, float x, float y) - { - if (text == "") return; - var s = ""; - Buffer.Append(s); - } - - private string GetSvgTextAlignment() - { - switch (TextAlign) - { - case TextAlign.Left: return "start"; - case TextAlign.Center: return "middle"; - case TextAlign.Right: return "end"; - } - return ""; - } - - private string GetSvgBaseLine() - { - switch (TextBaseline) - { - case TextBaseline.Top: return "hanging"; - case TextBaseline.Middle: return "middle"; - case TextBaseline.Bottom: return "bottom"; - default: return ""; - } - } - - public float MeasureText(string text) - { - if (string.IsNullOrEmpty(text)) return 0; - var font = SupportedFonts.Arial; - if (Font.Family.Contains("Times")) - { - font = SupportedFonts.TimesNewRoman; - } - return FontSizes.MeasureString(text, font, Font.Size, Font.Style); - } - - public abstract void FillMusicFontSymbol(float x, float y, float scale, MusicFontSymbol symbol); - public abstract void FillMusicFontSymbols(float x, float y, float scale, MusicFontSymbol[] symbols); - - - public virtual object OnPreRender() - { - // nothing to do - return null; - } - - public virtual object OnRenderFinished() - { - // nothing to do - return null; - } - - public void BeginRotate(float centerX, float centerY, float angle) - { - Buffer.Append(""); - } - - public void EndRotate() - { - Buffer.Append(""); - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/BarRendererBase.cs b/Source/AlphaTab/Rendering/BarRendererBase.cs deleted file mode 100644 index 8c77409d7..000000000 --- a/Source/AlphaTab/Rendering/BarRendererBase.cs +++ /dev/null @@ -1,570 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using AlphaTab.Collections; -using AlphaTab.Model; -using AlphaTab.Platform; -using AlphaTab.Platform.Model; -using AlphaTab.Rendering.Glyphs; -using AlphaTab.Rendering.Layout; -using AlphaTab.Rendering.Staves; -using AlphaTab.Rendering.Utils; -using AlphaTab.Util; -using Staff = AlphaTab.Rendering.Staves.Staff; - -namespace AlphaTab.Rendering -{ - /// - /// This is the base public class for creating blocks which can render bars. - /// - class BarRendererBase - { - private LeftToRightLayoutingGlyphGroup _preBeatGlyphs; - private FastDictionary _voiceContainers; - private LeftToRightLayoutingGlyphGroup _postBeatGlyphs; - - public BarRendererBase NextRenderer - { - get - { - if (Bar.NextBar == null) - { - return null; - } - return ScoreRenderer.Layout.GetRendererForBar(Staff.StaveId, Bar.NextBar); - } - } - - public BarRendererBase PreviousRenderer - { - get - { - if (Bar.PreviousBar == null) - { - return null; - } - return ScoreRenderer.Layout.GetRendererForBar(Staff.StaveId, Bar.PreviousBar); - } - } - - public Staff Staff { get; set; } - public float X { get; set; } - public float Y { get; set; } - public float Width { get; set; } - public float Height { get; set; } - public int Index { get; set; } - - public float TopOverflow { get; set; } - public float BottomOverflow { get; set; } - - public BarHelpers Helpers { get; set; } - public Bar Bar { get; set; } - - /// - /// Gets or sets whether this renderer is linked to the next one - /// by some glyphs like a vibrato effect - /// - public bool IsLinkedToPrevious { get; set; } - - /// - /// Gets or sets whether this renderer can wrap to the next line - /// or it needs to stay connected to the previous one. - /// (e.g. when having double bar repeats we must not separate the 2 bars) - /// - public bool CanWrap { get; set; } - - public BarRendererBase(ScoreRenderer renderer, Bar bar) - { - Bar = bar; - ScoreRenderer = renderer; - Helpers = new BarHelpers(bar); - CanWrap = true; - } - - public void RegisterOverflowTop(float topOverflow) - { - if (topOverflow > TopOverflow) - TopOverflow = topOverflow; - } - public void RegisterOverflowBottom(float bottomOverflow) - { - if (bottomOverflow > BottomOverflow) - BottomOverflow = bottomOverflow; - } - - public virtual void ScaleToWidth(float width) - { - // preBeat and postBeat glyphs do not get resized - var containerWidth = width - _preBeatGlyphs.Width - _postBeatGlyphs.Width; - - foreach (var voice in _voiceContainers) - { - var c = _voiceContainers[voice]; - c.ScaleToWidth(containerWidth); - } - - _postBeatGlyphs.X = _preBeatGlyphs.X + _preBeatGlyphs.Width + containerWidth; - - Width = width; - } - - public RenderingResources Resources - { - get - { - return ScoreRenderer.RenderingResources; - } - } - - public ScoreRenderer ScoreRenderer - { - get; - private set; - } - - public Settings Settings - { - get - { - return ScoreRenderer.Settings; - } - } - - public float Scale - { - get - { - return Settings.Scale; - } - } - - private bool _wasFirstOfLine; - public bool IsFirstOfLine - { - get - { - return Index == 0; - } - } - - public bool IsLast - { - get - { - return Bar.Index == ScoreRenderer.Layout.LastBarIndex; - } - } - - public BarLayoutingInfo LayoutingInfo { get; set; } - - public virtual void RegisterLayoutingInfo() - { - var info = LayoutingInfo; - - var preSize = _preBeatGlyphs.Width; - if (info.PreBeatSize < preSize) - { - info.PreBeatSize = preSize; - } - - foreach (var voice in _voiceContainers) - { - var c = _voiceContainers[voice]; - c.RegisterLayoutingInfo(info); - } - - var postSize = _postBeatGlyphs.Width; - if (info.PostBeatSize < postSize) - { - info.PostBeatSize = postSize; - } - } - - private int _appliedLayoutingInfo; - public virtual bool ApplyLayoutingInfo() - { - if (_appliedLayoutingInfo >= LayoutingInfo.Version) - { - return false; - } - _appliedLayoutingInfo = LayoutingInfo.Version; - // if we need additional space in the preBeat group we simply - // add a new spacer - _preBeatGlyphs.Width = LayoutingInfo.PreBeatSize; - - // on beat glyphs we apply the glyph spacing - var voiceEnd = _preBeatGlyphs.X + _preBeatGlyphs.Width; - foreach (var voice in _voiceContainers) - { - var c = _voiceContainers[voice]; - c.X = _preBeatGlyphs.X + _preBeatGlyphs.Width; - c.ApplyLayoutingInfo(LayoutingInfo); - var newEnd = c.X + c.Width; - if (voiceEnd < newEnd) - { - voiceEnd = newEnd; - } - } - - // on the post glyphs we add the spacing before all other glyphs - _postBeatGlyphs.X = (float)Math.Floor(voiceEnd); - _postBeatGlyphs.Width = LayoutingInfo.PostBeatSize; - - Width = (float)Math.Ceiling(_postBeatGlyphs.X + _postBeatGlyphs.Width); - return true; - } - - public bool IsFinalized { get; private set; } - - public virtual void FinalizeRenderer() - { - IsFinalized = true; - } - - /// - /// Gets the top padding for the main content of the renderer. - /// Can be used to specify where i.E. the score lines of the notation start. - /// - /// - public float TopPadding - { - get; set; - } - - /// - /// Gets the bottom padding for the main content of the renderer. - /// Can be used to specify where i.E. the score lines of the notation end. - /// - public float BottomPadding - { - get; set; - } - - public virtual void DoLayout() - { - _preBeatGlyphs = new LeftToRightLayoutingGlyphGroup(); - _preBeatGlyphs.Renderer = this; - _voiceContainers = new FastDictionary(); - _postBeatGlyphs = new LeftToRightLayoutingGlyphGroup(); - _postBeatGlyphs.Renderer = this; - - for (int i = 0; i < Bar.Voices.Count; i++) - { - var voice = Bar.Voices[i]; - if (HasVoiceContainer(voice)) - { - var c = new VoiceContainerGlyph(0, 0, voice); - c.Renderer = this; - _voiceContainers[Bar.Voices[i].Index] = c; - } - } - - if (Bar.SimileMark == SimileMark.SecondOfDouble) - { - CanWrap = false; - } - - CreatePreBeatGlyphs(); - CreateBeatGlyphs(); - CreatePostBeatGlyphs(); - - UpdateSizes(); - } - - protected virtual bool HasVoiceContainer(Voice voice) - { - return !voice.IsEmpty || voice.Index == 0; - } - - protected virtual void UpdateSizes() - { - Staff.RegisterStaffTop(TopPadding); - Staff.RegisterStaffBottom(Height - BottomPadding); - - var voiceContainers = _voiceContainers; - var beatGlyphsStart = BeatGlyphsStart; - var postBeatStart = beatGlyphsStart; - foreach (var voice in voiceContainers) - { - var c = voiceContainers[voice]; - c.X = beatGlyphsStart; - c.DoLayout(); - var x = c.X + c.Width; - if (postBeatStart < x) - { - postBeatStart = x; - } - } - - _postBeatGlyphs.X = (float)Math.Floor(postBeatStart); - - Width = (float)Math.Ceiling(_postBeatGlyphs.X + _postBeatGlyphs.Width); - } - - protected void AddPreBeatGlyph(Glyph g) - { - _preBeatGlyphs.AddGlyph(g); - } - - protected void AddBeatGlyph(BeatContainerGlyph g) - { - g.Renderer = this; - g.PreNotes.Renderer = this; - g.OnNotes.Renderer = this; - g.OnNotes.BeamingHelper = Helpers.BeamHelperLookup[g.Beat.Voice.Index][g.Beat.Index]; - GetOrCreateVoiceContainer(g.Beat.Voice).AddGlyph(g); - } - - protected VoiceContainerGlyph GetOrCreateVoiceContainer(Voice voice) - { - return _voiceContainers[voice.Index]; - } - - public BeatContainerGlyph GetBeatContainer(Beat beat) - { - return GetOrCreateVoiceContainer(beat.Voice).BeatGlyphs[beat.Index]; - } - - public BeatGlyphBase GetPreNotesGlyphForBeat(Beat beat) - { - return GetBeatContainer(beat).PreNotes; - } - - public BeatGlyphBase GetOnNotesGlyphForBeat(Beat beat) - { - return GetBeatContainer(beat).OnNotes; - } - - public virtual void Paint(float cx, float cy, ICanvas canvas) - { - PaintBackground(cx, cy, canvas); - - canvas.Color = Resources.MainGlyphColor; - - _preBeatGlyphs.Paint(cx + X, cy + Y, canvas); - - foreach (var voice in _voiceContainers) - { - var c = _voiceContainers[voice]; - canvas.Color = c.Voice.Index == 0 - ? Resources.MainGlyphColor - : Resources.SecondaryGlyphColor; - c.Paint(cx + X, cy + Y, canvas); - } - - - canvas.Color = Resources.MainGlyphColor; - _postBeatGlyphs.Paint(cx + X, cy + Y, canvas); - } - - protected virtual void PaintBackground(float cx, float cy, ICanvas canvas) - { - //canvas.Color = Color.Random(); - //canvas.FillRect(cx + X, cy + Y - TopOverflow, Width, Height + TopOverflow + BottomOverflow); - } - - public virtual void BuildBoundingsLookup(MasterBarBounds masterBarBounds, float cx, float cy) - { - var barBounds = new BarBounds(); - barBounds.Bar = Bar; - barBounds.VisualBounds = new Bounds - { - X = cx + X, - Y = cy + Y + TopPadding, - W = Width, - H = Height - TopPadding - BottomPadding - }; - barBounds.RealBounds = new Bounds - { - X = cx + X, - Y = cy + Y, - W = Width, - H = Height - }; - masterBarBounds.AddBar(barBounds); - - foreach (var voice in _voiceContainers) - { - var c = _voiceContainers[voice]; - var isEmptyBar = (Bar.IsEmpty && voice == 0); - if (!c.Voice.IsEmpty || isEmptyBar) - { - for (int i = 0, j = c.BeatGlyphs.Count; i < j; i++) - { - var bc = c.BeatGlyphs[i]; - - var beatBoundings = new BeatBounds(); - beatBoundings.Beat = bc.Beat; - beatBoundings.VisualBounds = new Bounds - { - X = cx + X + c.X + bc.X + bc.OnNotes.X, - Y = barBounds.VisualBounds.Y, - W = bc.OnNotes.Width, - H = barBounds.VisualBounds.H - }; - beatBoundings.RealBounds = new Bounds - { - X = cx + X + c.X + bc.X, - Y = barBounds.RealBounds.Y, - W = bc.Width, - H = barBounds.RealBounds.H - }; - - if (isEmptyBar) - { - beatBoundings.VisualBounds.X = cx + X; - beatBoundings.RealBounds.X = beatBoundings.VisualBounds.X; - } - - barBounds.AddBeat(beatBoundings); - } - } - } - } - - protected void AddPostBeatGlyph(Glyph g) - { - _postBeatGlyphs.AddGlyph(g); - } - - protected virtual void CreatePreBeatGlyphs() - { - _wasFirstOfLine = IsFirstOfLine; - } - - protected virtual void CreateBeatGlyphs() - { - } - - protected virtual void CreatePostBeatGlyphs() - { - } - - public float BeatGlyphsStart - { - get - { - return _preBeatGlyphs.X + _preBeatGlyphs.Width; - } - } - - public float PostBeatGlyphsStart - { - get - { - return _postBeatGlyphs.X; - } - } - - public virtual float GetNoteX(Note note, bool onEnd = true) - { - return 0; - } - - public float GetBeatX(Beat beat, BeatXPosition requestedPosition = BeatXPosition.PreNotes) - { - var container = GetBeatContainer(beat); - if (container != null) - { - switch (requestedPosition) - { - case BeatXPosition.PreNotes: - return container.VoiceContainer.X + container.X; - case BeatXPosition.OnNotes: - return container.VoiceContainer.X + container.X + container.OnNotes.X; - case BeatXPosition.MiddleNotes: - return container.VoiceContainer.X + container.X + container.OnTimeX; - case BeatXPosition.PostNotes: - return container.VoiceContainer.X + container.X + container.OnNotes.X + container.OnNotes.Width; - case BeatXPosition.EndBeat: - return container.VoiceContainer.X + container.X + container.Width; - } - } - return 0; - } - - public virtual float GetNoteY(Note note, bool aboveNote = false) - { - return 0; - } - - public void ReLayout() - { - // there are some glyphs which are shown only for renderers at the line start, so we simply recreate them - // but we only need to recreate them for the renderers that were the first of the line or are now the first of the line - if ((_wasFirstOfLine && !IsFirstOfLine) || (!_wasFirstOfLine && IsFirstOfLine)) - { - _preBeatGlyphs = new LeftToRightLayoutingGlyphGroup(); - _preBeatGlyphs.Renderer = this; - CreatePreBeatGlyphs(); - } - - UpdateSizes(); - RegisterLayoutingInfo(); - } - - protected void PaintSimileMark(float cx, float cy, ICanvas canvas) - { - switch (Bar.SimileMark) - { - case SimileMark.Simple: - canvas.FillMusicFontSymbol(cx + X + (Width - 20 * Scale) / 2, cy + Y + Height / 2, 1, - MusicFontSymbol.SimileMarkSimple); - break; - case SimileMark.SecondOfDouble: - canvas.FillMusicFontSymbol(cx + X - (28 * Scale) / 2, cy + Y + Height / 2, 1, - MusicFontSymbol.SimileMarkDouble); - break; - } - - } - } - - /// - /// Lists the different position modes for - /// - public enum BeatXPosition - { - /// - /// Gets the pre-notes position which is located before the accidentals - /// - PreNotes, - - /// - /// Gets the on-notes position which is located after the accidentals but before the note heads. - /// - OnNotes, - - /// - /// Gets the middel-notes position which is located after in the middle the note heads. - /// - MiddleNotes, - - /// - /// Get the post-notes position which is located at after the note heads. - /// - PostNotes, - - /// - /// Get the end-beat position which is located at the end of the beat. This position is almost - /// equal to the pre-notes position of the next beat. - /// - EndBeat - } -} diff --git a/Source/AlphaTab/Rendering/BarRendererFactory.cs b/Source/AlphaTab/Rendering/BarRendererFactory.cs deleted file mode 100644 index 1e8976d95..000000000 --- a/Source/AlphaTab/Rendering/BarRendererFactory.cs +++ /dev/null @@ -1,47 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Collections; -using AlphaTab.Model; - -namespace AlphaTab.Rendering -{ - /// - /// This is the base public class for creating factories providing BarRenderers - /// - abstract class BarRendererFactory - { - public bool IsInAccolade { get; set; } - public bool HideOnMultiTrack { get; set; } - public bool HideOnPercussionTrack { get; set; } - public abstract string StaffId { get; } - - protected BarRendererFactory() - { - IsInAccolade = true; - HideOnPercussionTrack = false; - HideOnMultiTrack = false; - } - - public virtual bool CanCreate(Track track, Staff staff) - { - return !HideOnPercussionTrack || staff.StaffKind != StaffKind.Percussion; - } - public abstract BarRendererBase Create(ScoreRenderer renderer, Bar bar, StaveSettings staveSettings); - } -} diff --git a/Source/AlphaTab/Rendering/EffectBarGlyphSizing.cs b/Source/AlphaTab/Rendering/EffectBarGlyphSizing.cs deleted file mode 100644 index 995d3aa61..000000000 --- a/Source/AlphaTab/Rendering/EffectBarGlyphSizing.cs +++ /dev/null @@ -1,62 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Rendering -{ - /// - /// Lists all sizing types of the effect bar glyphs - /// - enum EffectBarGlyphSizing - { - /// - /// The effect glyph is placed above the pre-beat glyph which is before - /// the actual note in the area where also accidentals are renderered. - /// - SinglePreBeat, - /// - /// The effect glyph is placed above the on-beat glyph which is where - /// the actual note head glyphs are placed. - /// - SingleOnBeat, - /// - /// The effect glyph is placed above the on-beat glyph which is where - /// the actual note head glyphs are placed. The glyph will size to the end of - /// the applied beat. - /// - SingleOnBeatToEnd, - /// - /// The effect glyph is placed above the on-beat glyph and expaded to the - /// on-beat position of the next beat. - /// - GroupedBeforeBeat, - /// - /// The effect glyph is placed above the on-beat glyph and expaded to the - /// on-beat position of the next beat. - /// - GroupedOnBeat, - /// - /// The effect glyph is placed above the on-beat glyph and expaded to the - /// on-beat position of the next beat. The glyph will size to the end of - /// the applied beat. - /// - GroupedOnBeatToEnd, - /// - /// The effect glyph is placed on the whole bar covering the whole width - /// - FullBar - } -} diff --git a/Source/AlphaTab/Rendering/EffectBarRenderer.cs b/Source/AlphaTab/Rendering/EffectBarRenderer.cs deleted file mode 100644 index f6ea41f11..000000000 --- a/Source/AlphaTab/Rendering/EffectBarRenderer.cs +++ /dev/null @@ -1,544 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using AlphaTab.Collections; -using AlphaTab.Model; -using AlphaTab.Platform; -using AlphaTab.Platform.Model; -using AlphaTab.Rendering.Glyphs; - -namespace AlphaTab.Rendering -{ - class EffectBand : Glyph - { - private FastList> _uniqueEffectGlyphs; - private FastList> _effectGlyphs; - - public bool IsEmpty { get; set; } - public EffectBand PreviousBand { get; set; } - public bool IsLinkedToPrevious { get; set; } - public Beat FirstBeat { get; set; } - public Beat LastBeat { get; set; } - public float Height { get; set; } - public Voice Voice { get; set; } - - public IEffectBarRendererInfo Info { get; set; } - public EffectBandSlot Slot { get; set; } - - public EffectBand(Voice voice, IEffectBarRendererInfo info) - : base(0, 0) - { - Voice = voice; - Info = info; - _uniqueEffectGlyphs = new FastList>(); - _effectGlyphs = new FastList>(); - IsEmpty = true; - } - - public override void DoLayout() - { - base.DoLayout(); - for (int i = 0; i < Renderer.Bar.Voices.Count; i++) - { - _effectGlyphs.Add(new FastDictionary()); - _uniqueEffectGlyphs.Add(new FastList()); - } - } - - public void CreateGlyph(Beat beat) - { - if (beat.Voice != Voice) return; - - // NOTE: the track order will never change. even if the staff behind the renderer changes, the trackIndex will not. - // so it's okay to access the staff here while creating the glyphs. - if (Info.ShouldCreateGlyph(Renderer.Settings, beat) && (!Info.HideOnMultiTrack || Renderer.Staff.TrackIndex == 0)) - { - IsEmpty = false; - if (FirstBeat == null || beat.IsBefore(FirstBeat)) - { - FirstBeat = beat; - } - if (LastBeat == null || beat.IsAfter(LastBeat)) - { - LastBeat = beat; - - // for "toEnd" sizing occupy until next follow-up-beat - switch (Info.SizingMode) - { - case EffectBarGlyphSizing.SingleOnBeatToEnd: - case EffectBarGlyphSizing.GroupedOnBeatToEnd: - if (LastBeat.NextBeat != null) - { - LastBeat = LastBeat.NextBeat; - } - break; - } - } - - - var glyph = CreateOrResizeGlyph(Info.SizingMode, beat); - if (glyph.Height > Height) - { - Height = glyph.Height; - } - } - } - - private EffectGlyph CreateOrResizeGlyph(EffectBarGlyphSizing sizing, Beat b) - { - EffectGlyph g; - switch (sizing) - { - case EffectBarGlyphSizing.FullBar: - g = Info.CreateNewGlyph(Renderer, b); - g.Renderer = Renderer; - g.Beat = b; - g.DoLayout(); - _effectGlyphs[b.Voice.Index][b.Index] = g; - _uniqueEffectGlyphs[b.Voice.Index].Add(g); - return g; - case EffectBarGlyphSizing.SinglePreBeat: - case EffectBarGlyphSizing.SingleOnBeat: - case EffectBarGlyphSizing.SingleOnBeatToEnd: - g = Info.CreateNewGlyph(Renderer, b); - g.Renderer = Renderer; - g.Beat = b; - g.DoLayout(); - _effectGlyphs[b.Voice.Index][b.Index] = g; - _uniqueEffectGlyphs[b.Voice.Index].Add(g); - return g; - - case EffectBarGlyphSizing.GroupedOnBeat: - case EffectBarGlyphSizing.GroupedOnBeatToEnd: - - var singleSizing = sizing == EffectBarGlyphSizing.GroupedOnBeat - ? EffectBarGlyphSizing.SingleOnBeat - : EffectBarGlyphSizing.SingleOnBeatToEnd; - - if (b.Index > 0 || Renderer.Index > 0) - { - // check if the previous beat also had this effect - Beat prevBeat = b.PreviousBeat; - if (Info.ShouldCreateGlyph(Renderer.Settings, prevBeat)) - { - // first load the effect bar renderer and glyph - EffectGlyph prevEffect = null; - - if (b.Index > 0 && _effectGlyphs[b.Voice.Index].ContainsKey(prevBeat.Index)) - { - // load effect from previous beat in the same renderer - prevEffect = _effectGlyphs[b.Voice.Index][prevBeat.Index]; - } - else if (Renderer.Index > 0) - { - // load the effect from the previous renderer if possible. - var previousRenderer = (EffectBarRenderer)Renderer.PreviousRenderer; - var previousBand = previousRenderer.GetBand(Voice, Info.EffectId); - var voiceGlyphs = previousBand._effectGlyphs[b.Voice.Index]; - if (voiceGlyphs.ContainsKey(prevBeat.Index)) - { - prevEffect = voiceGlyphs[prevBeat.Index]; - } - } - - // if the effect cannot be expanded, create a new glyph - // in case of expansion also create a new glyph, but also link the glyphs together - // so for rendering it might be expanded. - EffectGlyph newGlyph = CreateOrResizeGlyph(singleSizing, b); - - if (prevEffect != null && Info.CanExpand(prevBeat, b)) - { - // link glyphs - prevEffect.NextGlyph = newGlyph; - newGlyph.PreviousGlyph = prevEffect; - - // mark renderers as linked for consideration when layouting the renderers (line breaking, partial breaking) - IsLinkedToPrevious = true; - } - - return newGlyph; - } - - // in case the previous beat did not have the same effect, we simply create a new glyph - return CreateOrResizeGlyph(singleSizing, b); - } - - // in case of the very first beat, we simply create the glyph. - return CreateOrResizeGlyph(singleSizing, b); - } - - return null; - } - - - public override void Paint(float cx, float cy, ICanvas canvas) - { - base.Paint(cx, cy, canvas); - - //canvas.LineWidth = 1; - //canvas.StrokeRect(cx + X, cy + Y, Renderer.Width, Slot.Shared.Height); - //canvas.LineWidth = 1.5f; - - for (int i = 0, j = _uniqueEffectGlyphs.Count; i < j; i++) - { - var v = _uniqueEffectGlyphs[i]; - - for (int k = 0, l = v.Count; k < l; k++) - { - var g = v[k]; - g.Paint(cx + X, cy + Y, canvas); - } - } - } - - public void AlignGlyphs() - { - for (int v = 0; v < _effectGlyphs.Count; v++) - { - foreach (var key in _effectGlyphs[v]) - { - AlignGlyph(Info.SizingMode, Renderer.Bar.Voices[v].Beats[key]); - } - } - } - - private void AlignGlyph(EffectBarGlyphSizing sizing, Beat beat) - { - EffectGlyph g = _effectGlyphs[beat.Voice.Index][beat.Index]; - Glyph pos; - var container = Renderer.GetBeatContainer(beat); - switch (sizing) - { - case EffectBarGlyphSizing.SinglePreBeat: - pos = container.PreNotes; - g.X = Renderer.BeatGlyphsStart + pos.X + container.X; - g.Width = pos.Width; - break; - - case EffectBarGlyphSizing.SingleOnBeat: - case EffectBarGlyphSizing.GroupedOnBeat: // grouping is achieved by linking the normaly aligned glyphs - pos = container.OnNotes; - g.X = Renderer.BeatGlyphsStart + pos.X + container.X; - g.Width = pos.Width; - break; - - case EffectBarGlyphSizing.SingleOnBeatToEnd: - case EffectBarGlyphSizing.GroupedOnBeatToEnd: // grouping is achieved by linking the normaly aligned glyphs - pos = container.OnNotes; - g.X = Renderer.BeatGlyphsStart + pos.X + container.X; - if (container.Beat.IsLastOfVoice) - { - g.Width = Renderer.Width - g.X; - } - else - { - g.Width = container.Width - container.PreNotes.Width - container.PreNotes.X; - } - break; - - case EffectBarGlyphSizing.FullBar: - g.Width = Renderer.Width; - break; - } - } - } - - class EffectBandSizingInfo - { - private readonly FastDictionary _effectSlot; - public FastList Slots { get; set; } - - public EffectBandSizingInfo() - { - Slots = new FastList(); - _effectSlot = new FastDictionary(); - } - - public EffectBandSlot GetOrCreateSlot(EffectBand band) - { - // first check preferrable slot depending on type - if (_effectSlot.ContainsKey(band.Info.EffectId)) - { - var slot = _effectSlot[band.Info.EffectId]; - if (slot.CanBeUsed(band)) - { - return slot; - } - } - - // find any slot that can be used - foreach (var slot in Slots) - { - if (slot.CanBeUsed(band)) - { - return slot; - } - } - - // create a new slot if required - var newSlot = new EffectBandSlot(); - Slots.Add(newSlot); - return newSlot; - } - - public void Register(EffectBand effectBand) - { - var freeSlot = GetOrCreateSlot(effectBand); - freeSlot.Update(effectBand); - _effectSlot[effectBand.Info.EffectId] = freeSlot; - } - } - - class EffectBandSlotShared - { - public string UniqueEffectId { get; set; } - public float Y { get; set; } - public float Height { get; set; } - public Beat FirstBeat { get; set; } - public Beat LastBeat { get; set; } - - public EffectBandSlotShared() - { - Y = 0; - Height = 0; - } - } - - class EffectBandSlot - { - public FastList Bands { get; set; } - public EffectBandSlotShared Shared { get; set; } - - public EffectBandSlot() - { - Bands = new FastList(); - Shared = new EffectBandSlotShared(); - } - - public void Update(EffectBand effectBand) - { - // lock band to particular effect if needed - if (!effectBand.Info.CanShareBand) - { - Shared.UniqueEffectId = effectBand.Info.EffectId; - } - - effectBand.Slot = this; - Bands.Add(effectBand); - - if (effectBand.Height > Shared.Height) - { - Shared.Height = effectBand.Height; - } - if (Shared.FirstBeat == null || effectBand.FirstBeat.IsBefore(Shared.FirstBeat)) - { - Shared.FirstBeat = effectBand.FirstBeat; - } - if (Shared.LastBeat == null || effectBand.LastBeat.IsAfter(Shared.LastBeat)) - { - Shared.LastBeat = effectBand.LastBeat; - } - } - - public bool CanBeUsed(EffectBand band) - { - return - // if the current band is marked as unique, only merge with same effect, - // if the current band is shared, only merge with same effect - ((Shared.UniqueEffectId == null && band.Info.CanShareBand) || band.Info.EffectId == Shared.UniqueEffectId) - // only merge if there is space for the new band before or after the current beat - && (Shared.FirstBeat == null || Shared.LastBeat.IsBefore(band.FirstBeat) || Shared.LastBeat.IsBefore(Shared.FirstBeat)); - } - } - - /// - /// This renderer is responsible for displaying effects above or below the other staves - /// like the vibrato. - /// - class EffectBarRenderer : BarRendererBase - { - private readonly IEffectBarRendererInfo[] _infos; - private FastList _bands; - private FastDictionary _bandLookup; - - public EffectBandSizingInfo SizingInfo { get; set; } - - - public EffectBarRenderer(ScoreRenderer renderer, Bar bar, IEffectBarRendererInfo[] infos) - : base(renderer, bar) - { - _infos = infos; - } - - protected override void UpdateSizes() - { - TopOverflow = 0; - BottomOverflow = 0; - TopPadding = 0; - BottomPadding = 0; - - UpdateHeight(); - - base.UpdateSizes(); - } - - public override void FinalizeRenderer() - { - base.FinalizeRenderer(); - UpdateHeight(); - } - - private void UpdateHeight() - { - if (SizingInfo == null) return; - - var y = 0f; - foreach (var slot in SizingInfo.Slots) - { - slot.Shared.Y = y; - foreach (var band in slot.Bands) - { - band.Y = y; - band.Height = slot.Shared.Height; - } - - y += slot.Shared.Height; - } - - Height = y; - } - - - public override bool ApplyLayoutingInfo() - { - if (!base.ApplyLayoutingInfo()) return false; - - // we create empty slots for the same group - if (Index > 0) - { - var previousRenderer = (EffectBarRenderer)PreviousRenderer; - SizingInfo = previousRenderer.SizingInfo; - } - else - { - SizingInfo = new EffectBandSizingInfo(); - } - - foreach (var effectBand in _bands) - { - effectBand.AlignGlyphs(); - if (!effectBand.IsEmpty) - { - // find a slot that ended before the start of the band - SizingInfo.Register(effectBand); - } - } - - UpdateHeight(); - - return true; - } - - public override void ScaleToWidth(float width) - { - base.ScaleToWidth(width); - foreach (var effectBand in _bands) - { - effectBand.AlignGlyphs(); - } - } - - protected override void CreateBeatGlyphs() - { - _bands = new FastList(); - _bandLookup = new FastDictionary(); - foreach (var voice in Bar.Voices) - { - if (HasVoiceContainer(voice)) - { - foreach (var info in _infos) - { - var band = new EffectBand(voice, info); - band.Renderer = this; - band.DoLayout(); - _bands.Add(band); - _bandLookup[voice.Index + "." + info.EffectId] = band; - } - } - } - - foreach (var voice in Bar.Voices) - { - if (HasVoiceContainer(voice)) - { - CreateVoiceGlyphs(voice); - } - } - - foreach (var effectBand in _bands) - { - if (effectBand.IsLinkedToPrevious) - { - IsLinkedToPrevious = true; - } - } - } - - private void CreateVoiceGlyphs(Voice v) - { - foreach (var b in v.Beats) - { - // we create empty glyphs as alignment references and to get the - // effect bar sized - var container = new BeatContainerGlyph(b, GetOrCreateVoiceContainer(v)); - container.PreNotes = new BeatGlyphBase(); - container.OnNotes = new BeatOnNoteGlyphBase(); - AddBeatGlyph(container); - - foreach (var effectBand in _bands) - { - effectBand.CreateGlyph(b); - } - } - } - - public override void Paint(float cx, float cy, ICanvas canvas) - { - PaintBackground(cx, cy, canvas); - foreach (var effectBand in _bands) - { - canvas.Color = effectBand.Voice.Index == 0 ? Resources.MainGlyphColor : Resources.SecondaryGlyphColor; - if (!effectBand.IsEmpty) - { - effectBand.Paint(cx + X, cy + Y, canvas); - } - } - - //canvas.Color = new Color(0, 0, 200, 100); - //canvas.StrokeRect(cx + X, cy + Y, Width, Height); - } - - public EffectBand GetBand(Voice voice, string effectId) - { - var id = voice.Index + "." + effectId; - if (_bandLookup.ContainsKey(id)) return _bandLookup[id]; - return null; - } - } -} diff --git a/Source/AlphaTab/Rendering/EffectBarRendererFactory.cs b/Source/AlphaTab/Rendering/EffectBarRendererFactory.cs deleted file mode 100644 index 53c2631cd..000000000 --- a/Source/AlphaTab/Rendering/EffectBarRendererFactory.cs +++ /dev/null @@ -1,45 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Collections; -using AlphaTab.Model; - -namespace AlphaTab.Rendering -{ - class EffectBarRendererFactory : BarRendererFactory - { - private readonly IEffectBarRendererInfo[] _infos; - private readonly string _staffId; - - public override string StaffId - { - get { return _staffId; } - } - - public EffectBarRendererFactory(string staffId, IEffectBarRendererInfo[] infos) - { - _infos = infos; - _staffId = staffId; - IsInAccolade = false; - } - - public override BarRendererBase Create(ScoreRenderer renderer, Bar bar, StaveSettings staveSettings) - { - return new EffectBarRenderer(renderer, bar, _infos); - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Effects/AlternateEndingsEffectInfo.cs b/Source/AlphaTab/Rendering/Effects/AlternateEndingsEffectInfo.cs deleted file mode 100644 index 33e9819b6..000000000 --- a/Source/AlphaTab/Rendering/Effects/AlternateEndingsEffectInfo.cs +++ /dev/null @@ -1,45 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Model; -using AlphaTab.Rendering.Glyphs; - -namespace AlphaTab.Rendering.Effects -{ - class AlternateEndingsEffectInfo : IEffectBarRendererInfo - { - public string EffectId { get { return "alternate-feel"; } } - public bool HideOnMultiTrack { get { return true; } } - public bool CanShareBand { get { return false; } } - public EffectBarGlyphSizing SizingMode { get { return EffectBarGlyphSizing.FullBar; } } - - public bool ShouldCreateGlyph(Settings settings, Beat beat) - { - return beat.Index == 0 && beat.Voice.Bar.MasterBar.AlternateEndings != 0; - } - - public EffectGlyph CreateNewGlyph(BarRendererBase renderer, Beat beat) - { - return new AlternateEndingsGlyph(0,0, beat.Voice.Bar.MasterBar.AlternateEndings); - } - - public bool CanExpand(Beat from, Beat to) - { - return true; - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Effects/CapoEffectInfo.cs b/Source/AlphaTab/Rendering/Effects/CapoEffectInfo.cs deleted file mode 100644 index 2434449a0..000000000 --- a/Source/AlphaTab/Rendering/Effects/CapoEffectInfo.cs +++ /dev/null @@ -1,45 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Model; -using AlphaTab.Rendering.Glyphs; - -namespace AlphaTab.Rendering.Effects -{ - class CapoEffectInfo : IEffectBarRendererInfo - { - public string EffectId { get { return "capo"; } } - public bool HideOnMultiTrack { get { return false; } } - public bool CanShareBand { get { return false; } } - public EffectBarGlyphSizing SizingMode { get { return EffectBarGlyphSizing.SingleOnBeat; } } - - public bool ShouldCreateGlyph(Settings settings, Beat beat) - { - return beat.Index == 0 && beat.Voice.Bar.Index == 0 && beat.Voice.Bar.Staff.Capo != 0; - } - - public EffectGlyph CreateNewGlyph(BarRendererBase renderer, Beat beat) - { - return new TextGlyph(0,0, "Capo. fret " + beat.Voice.Bar.Staff.Capo, renderer.Resources.EffectFont); - } - - public bool CanExpand(Beat from, Beat to) - { - return false; - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Effects/ChordsEffectInfo.cs b/Source/AlphaTab/Rendering/Effects/ChordsEffectInfo.cs deleted file mode 100644 index d6f240607..000000000 --- a/Source/AlphaTab/Rendering/Effects/ChordsEffectInfo.cs +++ /dev/null @@ -1,46 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Model; -using AlphaTab.Platform.Model; -using AlphaTab.Rendering.Glyphs; - -namespace AlphaTab.Rendering.Effects -{ - class ChordsEffectInfo : IEffectBarRendererInfo - { - public string EffectId { get { return "chords"; } } - public bool HideOnMultiTrack { get { return false; } } - public bool CanShareBand { get { return true; } } - public EffectBarGlyphSizing SizingMode { get { return EffectBarGlyphSizing.SingleOnBeat; } } - - public bool ShouldCreateGlyph(Settings settings, Beat beat) - { - return beat.HasChord; - } - - public EffectGlyph CreateNewGlyph(BarRendererBase renderer, Beat beat) - { - return new TextGlyph(0, 0, beat.Chord.Name, renderer.Resources.EffectFont, TextAlign.Center); - } - - public bool CanExpand(Beat from, Beat to) - { - return false; - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Effects/CrescendoEffectInfo.cs b/Source/AlphaTab/Rendering/Effects/CrescendoEffectInfo.cs deleted file mode 100644 index 4fff95674..000000000 --- a/Source/AlphaTab/Rendering/Effects/CrescendoEffectInfo.cs +++ /dev/null @@ -1,46 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Model; -using AlphaTab.Rendering.Glyphs; - -namespace AlphaTab.Rendering.Effects -{ - class CrescendoEffectInfo : IEffectBarRendererInfo - { - public string EffectId { get { return "crescendo"; } } - public bool HideOnMultiTrack { get { return false; } } - public bool CanShareBand { get { return true; } } - public EffectBarGlyphSizing SizingMode { get { return EffectBarGlyphSizing.GroupedOnBeatToEnd; } } - - public bool ShouldCreateGlyph(Settings settings, Beat beat) - { - return beat.Crescendo != CrescendoType.None; - } - - - public EffectGlyph CreateNewGlyph(BarRendererBase renderer, Beat beat) - { - return new CrescendoGlyph(0, 0, beat.Crescendo); - } - - public bool CanExpand(Beat @from, Beat to) - { - return from.Crescendo == to.Crescendo; - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Effects/DummyEffectGlyph.cs b/Source/AlphaTab/Rendering/Effects/DummyEffectGlyph.cs deleted file mode 100644 index 301485151..000000000 --- a/Source/AlphaTab/Rendering/Effects/DummyEffectGlyph.cs +++ /dev/null @@ -1,46 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Platform; -using AlphaTab.Rendering.Glyphs; - -namespace AlphaTab.Rendering.Effects -{ - class DummyEffectGlyph : EffectGlyph - { - private readonly string _s; - - public DummyEffectGlyph(float x, float y, string s) - : base(x, y) - { - _s = s; - } - - public override void DoLayout() - { - Width = 20 * Scale; - } - - public override void Paint(float cx, float cy, ICanvas canvas) - { - var res = Renderer.Resources; - canvas.StrokeRect(cx + X, cy + Y, Width, 20 * Scale); - canvas.Font = res.TablatureFont; - canvas.FillText(_s, cx + X, cy + Y); - } - } -} diff --git a/Source/AlphaTab/Rendering/Effects/DynamicsEffectInfo.cs b/Source/AlphaTab/Rendering/Effects/DynamicsEffectInfo.cs deleted file mode 100644 index 873e56298..000000000 --- a/Source/AlphaTab/Rendering/Effects/DynamicsEffectInfo.cs +++ /dev/null @@ -1,73 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Model; -using AlphaTab.Rendering.Glyphs; - -namespace AlphaTab.Rendering.Effects -{ - class DynamicsEffectInfo : IEffectBarRendererInfo - { - public string EffectId { get { return "dynamics"; } } - public bool HideOnMultiTrack { get { return false; } } - public bool CanShareBand { get { return false; } } - public EffectBarGlyphSizing SizingMode { get { return EffectBarGlyphSizing.SingleOnBeat; } } - - public bool ShouldCreateGlyph(Settings settings, Beat beat) - { - return InternalShouldCreateGlyph(settings, beat, true); - } - - private bool InternalShouldCreateGlyph(Settings settings, Beat beat, bool checkForDuplicates) - { - if (beat.Voice.Bar.Staff.Track.Score.Stylesheet.HideDynamics || beat.IsEmpty || beat.Voice.IsEmpty) - { - return false; - } - - var show = ((beat.Voice.Index == 0 && beat.Index == 0 && beat.Voice.Bar.Index == 0) || (beat.PreviousBeat != null && beat.Dynamic != beat.PreviousBeat.Dynamic)); - - // ensure we do not show duplicate dynamics - if (show && beat.Voice.Index > 0) - { - foreach (var voice in beat.Voice.Bar.Voices) - { - if (voice.Index < beat.Voice.Index) - { - var beatAtSamePos = voice.GetBeatAtDisplayStart(beat.DisplayStart); - if (beatAtSamePos != null && beat.Dynamic == beatAtSamePos.Dynamic && InternalShouldCreateGlyph(settings, beatAtSamePos, false)) - { - show = false; - } - } - } - } - - return show; - } - - public EffectGlyph CreateNewGlyph(BarRendererBase renderer, Beat beat) - { - return new DynamicsGlyph(0, 0, beat.Dynamic); - } - - public bool CanExpand(Beat @from, Beat to) - { - return true; - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Effects/FadeInEffectInfo.cs b/Source/AlphaTab/Rendering/Effects/FadeInEffectInfo.cs deleted file mode 100644 index 15dc98c5a..000000000 --- a/Source/AlphaTab/Rendering/Effects/FadeInEffectInfo.cs +++ /dev/null @@ -1,45 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Model; -using AlphaTab.Rendering.Glyphs; - -namespace AlphaTab.Rendering.Effects -{ - class FadeInEffectInfo : IEffectBarRendererInfo - { - public string EffectId { get { return "fade-in"; } } - public bool HideOnMultiTrack { get { return false; } } - public bool CanShareBand { get { return true; } } - public EffectBarGlyphSizing SizingMode { get { return EffectBarGlyphSizing.SingleOnBeat; } } - - public bool ShouldCreateGlyph(Settings settings, Beat beat) - { - return beat.FadeIn; - } - - public EffectGlyph CreateNewGlyph(BarRendererBase renderer, Beat beat) - { - return new FadeInGlyph(0, 0); - } - - public bool CanExpand(Beat from, Beat to) - { - return true; - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Effects/FermataEffectInfo.cs b/Source/AlphaTab/Rendering/Effects/FermataEffectInfo.cs deleted file mode 100644 index 0c993712b..000000000 --- a/Source/AlphaTab/Rendering/Effects/FermataEffectInfo.cs +++ /dev/null @@ -1,28 +0,0 @@ -using AlphaTab.Model; -using AlphaTab.Rendering.Glyphs; - -namespace AlphaTab.Rendering.Effects -{ - class FermataEffectInfo : IEffectBarRendererInfo - { - public string EffectId { get { return "fermata"; } } - public bool HideOnMultiTrack { get { return false; } } - public bool CanShareBand { get { return false; } } - public EffectBarGlyphSizing SizingMode { get { return EffectBarGlyphSizing.SingleOnBeat; } } - - public bool ShouldCreateGlyph(Settings settings, Beat beat) - { - return beat.Fermata != null; - } - - public EffectGlyph CreateNewGlyph(BarRendererBase renderer, Beat beat) - { - return new FermataGlyph(0, 0, beat.Fermata.Type); - } - - public bool CanExpand(Beat from, Beat to) - { - return true; - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Effects/FingeringEffectInfo.cs b/Source/AlphaTab/Rendering/Effects/FingeringEffectInfo.cs deleted file mode 100644 index f94884d14..000000000 --- a/Source/AlphaTab/Rendering/Effects/FingeringEffectInfo.cs +++ /dev/null @@ -1,45 +0,0 @@ -using AlphaTab.Model; -using AlphaTab.Platform.Model; -using AlphaTab.Rendering.Glyphs; - -namespace AlphaTab.Rendering.Effects -{ - class FingeringEffectInfo : IEffectBarRendererInfo - { - public string EffectId { get { return "fingering"; } } - public bool HideOnMultiTrack { get { return false; } } - public bool CanShareBand { get { return true; } } - public EffectBarGlyphSizing SizingMode { get { return EffectBarGlyphSizing.SingleOnBeat; } } - - public bool ShouldCreateGlyph(Settings settings, Beat beat) - { - if (beat.Voice.Index != 0 || beat.IsRest || settings.FingeringMode != FingeringMode.SingleNoteEffectBand) return false; - if (beat.Notes.Count != 1) return false; - return beat.Notes[0].IsFingering; - } - - public EffectGlyph CreateNewGlyph(BarRendererBase renderer, Beat beat) - { - var finger = Fingers.Unknown; - var isLeft = false; - var note = beat.Notes[0]; - if (note.LeftHandFinger != Fingers.Unknown) - { - finger = note.LeftHandFinger; - isLeft = true; - } - else if (note.RightHandFinger != Fingers.Unknown) - { - finger = note.RightHandFinger; - } - - var s = ModelUtils.FingerToString(renderer.Settings, beat, finger, isLeft); - return new TextGlyph(0, 0, s, renderer.Resources.FingeringFont, TextAlign.Left); - } - - public bool CanExpand(Beat from, Beat to) - { - return true; - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Effects/HarmonicsEffectInfo.cs b/Source/AlphaTab/Rendering/Effects/HarmonicsEffectInfo.cs deleted file mode 100644 index 94a67bb9b..000000000 --- a/Source/AlphaTab/Rendering/Effects/HarmonicsEffectInfo.cs +++ /dev/null @@ -1,98 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Model; -using AlphaTab.Rendering.Glyphs; - -namespace AlphaTab.Rendering.Effects -{ - class HarmonicsEffectInfo : NoteEffectInfoBase - { - private readonly HarmonicType _harmonicType; - private Beat _beat; - private string _effectId; - - public override string EffectId { get { return _effectId; } } - - public HarmonicsEffectInfo(HarmonicType harmonicType) - { - _harmonicType = harmonicType; - switch (harmonicType) - { - case HarmonicType.Natural: - _effectId = "harmonics-natural"; - break; - case HarmonicType.Artificial: - _effectId = "harmonics-artificial"; - break; - case HarmonicType.Pinch: - _effectId = "harmonics-pinch"; - break; - case HarmonicType.Tap: - _effectId = "harmonics-tap"; - break; - case HarmonicType.Semi: - _effectId = "harmonics-semi"; - break; - case HarmonicType.Feedback: - _effectId = "harmonics-feedback"; - break; - } - } - - - protected override bool ShouldCreateGlyphForNote(Note note) - { - if (!note.IsHarmonic || note.HarmonicType != _harmonicType) return false; - if (note.Beat != _beat) - { - _beat = note.Beat; - } - return true; - } - - public override EffectBarGlyphSizing SizingMode - { - get { return EffectBarGlyphSizing.GroupedOnBeat; } - } - - public override EffectGlyph CreateNewGlyph(BarRendererBase renderer, Beat beat) - { - return new LineRangedGlyph(HarmonicToString(_harmonicType)); - } - - public static string HarmonicToString(HarmonicType type) - { - switch (type) - { - case HarmonicType.Natural: - return "N.H."; - case HarmonicType.Artificial: - return "A.H."; - case HarmonicType.Pinch: - return "P.H."; - case HarmonicType.Tap: - return "T.H."; - case HarmonicType.Semi: - return "S.H."; - case HarmonicType.Feedback: - return "Fdbk."; - } - return ""; - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Effects/LetRingEffectInfo.cs b/Source/AlphaTab/Rendering/Effects/LetRingEffectInfo.cs deleted file mode 100644 index f5605583b..000000000 --- a/Source/AlphaTab/Rendering/Effects/LetRingEffectInfo.cs +++ /dev/null @@ -1,46 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Model; -using AlphaTab.Rendering.Glyphs; - -namespace AlphaTab.Rendering.Effects -{ - class LetRingEffectInfo : IEffectBarRendererInfo - { - public string EffectId => "let-ring"; - public bool CanShareBand => false; - public virtual bool HideOnMultiTrack => false; - - public bool ShouldCreateGlyph(Settings settings, Beat beat) - { - return beat.IsLetRing; - } - - public EffectBarGlyphSizing SizingMode => EffectBarGlyphSizing.GroupedOnBeat; - - public EffectGlyph CreateNewGlyph(BarRendererBase renderer, Beat beat) - { - return new LineRangedGlyph("LetRing"); - } - - public virtual bool CanExpand(Beat from, Beat to) - { - return true; - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Effects/LyricsEffectInfo.cs b/Source/AlphaTab/Rendering/Effects/LyricsEffectInfo.cs deleted file mode 100644 index d4d7dc4b2..000000000 --- a/Source/AlphaTab/Rendering/Effects/LyricsEffectInfo.cs +++ /dev/null @@ -1,46 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Model; -using AlphaTab.Platform; -using AlphaTab.Rendering.Glyphs; - -namespace AlphaTab.Rendering.Effects -{ - class LyricsEffectInfo : IEffectBarRendererInfo - { - public string EffectId { get { return "lyrics"; } } - public bool HideOnMultiTrack { get { return false; } } - public bool CanShareBand { get { return false; } } - public EffectBarGlyphSizing SizingMode { get { return EffectBarGlyphSizing.SingleOnBeat; } } - - public bool ShouldCreateGlyph(Settings settings, Beat beat) - { - return beat.Lyrics != null; - } - - public EffectGlyph CreateNewGlyph(BarRendererBase renderer, Beat beat) - { - return new LyricsGlyph(0, 0, beat.Lyrics, renderer.Resources.EffectFont); - } - - public bool CanExpand(Beat from, Beat to) - { - return true; - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Effects/MarkerEffectInfo.cs b/Source/AlphaTab/Rendering/Effects/MarkerEffectInfo.cs deleted file mode 100644 index 34633a554..000000000 --- a/Source/AlphaTab/Rendering/Effects/MarkerEffectInfo.cs +++ /dev/null @@ -1,45 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Model; -using AlphaTab.Rendering.Glyphs; - -namespace AlphaTab.Rendering.Effects -{ - class MarkerEffectInfo : IEffectBarRendererInfo - { - public string EffectId { get { return "marker"; } } - public bool HideOnMultiTrack { get { return true; } } - public bool CanShareBand { get { return true; } } - public EffectBarGlyphSizing SizingMode { get { return EffectBarGlyphSizing.SinglePreBeat; } } - - public bool ShouldCreateGlyph(Settings settings, Beat beat) - { - return beat.Voice.Bar.Staff.Index == 0 && beat.Voice.Index == 0 && beat.Index == 0 && beat.Voice.Bar.MasterBar.IsSectionStart; - } - - public EffectGlyph CreateNewGlyph(BarRendererBase renderer, Beat beat) - { - return new TextGlyph(0, 0, beat.Voice.Bar.MasterBar.Section.Text, renderer.Resources.MarkerFont); - } - - public bool CanExpand(Beat @from, Beat to) - { - return true; - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Effects/NoteEffectInfoBase.cs b/Source/AlphaTab/Rendering/Effects/NoteEffectInfoBase.cs deleted file mode 100644 index 8e316df51..000000000 --- a/Source/AlphaTab/Rendering/Effects/NoteEffectInfoBase.cs +++ /dev/null @@ -1,55 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Collections; -using AlphaTab.Model; -using AlphaTab.Rendering.Glyphs; - -namespace AlphaTab.Rendering.Effects -{ - abstract class NoteEffectInfoBase : IEffectBarRendererInfo - { - protected FastList LastCreateInfo; - - public virtual bool ShouldCreateGlyph(Settings settings, Beat beat) - { - LastCreateInfo = new FastList(); - for (int i = 0, j = beat.Notes.Count; i < j; i++) - { - var n = beat.Notes[i]; - if (ShouldCreateGlyphForNote(n)) - { - LastCreateInfo.Add(n); - } - } - return LastCreateInfo.Count > 0; - } - - protected abstract bool ShouldCreateGlyphForNote(Note note); - - public abstract string EffectId { get; } - public virtual bool HideOnMultiTrack { get { return false; } } - public virtual bool CanShareBand { get { return true; } } - public abstract EffectBarGlyphSizing SizingMode { get; } - public abstract EffectGlyph CreateNewGlyph(BarRendererBase renderer, Beat beat); - - public virtual bool CanExpand(Beat @from, Beat to) - { - return true; - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Effects/OttaviaEffectInfo.cs b/Source/AlphaTab/Rendering/Effects/OttaviaEffectInfo.cs deleted file mode 100644 index 4166895ac..000000000 --- a/Source/AlphaTab/Rendering/Effects/OttaviaEffectInfo.cs +++ /dev/null @@ -1,64 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using AlphaTab.Model; -using AlphaTab.Rendering.Glyphs; - -namespace AlphaTab.Rendering.Effects -{ - class OttaviaEffectInfo : IEffectBarRendererInfo - { - private readonly bool _aboveStaff; - public string EffectId => "ottavia-" + (_aboveStaff ? "above" : "below"); - public bool HideOnMultiTrack => false; - public bool CanShareBand => true; - public EffectBarGlyphSizing SizingMode => EffectBarGlyphSizing.GroupedOnBeat; - - public OttaviaEffectInfo(bool aboveStaff) - { - _aboveStaff = aboveStaff; - } - - public bool ShouldCreateGlyph(Settings settings, Beat beat) - { - switch (beat.Ottava) - { - case Ottavia._15ma: - return _aboveStaff; - case Ottavia._8va: - return _aboveStaff; - case Ottavia._8vb: - return !_aboveStaff; - case Ottavia._15mb: - return !_aboveStaff; - } - return false; - } - - public EffectGlyph CreateNewGlyph(BarRendererBase renderer, Beat beat) - { - return new OttavaGlyph(beat.Ottava, _aboveStaff); - } - - public bool CanExpand(Beat from, Beat to) - { - return from.Ottava == to.Ottava; - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Effects/PalmMuteEffectInfo.cs b/Source/AlphaTab/Rendering/Effects/PalmMuteEffectInfo.cs deleted file mode 100644 index 2b38dd2c7..000000000 --- a/Source/AlphaTab/Rendering/Effects/PalmMuteEffectInfo.cs +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Model; -using AlphaTab.Rendering.Glyphs; - -namespace AlphaTab.Rendering.Effects -{ - class PalmMuteEffectInfo : NoteEffectInfoBase - { - public override string EffectId { get { return "palm-mute"; } } - - protected override bool ShouldCreateGlyphForNote(Note note) - { - return note.IsPalmMute; - } - - public override EffectBarGlyphSizing SizingMode - { - get { return EffectBarGlyphSizing.GroupedOnBeat; } - } - - public override EffectGlyph CreateNewGlyph(BarRendererBase renderer, Beat beat) - { - return new LineRangedGlyph("P.M."); - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Effects/PickSlideEffectInfo.cs b/Source/AlphaTab/Rendering/Effects/PickSlideEffectInfo.cs deleted file mode 100644 index 83d4abd9e..000000000 --- a/Source/AlphaTab/Rendering/Effects/PickSlideEffectInfo.cs +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Model; -using AlphaTab.Rendering.Glyphs; - -namespace AlphaTab.Rendering.Effects -{ - class PickSlideEffectInfo : NoteEffectInfoBase - { - public override string EffectId { get { return "pick-slide"; } } - - protected override bool ShouldCreateGlyphForNote(Note note) - { - return note.SlideType == SlideType.PickSlideDown || note.SlideType == SlideType.PickSlideUp; - } - - public override EffectBarGlyphSizing SizingMode - { - get { return EffectBarGlyphSizing.GroupedOnBeat; } - } - - public override EffectGlyph CreateNewGlyph(BarRendererBase renderer, Beat beat) - { - return new LineRangedGlyph("P.S."); - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Effects/PickStrokeEffectInfo.cs b/Source/AlphaTab/Rendering/Effects/PickStrokeEffectInfo.cs deleted file mode 100644 index ae467628a..000000000 --- a/Source/AlphaTab/Rendering/Effects/PickStrokeEffectInfo.cs +++ /dev/null @@ -1,45 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Model; -using AlphaTab.Rendering.Glyphs; - -namespace AlphaTab.Rendering.Effects -{ - class PickStrokeEffectInfo : IEffectBarRendererInfo - { - public string EffectId { get { return "pick-stroke"; } } - public bool HideOnMultiTrack { get { return false; } } - public bool CanShareBand { get { return true; } } - public EffectBarGlyphSizing SizingMode { get { return EffectBarGlyphSizing.SingleOnBeat; } } - - public bool ShouldCreateGlyph(Settings settings, Beat beat) - { - return beat.PickStroke != PickStroke.None; - } - - public EffectGlyph CreateNewGlyph(BarRendererBase renderer, Beat beat) - { - return new PickStrokeGlyph(0, 0, beat.PickStroke); - } - - public bool CanExpand(Beat @from, Beat to) - { - return true; - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Effects/SlightBeatVibratoEffectInfo.cs b/Source/AlphaTab/Rendering/Effects/SlightBeatVibratoEffectInfo.cs deleted file mode 100644 index f73437c28..000000000 --- a/Source/AlphaTab/Rendering/Effects/SlightBeatVibratoEffectInfo.cs +++ /dev/null @@ -1,45 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Model; -using AlphaTab.Rendering.Glyphs; - -namespace AlphaTab.Rendering.Effects -{ - class SlightBeatVibratoEffectInfo : IEffectBarRendererInfo - { - public string EffectId { get { return "slight-beat-vibrato"; } } - public bool HideOnMultiTrack { get { return false; } } - public bool CanShareBand { get { return true; } } - public EffectBarGlyphSizing SizingMode { get { return EffectBarGlyphSizing.GroupedOnBeatToEnd; } } - - public bool ShouldCreateGlyph(Settings settings, Beat beat) - { - return beat.Vibrato == VibratoType.Slight; - } - - public EffectGlyph CreateNewGlyph(BarRendererBase renderer, Beat beat) - { - return new BeatVibratoGlyph(VibratoType.Slight); - } - - public bool CanExpand(Beat from, Beat to) - { - return true; - } - } -} diff --git a/Source/AlphaTab/Rendering/Effects/SlightNoteVibratoEffectInfo.cs b/Source/AlphaTab/Rendering/Effects/SlightNoteVibratoEffectInfo.cs deleted file mode 100644 index 9ae443b62..000000000 --- a/Source/AlphaTab/Rendering/Effects/SlightNoteVibratoEffectInfo.cs +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Model; -using AlphaTab.Rendering.Glyphs; - -namespace AlphaTab.Rendering.Effects -{ - class SlightNoteVibratoEffectInfo : NoteEffectInfoBase - { - public override string EffectId { get { return "slight-note-vibrato"; } } - - protected override bool ShouldCreateGlyphForNote(Note note) - { - return note.Vibrato == VibratoType.Slight || (note.IsTieDestination && note.TieOrigin.Vibrato == VibratoType.Slight); - } - - public override EffectBarGlyphSizing SizingMode - { - get { return EffectBarGlyphSizing.GroupedOnBeatToEnd; } - } - - public override EffectGlyph CreateNewGlyph(BarRendererBase renderer, Beat beat) - { - return new NoteVibratoGlyph(0, 0, VibratoType.Slight); - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Effects/TapEffectInfo.cs b/Source/AlphaTab/Rendering/Effects/TapEffectInfo.cs deleted file mode 100644 index 20a227393..000000000 --- a/Source/AlphaTab/Rendering/Effects/TapEffectInfo.cs +++ /dev/null @@ -1,55 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Model; -using AlphaTab.Rendering.Glyphs; - -namespace AlphaTab.Rendering.Effects -{ - class TapEffectInfo : IEffectBarRendererInfo - { - public string EffectId { get { return "tap"; } } - public bool HideOnMultiTrack { get { return false; } } - public bool CanShareBand { get { return true; } } - public EffectBarGlyphSizing SizingMode { get { return EffectBarGlyphSizing.SingleOnBeat; } } - - public bool ShouldCreateGlyph(Settings settings, Beat beat) - { - return (beat.Slap || beat.Pop || beat.Tap); - } - - - public EffectGlyph CreateNewGlyph(BarRendererBase renderer, Beat beat) - { - var res = renderer.Resources; - if (beat.Slap) - { - return new TextGlyph(0, 0, "S", res.EffectFont); - } - if (beat.Pop) - { - return new TextGlyph(0, 0, "P", res.EffectFont); - } - return new TextGlyph(0, 0, "T", res.EffectFont); - } - - public bool CanExpand(Beat @from, Beat to) - { - return true; - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Effects/TempoEffectInfo.cs b/Source/AlphaTab/Rendering/Effects/TempoEffectInfo.cs deleted file mode 100644 index 62a35bbcd..000000000 --- a/Source/AlphaTab/Rendering/Effects/TempoEffectInfo.cs +++ /dev/null @@ -1,54 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Model; -using AlphaTab.Rendering.Glyphs; - -namespace AlphaTab.Rendering.Effects -{ - class TempoEffectInfo : IEffectBarRendererInfo - { - public string EffectId { get { return "tempo"; } } - public bool HideOnMultiTrack { get { return true; } } - public bool CanShareBand { get { return false; } } - public EffectBarGlyphSizing SizingMode { get { return EffectBarGlyphSizing.SinglePreBeat; } } - - public bool ShouldCreateGlyph(Settings settings, Beat beat) - { - return beat.Voice.Bar.Staff.Index == 0 && beat.Voice.Index == 0 && beat.Index == 0 && (beat.Voice.Bar.MasterBar.TempoAutomation != null || beat.Voice.Bar.Index == 0); - } - - public EffectGlyph CreateNewGlyph(BarRendererBase renderer, Beat beat) - { - int tempo; - if (beat.Voice.Bar.MasterBar.TempoAutomation != null) - { - tempo = (int)(beat.Voice.Bar.MasterBar.TempoAutomation.Value); - } - else - { - tempo = beat.Voice.Bar.Staff.Track.Score.Tempo; - } - return new TempoGlyph(0, 0, tempo); - } - - public bool CanExpand(Beat @from, Beat to) - { - return true; - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Effects/TextEffectInfo.cs b/Source/AlphaTab/Rendering/Effects/TextEffectInfo.cs deleted file mode 100644 index 92a08ffde..000000000 --- a/Source/AlphaTab/Rendering/Effects/TextEffectInfo.cs +++ /dev/null @@ -1,47 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Model; -using AlphaTab.Platform; -using AlphaTab.Rendering.Glyphs; - -namespace AlphaTab.Rendering.Effects -{ - class TextEffectInfo : IEffectBarRendererInfo - { - public string EffectId { get { return "text"; } } - public bool HideOnMultiTrack { get { return false; } } - public bool CanShareBand { get { return false; } } - public EffectBarGlyphSizing SizingMode { get { return EffectBarGlyphSizing.SingleOnBeat; } } - - public bool ShouldCreateGlyph(Settings settings, Beat beat) - { - return !string.IsNullOrWhiteSpace(beat.Text); - } - - - public EffectGlyph CreateNewGlyph(BarRendererBase renderer, Beat beat) - { - return new TextGlyph(0, 0, beat.Text, renderer.Resources.EffectFont); - } - - public bool CanExpand(Beat @from, Beat to) - { - return true; - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Effects/TrillEffectInfo.cs b/Source/AlphaTab/Rendering/Effects/TrillEffectInfo.cs deleted file mode 100644 index 27b399abe..000000000 --- a/Source/AlphaTab/Rendering/Effects/TrillEffectInfo.cs +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Model; -using AlphaTab.Rendering.Glyphs; - -namespace AlphaTab.Rendering.Effects -{ - class TrillEffectInfo : NoteEffectInfoBase - { - public override string EffectId { get { return "trill"; } } - - protected override bool ShouldCreateGlyphForNote(Note note) - { - return note.IsTrill; - } - - public override EffectBarGlyphSizing SizingMode - { - get { return EffectBarGlyphSizing.SingleOnBeat; } - } - - public override EffectGlyph CreateNewGlyph(BarRendererBase renderer, Beat beat) - { - return new TrillGlyph(0, 0); - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Effects/TripletFeelEffectInfo.cs b/Source/AlphaTab/Rendering/Effects/TripletFeelEffectInfo.cs deleted file mode 100644 index 3dc05f741..000000000 --- a/Source/AlphaTab/Rendering/Effects/TripletFeelEffectInfo.cs +++ /dev/null @@ -1,50 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Model; -using AlphaTab.Rendering.Glyphs; - -namespace AlphaTab.Rendering.Effects -{ - class TripletFeelEffectInfo : IEffectBarRendererInfo - { - public string EffectId { get { return "triplet-feel"; } } - public bool HideOnMultiTrack { get { return true; } } - public bool CanShareBand { get { return false; } } - public EffectBarGlyphSizing SizingMode { get { return EffectBarGlyphSizing.SinglePreBeat; } } - - public bool ShouldCreateGlyph(Settings settings, Beat beat) - { - return beat.Index == 0 && - ( - (beat.Voice.Bar.MasterBar.Index == 0 && beat.Voice.Bar.MasterBar.TripletFeel != TripletFeel.NoTripletFeel) - || (beat.Voice.Bar.MasterBar.Index > 0 && beat.Voice.Bar.MasterBar.TripletFeel != beat.Voice.Bar.MasterBar.PreviousMasterBar.TripletFeel) - ); - } - - - public EffectGlyph CreateNewGlyph(BarRendererBase renderer, Beat beat) - { - return new TripletFeelGlyph(beat.Voice.Bar.MasterBar.TripletFeel); - } - - public bool CanExpand(Beat @from, Beat to) - { - return true; - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Effects/WhammyBarEffectInfo.cs b/Source/AlphaTab/Rendering/Effects/WhammyBarEffectInfo.cs deleted file mode 100644 index 14380b615..000000000 --- a/Source/AlphaTab/Rendering/Effects/WhammyBarEffectInfo.cs +++ /dev/null @@ -1,45 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Model; -using AlphaTab.Rendering.Glyphs; - -namespace AlphaTab.Rendering.Effects -{ - class WhammyBarEffectInfo : IEffectBarRendererInfo - { - public string EffectId { get { return "whammy"; } } - public bool HideOnMultiTrack { get { return false; } } - public bool CanShareBand { get { return false; } } - public EffectBarGlyphSizing SizingMode { get { return EffectBarGlyphSizing.GroupedOnBeat; } } - - public bool ShouldCreateGlyph(Settings settings, Beat beat) - { - return beat.HasWhammyBar; - } - - public EffectGlyph CreateNewGlyph(BarRendererBase renderer, Beat beat) - { - return new LineRangedGlyph("w/bar"); - } - - public bool CanExpand(Beat from, Beat to) - { - return true; - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Effects/WideBeatVibratoEffectInfo.cs b/Source/AlphaTab/Rendering/Effects/WideBeatVibratoEffectInfo.cs deleted file mode 100644 index cb36e67fe..000000000 --- a/Source/AlphaTab/Rendering/Effects/WideBeatVibratoEffectInfo.cs +++ /dev/null @@ -1,45 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Model; -using AlphaTab.Rendering.Glyphs; - -namespace AlphaTab.Rendering.Effects -{ - class WideBeatVibratoEffectInfo : IEffectBarRendererInfo - { - public string EffectId { get { return "wide-beat-vibrato"; } } - public bool HideOnMultiTrack { get { return false; } } - public bool CanShareBand { get { return true; } } - public EffectBarGlyphSizing SizingMode { get { return EffectBarGlyphSizing.GroupedOnBeatToEnd; } } - - public bool ShouldCreateGlyph(Settings settings, Beat beat) - { - return beat.Vibrato == VibratoType.Wide; - } - - public EffectGlyph CreateNewGlyph(BarRendererBase renderer, Beat beat) - { - return new BeatVibratoGlyph(VibratoType.Wide); - } - - public bool CanExpand(Beat from, Beat to) - { - return true; - } - } -} diff --git a/Source/AlphaTab/Rendering/Effects/WideNoteVibratoEffectInfo.cs b/Source/AlphaTab/Rendering/Effects/WideNoteVibratoEffectInfo.cs deleted file mode 100644 index 400f8b701..000000000 --- a/Source/AlphaTab/Rendering/Effects/WideNoteVibratoEffectInfo.cs +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Model; -using AlphaTab.Rendering.Glyphs; - -namespace AlphaTab.Rendering.Effects -{ - class WideNoteVibratoEffectInfo : NoteEffectInfoBase - { - public override string EffectId { get { return "wide-note-vibrato"; } } - - protected override bool ShouldCreateGlyphForNote(Note note) - { - return note.Vibrato == VibratoType.Wide || (note.IsTieDestination && note.TieOrigin.Vibrato == VibratoType.Wide); - } - - public override EffectBarGlyphSizing SizingMode - { - get { return EffectBarGlyphSizing.GroupedOnBeatToEnd; } - } - - public override EffectGlyph CreateNewGlyph(BarRendererBase renderer, Beat beat) - { - return new NoteVibratoGlyph(0, 0, VibratoType.Wide); - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Glyphs/AccentuationGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/AccentuationGlyph.cs deleted file mode 100644 index d811f167b..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/AccentuationGlyph.cs +++ /dev/null @@ -1,49 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Model; - -namespace AlphaTab.Rendering.Glyphs -{ - class AccentuationGlyph : MusicFontGlyph - { - public AccentuationGlyph(float x, float y, AccentuationType accentuation) - : base(x, y, 1, GetSymbol(accentuation)) - { - } - - private static MusicFontSymbol GetSymbol(AccentuationType accentuation) - { - switch (accentuation) - { - case AccentuationType.None: - return MusicFontSymbol.None; - case AccentuationType.Normal: - return MusicFontSymbol.Accentuation; - case AccentuationType.Heavy: - return MusicFontSymbol.HeavyAccentuation; - default: - return MusicFontSymbol.None; - } - } - - public override void DoLayout() - { - Width = 9 * Scale; - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/AccidentalGroupGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/AccidentalGroupGlyph.cs deleted file mode 100644 index 8ec2f3b09..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/AccidentalGroupGlyph.cs +++ /dev/null @@ -1,103 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Collections; -using AlphaTab.Platform; - -namespace AlphaTab.Rendering.Glyphs -{ - class AccidentalGroupGlyph : GlyphGroup - { - private const int NonReserved = -3000; - - public AccidentalGroupGlyph() - : base(0, 0) - { - - } - - public override void DoLayout() - { - if (Glyphs == null) - { - Width = 0; - return; - } - // - // Determine Columns for accidentals - // - Glyphs.Sort((a, b) => - { - if (a.Y < b.Y) return -1; - if (a.Y > b.Y) return 1; - return 0; - }); - - // defines the reserved y position of the columns - var columns = new FastList(); - columns.Add(NonReserved); - - var accidentalSize = 21 * Scale; - for (int i = 0, j = Glyphs.Count; i < j; i++) - { - var g = Glyphs[i]; - g.Renderer = Renderer; - g.DoLayout(); - - // find column where glyph fits into - - // as long the glyph does not fit into the current column - var gColumn = 0; - while (columns[gColumn] > g.Y) - { - // move to next column - gColumn++; - - // and create the new column if needed - if (gColumn == columns.Count) - { - columns.Add(NonReserved); - } - } - - // temporary save column as X - g.X = gColumn; - columns[gColumn] = g.Y + accidentalSize; - } - - // - // Place accidentals in columns - // - var columnWidth = 8 * Scale; - var padding = 2*Scale; - if (Glyphs.Count == 0) - { - Width = 0; - } - else - { - Width = padding + (columnWidth * columns.Count); - } - - for (int i = 0, j = Glyphs.Count; i < j; i++) - { - var g = Glyphs[i]; - g.X = padding + (Width - ((g.X + 1) * columnWidth)); - } - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/AlternateEndingsGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/AlternateEndingsGlyph.cs deleted file mode 100644 index 3eaa33ba8..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/AlternateEndingsGlyph.cs +++ /dev/null @@ -1,73 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Collections; -using AlphaTab.Model; -using AlphaTab.Platform; - -namespace AlphaTab.Rendering.Glyphs -{ - class AlternateEndingsGlyph : EffectGlyph - { - private const float Padding = 3; - - private readonly FastList _endings; - private string _endingsString; - - public AlternateEndingsGlyph(float x, float y, byte alternateEndings) - : base(x, y) - { - _endings = new FastList(); - for (var i = 0; i < MasterBar.MaxAlternateEndings; i++) - { - if ((alternateEndings & (0x01 << i)) != 0) - { - _endings.Add(i); - } - } - } - - public override void DoLayout() - { - base.DoLayout(); - Height = Renderer.Resources.WordsFont.Size + (Padding * Scale + 2); - var endingsStrings = new StringBuilder(); - for (int i = 0, j = _endings.Count; i < j; i++) - { - endingsStrings.Append(_endings[i] + 1); - endingsStrings.Append(". "); - } - _endingsString = endingsStrings.ToString(); - } - - public override void Paint(float cx, float cy, ICanvas canvas) - { - base.Paint(cx, cy, canvas); - if (_endings.Count > 0) - { - var res = Renderer.Resources; - canvas.Font = res.WordsFont; - canvas.MoveTo(cx + X, cy + Y + Height); - canvas.LineTo(cx + X, cy + Y); - canvas.LineTo(cx + X + Width, cy + Y); - canvas.Stroke(); - - canvas.FillText(_endingsString, cx + X + Padding * Scale, cy + Y * Scale); - } - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/BarNumberGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/BarNumberGlyph.cs deleted file mode 100644 index f2fe22eb6..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/BarNumberGlyph.cs +++ /dev/null @@ -1,53 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Platform; - -namespace AlphaTab.Rendering.Glyphs -{ - class BarNumberGlyph : Glyph - { - private readonly int _number; - - public BarNumberGlyph(float x, float y, int number) - : base(x, y) - { - _number = number; - } - - public override void DoLayout() - { - Renderer.ScoreRenderer.Canvas.Font = Renderer.Resources.BarNumberFont; - Width = Renderer.ScoreRenderer.Canvas.MeasureText(_number.ToString()) + 5 * Scale; - } - - public override void Paint(float cx, float cy, ICanvas canvas) - { - if (!Renderer.Staff.IsFirstInAccolade) - { - return; - } - var res = Renderer.Resources; - var c = canvas.Color; - canvas.Color = res.BarNumberColor; - canvas.Font = res.BarNumberFont; - - canvas.FillText(_number.ToString(), cx + X, cy + Y); - canvas.Color = c; - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/BarSeperatorGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/BarSeperatorGlyph.cs deleted file mode 100644 index c9c44bb0e..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/BarSeperatorGlyph.cs +++ /dev/null @@ -1,78 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Platform; -using AlphaTab.Platform.Model; -using AlphaTab.Rendering.Effects; - -namespace AlphaTab.Rendering.Glyphs -{ - class BarSeperatorGlyph : Glyph - { - public BarSeperatorGlyph(float x, float y) - : base(x, y) - { - } - - public override void DoLayout() - { - if (Renderer.IsLast) - { - Width = 15 * Scale; - } - else if (Renderer.NextRenderer == null || Renderer.NextRenderer.Staff != Renderer.Staff || !Renderer.NextRenderer.Bar.MasterBar.IsRepeatStart) - { - Width = 2 * Scale; - if (Renderer.Bar.MasterBar.IsDoubleBar) - { - Width += 2 * Scale; - } - } - else - { - Width = 2 * Scale; - } - } - - public override void Paint(float cx, float cy, ICanvas canvas) - { - var blockWidth = 4 * Scale; - - var top = cy + Y + Renderer.TopPadding; - var bottom = cy + Y + Renderer.Height - Renderer.BottomPadding; - var left = (int)(cx + X); - var h = bottom - top; - - if (Renderer.IsLast) - { - // small bar - canvas.FillRect(left + Width - blockWidth - blockWidth, top, Scale, h); - // big bar - canvas.FillRect(left + Width - blockWidth, top, blockWidth, h); - } - else if (Renderer.NextRenderer == null || Renderer.NextRenderer.Staff != Renderer.Staff || !Renderer.NextRenderer.Bar.MasterBar.IsRepeatStart) - { - // small bar - canvas.FillRect(left + Width - Scale, top, Scale, h); - if (Renderer.Bar.MasterBar.IsDoubleBar) - { - canvas.FillRect(left + Width - 5 * Scale, top, Scale, h); - } - } - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Glyphs/BeamGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/BeamGlyph.cs deleted file mode 100644 index 12c654808..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/BeamGlyph.cs +++ /dev/null @@ -1,72 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Model; -using AlphaTab.Rendering.Utils; - -namespace AlphaTab.Rendering.Glyphs -{ - class BeamGlyph : MusicFontGlyph - { - public BeamGlyph(float x, float y, Duration duration, BeamDirection direction, bool isGrace) - : base(x, y, isGrace ? NoteHeadGlyph.GraceScale : 1, GetSymbol(duration, direction, isGrace)) - { - } - - public override void DoLayout() - { - Width = 0; - } - - private static MusicFontSymbol GetSymbol(Duration duration, BeamDirection direction, bool isGrace) - { - if (isGrace) - { - duration = Duration.Eighth; - } - - if (direction == BeamDirection.Up) - { - switch (duration) - { - case Duration.Eighth: return MusicFontSymbol.FooterUpEighth; - case Duration.Sixteenth: return MusicFontSymbol.FooterUpSixteenth; - case Duration.ThirtySecond: return MusicFontSymbol.FooterUpThirtySecond; - case Duration.SixtyFourth: return MusicFontSymbol.FooterUpSixtyFourth; - case Duration.OneHundredTwentyEighth: return MusicFontSymbol.FooterUpOneHundredTwentyEighth; - case Duration.TwoHundredFiftySixth: return MusicFontSymbol.FooterUpTwoHundredFiftySixth; - default: return MusicFontSymbol.FooterUpEighth; - } - } - else - { - switch (duration) - { - case Duration.Eighth: return MusicFontSymbol.FooterDownEighth; - case Duration.Sixteenth: return MusicFontSymbol.FooterDownSixteenth; - case Duration.ThirtySecond: return MusicFontSymbol.FooterDownThirtySecond; - case Duration.SixtyFourth: return MusicFontSymbol.FooterDownSixtyFourth; - case Duration.OneHundredTwentyEighth: return MusicFontSymbol.FooterDownOneHundredTwentyEighth; - case Duration.TwoHundredFiftySixth: return MusicFontSymbol.FooterDownOneHundredTwentyEighth; - default: return MusicFontSymbol.FooterDownEighth; - } - } - - } - - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/BeatContainerGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/BeatContainerGlyph.cs deleted file mode 100644 index 08db44774..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/BeatContainerGlyph.cs +++ /dev/null @@ -1,223 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Collections; -using AlphaTab.Model; -using AlphaTab.Platform; -using AlphaTab.Platform.Model; -using AlphaTab.Rendering.Staves; - -namespace AlphaTab.Rendering.Glyphs -{ - class BeatContainerGlyph : Glyph - { - public VoiceContainerGlyph VoiceContainer { get; set; } - - public Beat Beat { get; set; } - public BeatGlyphBase PreNotes { get; set; } - public BeatOnNoteGlyphBase OnNotes { get; set; } - public FastList Ties { get; set; } - - public float MinWidth { get; set; } - public float OnTimeX => OnNotes.X + OnNotes.CenterX; - - - public BeatContainerGlyph(Beat beat, VoiceContainerGlyph voiceContainer) - : base(0, 0) - { - Beat = beat; - Ties = new FastList(); - VoiceContainer = voiceContainer; - } - - public virtual void RegisterLayoutingInfo(BarLayoutingInfo layoutings) - { - var preBeatStretch = OnTimeX; - var postBeatStretch = 0f; - foreach (var tie in Ties) - { - if (tie.Width > postBeatStretch) - { - postBeatStretch = tie.Width; - } - } - postBeatStretch += OnNotes.X + (OnNotes.Width - OnNotes.CenterX); - - layoutings.AddBeatSpring(Beat, preBeatStretch, postBeatStretch); - // store sizes for special renderers like the EffectBarRenderer - layoutings.SetPreBeatSize(Beat, PreNotes.Width); - layoutings.SetOnBeatSize(Beat, OnNotes.Width); - layoutings.SetBeatCenterX(Beat, OnNotes.CenterX); - } - - public virtual void ApplyLayoutingInfo(BarLayoutingInfo info) - { - var offset = info.GetBeatCenterX(Beat) - OnNotes.CenterX; - PreNotes.X = offset; - PreNotes.Width = info.GetPreBeatSize(Beat); - OnNotes.Width = info.GetOnBeatSize(Beat); - OnNotes.X = PreNotes.X + PreNotes.Width; - OnNotes.UpdateBeamingHelper(); - } - - public override void DoLayout() - { - PreNotes.X = 0; - PreNotes.Renderer = Renderer; - PreNotes.Container = this; - PreNotes.DoLayout(); - - OnNotes.X = PreNotes.X + PreNotes.Width; - OnNotes.Renderer = Renderer; - OnNotes.Container = this; - OnNotes.DoLayout(); - var i = Beat.Notes.Count - 1; - while (i >= 0) - { - CreateTies(Beat.Notes[i--]); - } - - UpdateWidth(); - } - - protected virtual void UpdateWidth() - { - MinWidth = PreNotes.Width + OnNotes.Width; - if (!Beat.IsRest) - { - if (OnNotes.BeamingHelper.Beats.Count == 1) - { - // make space for footer - if (Beat.Duration >= Duration.Eighth) - { - MinWidth += 20 * Scale; - } - } - else - { - // ensure some space for small notes - switch (Beat.Duration) - { - case Duration.OneHundredTwentyEighth: - case Duration.TwoHundredFiftySixth: - MinWidth += 10 * Scale; - break; - } - } - } - - var tieWidth = 0f; - foreach (var tie in Ties) - { - if (tie.Width > tieWidth) - { - tieWidth = tie.Width; - } - } - MinWidth += tieWidth; - - - Width = MinWidth; - } - - public virtual void ScaleToWidth(float beatWidth) - { - foreach (var tie in Ties) - { - tie.DoLayout(); - } - OnNotes.UpdateBeamingHelper(); - Width = beatWidth; - } - - protected virtual void CreateTies(Note n) - { - } - - public static string GetGroupId(Beat beat) - { - return "b" + beat.Id; - } - - public override void Paint(float cx, float cy, ICanvas canvas) - { - if (Beat.Voice.IsEmpty) return; - - var isEmptyGlyph = PreNotes.IsEmpty && OnNotes.IsEmpty && Ties.Count == 0; - if (isEmptyGlyph) return; - - canvas.BeginGroup(GetGroupId(Beat)); - - //var c = canvas.Color; - //var ta = canvas.TextAlign; - //canvas.Color = new Color(255, 0, 0); - //canvas.TextAlign = TextAlign.Left; - //canvas.FillText(Beat.DisplayStart.ToString(), cx + X, cy + Y - 10); - //canvas.Color = c; - //canvas.TextAlign = ta; - - //canvas.Color = Color.Random(); - //canvas.FillRect(cx + X, cy + Y, Width, Renderer.Height); - - //var oldColor = canvas.Color; - //canvas.Color = new Color((byte)Platform.Platform.Random(255), (byte)Platform.Platform.Random(255), (byte)Platform.Platform.Random(255), 100); - //canvas.FillRect(cx + X, cy + Y, Width, Renderer.Height); - //canvas.Color = oldColor; - - //canvas.Color = new Color(200, 0, 0, 100); - //canvas.StrokeRect(cx + X, cy + Y + 15 * Beat.Voice.Index, Width, 10); - //canvas.Font = new Font("Arial", 10); - //canvas.Color = new Color(0, 0, 0); - //canvas.FillText(Beat.Voice.Index + ":" + Beat.Index, cx + X, cy + Y + 15 * Beat.Voice.Index); - - //if (Beat.Voice.Index == 0) - //{ - // canvas.Color = new Color(200, 0, 0, 100); - // canvas.StrokeRect(cx + X, cy + Y + PreNotes.Y + 30, Width, 10); - //} - - PreNotes.Paint(cx + X, cy + Y, canvas); - //if (Beat.Voice.Index == 0) - //{ - // canvas.Color = new Color(200, 0, 0, 100); - // canvas.StrokeRect(cx + X + PreNotes.X, cy + Y + PreNotes.Y, PreNotes.Width, 10); - //} - OnNotes.Paint(cx + X, cy + Y, canvas); - //if (Beat.Voice.Index == 0) - //{ - // canvas.Color = new Color(0, 200, 0, 100); - // canvas.StrokeRect(cx + X + OnNotes.X, cy + Y + OnNotes.Y + 10, OnNotes.Width, 10); - //} - - // paint the ties relative to the whole staff, - // reason: we have possibly multiple staves involved and need to calculate the correct positions. - var staffX = cx - VoiceContainer.X - Renderer.X; - var staffY = cy - VoiceContainer.Y - Renderer.Y; - - for (int i = 0, j = Ties.Count; i < j; i++) - { - var t = Ties[i]; - t.Renderer = Renderer; - t.Paint(staffX, staffY, canvas); - } - - canvas.EndGroup(); - } - - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/BeatGlyphBase.cs b/Source/AlphaTab/Rendering/Glyphs/BeatGlyphBase.cs deleted file mode 100644 index 775db561c..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/BeatGlyphBase.cs +++ /dev/null @@ -1,58 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.Model; - -namespace AlphaTab.Rendering.Glyphs -{ - class BeatGlyphBase : GlyphGroup - { - public BeatContainerGlyph Container { get; set; } - - public BeatGlyphBase() - : base(0, 0) - { - } - - public override void DoLayout() - { - // left to right layout - var w = 0f; - if (Glyphs != null) - { - for (int i = 0, j = Glyphs.Count; i < j; i++) - { - var g = Glyphs[i]; - g.X = w; - g.Renderer = Renderer; - g.DoLayout(); - w += g.Width; - } - } - Width = w; - } - - protected void NoteLoop(Action action) - { - for (int i = Container.Beat.Notes.Count - 1; i >= 0; i--) - { - action(Container.Beat.Notes[i]); - } - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/BeatOnNoteGlyphBase.cs b/Source/AlphaTab/Rendering/Glyphs/BeatOnNoteGlyphBase.cs deleted file mode 100644 index 866498668..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/BeatOnNoteGlyphBase.cs +++ /dev/null @@ -1,46 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Platform; -using AlphaTab.Platform.Model; -using AlphaTab.Rendering.Utils; - -namespace AlphaTab.Rendering.Glyphs -{ - class BeatOnNoteGlyphBase : BeatGlyphBase - { - public BeamingHelper BeamingHelper { get; set; } - public float CenterX { get; set; } - - public BeatOnNoteGlyphBase() - { - CenterX = 0; - } - - public virtual void UpdateBeamingHelper() - { - } - - //public override void Paint(float cx, float cy, ICanvas canvas) - //{ - // base.Paint(cx, cy, canvas); - // canvas.Color = Color.Random(); - // canvas.FillRect(cx + X, cy + Y, Width, 100); - //} - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Glyphs/BeatVibratoGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/BeatVibratoGlyph.cs deleted file mode 100644 index ad9943547..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/BeatVibratoGlyph.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using AlphaTab.Model; -using AlphaTab.Platform; - -namespace AlphaTab.Rendering.Glyphs -{ - class BeatVibratoGlyph : GroupedEffectGlyph - { - private readonly VibratoType _type; - private float _stepSize; - - public BeatVibratoGlyph(VibratoType type) - : base(BeatXPosition.EndBeat) - { - _type = type; - } - - public override void DoLayout() - { - base.DoLayout(); - - switch (_type) - { - case VibratoType.Slight: - _stepSize = 12 * Scale; - break; - case VibratoType.Wide: - _stepSize = 23 * Scale; - break; - } - - Height = 18 * Scale; - } - - protected override void PaintGrouped(float cx, float cy, float endX, ICanvas canvas) - { - var startX = cx + X; - var width = endX - startX; - var loops = (int)Math.Max(1, width / _stepSize); - - canvas.BeginPath(); - canvas.MoveTo(startX, cy + Y); - for (int i = 0; i < loops; i++) - { - canvas.LineTo(startX + _stepSize / 2, cy + Y + Height); - canvas.LineTo(startX + _stepSize, cy + Y); - startX += _stepSize; - } - - canvas.Stroke(); - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Glyphs/BendNoteHeadGroupGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/BendNoteHeadGroupGlyph.cs deleted file mode 100644 index 2ad050055..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/BendNoteHeadGroupGlyph.cs +++ /dev/null @@ -1,155 +0,0 @@ -using System; -using AlphaTab.Collections; -using AlphaTab.Model; -using AlphaTab.Platform; -using AlphaTab.Platform.Model; -using AlphaTab.Rendering.Utils; - -namespace AlphaTab.Rendering.Glyphs -{ - class BendNoteHeadGroupGlyph : ScoreNoteChordGlyphBase - { - private readonly Beat _beat; - private readonly bool _showParenthesis; - private readonly FastDictionary _noteValueLookup; - - private const int ElementPadding = 2; - private AccidentalGroupGlyph _accidentals; - private GhostNoteContainerGlyph _preNoteParenthesis; - private GhostNoteContainerGlyph _postNoteParenthesis; - public bool IsEmpty { get; set; } - public override BeamDirection Direction => BeamDirection.Up; - public float NoteHeadOffset { get; set; } - - public BendNoteHeadGroupGlyph(Beat beat, bool showParenthesis = false) - { - _beat = beat; - _showParenthesis = showParenthesis; - IsEmpty = true; - _accidentals = new AccidentalGroupGlyph(); - _noteValueLookup = new FastDictionary(); - if (showParenthesis) - { - _preNoteParenthesis = new GhostNoteContainerGlyph(true); - _postNoteParenthesis = new GhostNoteContainerGlyph(false); - } - } - - public float GetNoteValueY(int noteValue, bool aboveNote = false) - { - if (_noteValueLookup.ContainsKey(noteValue)) - { - return Y + _noteValueLookup[noteValue].Y + (aboveNote ? -(NoteHeadGlyph.NoteHeadHeight * NoteHeadGlyph.GraceScale * Scale) / 2 : 0); - } - return 0; - } - - public bool ContainsNoteValue(int noteValue) - { - return _noteValueLookup.ContainsKey(noteValue); - } - - public float GetNoteX(int noteValue, bool onMiddle = true) - { - if (_noteValueLookup.ContainsKey(noteValue)) - { - var n = _noteValueLookup[noteValue]; - var pos = X + n.X; - if (onMiddle) - { - pos += n.Width / 2.0f; - } - return pos; - } - return 0; - } - - public void AddGlyph(int noteValue, bool quarterBend = false) - { - var sr = (ScoreBarRenderer)Renderer; - var noteHeadGlyph = new NoteHeadGlyph(0, 0, Duration.Quarter, true); - var accidental = sr.AccidentalHelper.ApplyAccidentalForValue(_beat, noteValue, quarterBend); - var line = sr.AccidentalHelper.GetNoteLineForValue(noteValue); - noteHeadGlyph.Y = sr.GetScoreY(line); - - if (_showParenthesis) - { - _preNoteParenthesis.Renderer = Renderer; - _postNoteParenthesis.Renderer = Renderer; - _preNoteParenthesis.AddParenthesisOnLine(line, true); - _postNoteParenthesis.AddParenthesisOnLine(line, true); - } - - if (accidental != AccidentalType.None) - { - _accidentals.AddGlyph(new AccidentalGlyph(0, noteHeadGlyph.Y, accidental, true)); - } - - _noteValueLookup[noteValue] = noteHeadGlyph; - Add(noteHeadGlyph, line); - - IsEmpty = false; - } - - public override void DoLayout() - { - var x = 0f; - - if (_showParenthesis) - { - _preNoteParenthesis.X = x; - _preNoteParenthesis.Renderer = Renderer; - _preNoteParenthesis.DoLayout(); - x += _preNoteParenthesis.Width + ElementPadding * Scale; - } - - if (!_accidentals.IsEmpty) - { - _accidentals.X = x; - _accidentals.Renderer = Renderer; - _accidentals.DoLayout(); - x += _accidentals.Width + ElementPadding * Scale; - } - - NoteStartX = x; - - base.DoLayout(); - - NoteHeadOffset = NoteStartX + (Width - NoteStartX) / 2; - - if (_showParenthesis) - { - _postNoteParenthesis.X = Width + ElementPadding * Scale; - _postNoteParenthesis.Renderer = Renderer; - _postNoteParenthesis.DoLayout(); - Width += _postNoteParenthesis.Width + ElementPadding * Scale; - } - - } - - public override void Paint(float cx, float cy, ICanvas canvas) - { - var x = 0f; - if (!_accidentals.IsEmpty) x = _accidentals.X; - else if (_showParenthesis) x = _preNoteParenthesis.X; - - //canvas.Color = Color.Random(); - //canvas.FillRect(cx + X, cy + Y, Width, 10); - //canvas.Color = Renderer.Resources.MainGlyphColor; - - if (!_accidentals.IsEmpty) - { - _accidentals.Paint(cx + X, cy + Y, canvas); - } - - if (_showParenthesis) - { - _preNoteParenthesis.Paint(cx + X, cy + Y, canvas); - _postNoteParenthesis.Paint(cx + X, cy + Y, canvas); - } - - base.Paint(cx, cy, canvas); - } - - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Glyphs/ChineseCymbalGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/ChineseCymbalGlyph.cs deleted file mode 100644 index cb7f19218..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/ChineseCymbalGlyph.cs +++ /dev/null @@ -1,35 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Rendering.Glyphs -{ - class ChineseCymbalGlyph : MusicFontGlyph - { - private readonly bool _isGrace; - - public ChineseCymbalGlyph(float x, float y, bool isGrace) - : base(x, y, isGrace ? NoteHeadGlyph.GraceScale : 1, MusicFontSymbol.NoteHarmonic) - { - _isGrace = isGrace; - } - - public override void DoLayout() - { - Width = 9 * (_isGrace ? NoteHeadGlyph.GraceScale : 1) * Scale; - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/CircleGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/CircleGlyph.cs deleted file mode 100644 index 042c3ffa5..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/CircleGlyph.cs +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Platform; - -namespace AlphaTab.Rendering.Glyphs -{ - class CircleGlyph : Glyph - { - private readonly float _size; - - public CircleGlyph(float x, float y, float size) - : base(x, y) - { - _size = size; - } - - public override void DoLayout() - { - Width = _size + (3 * Scale); - } - - public override void Paint(float cx, float cy, ICanvas canvas) - { - canvas.FillCircle(cx + X, cy + Y, _size); - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/ClefGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/ClefGlyph.cs deleted file mode 100644 index 8c2acfcb6..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/ClefGlyph.cs +++ /dev/null @@ -1,134 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using AlphaTab.Model; -using AlphaTab.Platform; - -namespace AlphaTab.Rendering.Glyphs -{ - class ClefGlyph : MusicFontGlyph - { - private readonly Clef _clef; - private readonly Ottavia _clefOttava; - - public ClefGlyph(float x, float y, Clef clef, Ottavia clefOttava) - : base(x, y, 1, GetSymbol(clef)) - { - _clef = clef; - _clefOttava = clefOttava; - } - - public override void DoLayout() - { - switch (_clef) - { - case Clef.Neutral: - Width = 15 * Scale; - break; - case Clef.C3: - case Clef.C4: - case Clef.F4: - case Clef.G2: - Width = 28 * Scale; - break; - } - } - - private static MusicFontSymbol GetSymbol(Clef clef) - { - switch (clef) - { - case Clef.Neutral: - return MusicFontSymbol.ClefNeutral; - case Clef.C3: - return MusicFontSymbol.ClefC; - case Clef.C4: - return MusicFontSymbol.ClefC; - case Clef.F4: - return MusicFontSymbol.ClefF; - case Clef.G2: - return MusicFontSymbol.ClefG; - default: - return MusicFontSymbol.None; - } - } - - public override void Paint(float cx, float cy, ICanvas canvas) - { - base.Paint(cx, cy, canvas); - Glyph numberGlyph; - bool top = false; - switch (_clefOttava) - { - case Ottavia._15ma: - numberGlyph = new MusicFontGlyph(-4 * Scale, 0, 0.5f, MusicFontSymbol.Ottava15); - top = true; - break; - case Ottavia._8va: - numberGlyph = new MusicFontGlyph(-2 * Scale, 0, 0.5f, MusicFontSymbol.Ottava8); - top = true; - break; - case Ottavia._8vb: - numberGlyph = new MusicFontGlyph(-6 * Scale, 0, 0.5f, MusicFontSymbol.Ottava8); - break; - case Ottavia._15mb: - numberGlyph = new MusicFontGlyph(-8 * Scale, 0, 0.5f, MusicFontSymbol.Ottava15); - break; - default: - return; - } - - int offsetY; - int offsetX; - - switch (_clef) - { - case Clef.Neutral: - offsetY = top ? -12 : 15; - offsetX = 0; - break; - case Clef.C3: - offsetY = top ? -19 : 27; - offsetX = 0; - break; - case Clef.C4: - offsetY = top ? -19 : 27; - offsetX = 0; - break; - case Clef.F4: - offsetY = top ? -9 : 27; - offsetX = -4; - break; - case Clef.G2: - offsetY = top ? -37 : 30; - offsetX = 0; - break; - default: - return; - } - - numberGlyph.Renderer = Renderer; - numberGlyph.DoLayout(); - - var x = Width / 2; - - numberGlyph.Paint(cx + X + x + offsetX * Scale, cy + Y + offsetY * Scale, canvas); - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/CrescendoGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/CrescendoGlyph.cs deleted file mode 100644 index e8c84840a..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/CrescendoGlyph.cs +++ /dev/null @@ -1,66 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using AlphaTab.Model; -using AlphaTab.Platform; - -namespace AlphaTab.Rendering.Glyphs -{ - class CrescendoGlyph : GroupedEffectGlyph - { - private const int Padding = NoteHeadGlyph.QuarterNoteHeadWidth / 2; - private readonly CrescendoType _crescendo; - - public CrescendoGlyph(float x, float y, CrescendoType crescendo) - : base(BeatXPosition.EndBeat) - { - _crescendo = crescendo; - X = x; - Y = y; - } - - public override void DoLayout() - { - base.DoLayout(); - Height = 17*Scale; - } - - protected override void PaintGrouped(float cx, float cy, float endX, ICanvas canvas) - { - var startX = cx + X; - var height = Height * Scale; - canvas.BeginPath(); - if (_crescendo == CrescendoType.Crescendo) - { - endX -= Padding * Scale; - canvas.MoveTo(endX, cy + Y); - canvas.LineTo(startX, cy + Y + height / 2); - canvas.LineTo(endX, cy + Y + height); - } - else - { - endX -= Padding * Scale; - canvas.MoveTo(startX, cy + Y); - canvas.LineTo(endX, cy + Y + (height / 2)); - canvas.LineTo(startX, cy + Y + height); - } - canvas.Stroke(); - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/DeadNoteHeadGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/DeadNoteHeadGlyph.cs deleted file mode 100644 index 3ef4052ca..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/DeadNoteHeadGlyph.cs +++ /dev/null @@ -1,35 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Rendering.Glyphs -{ - class DeadNoteHeadGlyph : MusicFontGlyph - { - private readonly bool _isGrace; - - public DeadNoteHeadGlyph(float x, float y, bool isGrace) - : base(x, y, isGrace ? NoteHeadGlyph.GraceScale : 1, MusicFontSymbol.NoteDead) - { - _isGrace = isGrace; - } - - public override void DoLayout() - { - Width = 9 * (_isGrace ? NoteHeadGlyph.GraceScale : 1) * Scale; - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/DiamondNoteHeadGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/DiamondNoteHeadGlyph.cs deleted file mode 100644 index 3035c2e22..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/DiamondNoteHeadGlyph.cs +++ /dev/null @@ -1,52 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Model; - -namespace AlphaTab.Rendering.Glyphs -{ - class DiamondNoteHeadGlyph : MusicFontGlyph - { - private readonly bool _isGrace; - - public DiamondNoteHeadGlyph(float x, float y, Duration duration, bool isGrace) - : base(x, y, isGrace ? NoteHeadGlyph.GraceScale : 1, GetSymbol(duration)) - { - _isGrace = isGrace; - } - - private static MusicFontSymbol GetSymbol(Duration duration) - { - switch (duration) - { - case Duration.QuadrupleWhole: - case Duration.DoubleWhole: - case Duration.Whole: - case Duration.Half: - return MusicFontSymbol.NoteHarmonicWhole; - default: - return MusicFontSymbol.NoteHarmonic; - } - } - - public override void DoLayout() - { - Width = 9 * (_isGrace ? NoteHeadGlyph.GraceScale : 1) * Scale; - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/DigitGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/DigitGlyph.cs deleted file mode 100644 index 5a50267ef..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/DigitGlyph.cs +++ /dev/null @@ -1,88 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Rendering.Glyphs -{ - class DigitGlyph : MusicFontGlyph - { - private readonly int _digit; - private readonly float _scale; - - public DigitGlyph(float x, float y, int digit, float scale) - : base(x, y, scale, GetSymbol(digit)) - { - _digit = digit; - _scale = scale; - } - - public override void DoLayout() - { - Y += 7 * Scale; - Width = GetDigitWidth(_digit) * Scale * _scale; - } - - private float GetDigitWidth(int digit) - { - switch (digit) - { - case 0: - case 2: - case 3: - case 4: - case 5: - case 6: - case 7: - case 8: - case 9: - return 14; - case 1: - return 10; - default: - return 0; - } - } - - private static MusicFontSymbol GetSymbol(int digit) - { - switch (digit) - { - case 0: - return MusicFontSymbol.Num0; - case 1: - return MusicFontSymbol.Num1; - case 2: - return MusicFontSymbol.Num2; - case 3: - return MusicFontSymbol.Num3; - case 4: - return MusicFontSymbol.Num4; - case 5: - return MusicFontSymbol.Num5; - case 6: - return MusicFontSymbol.Num6; - case 7: - return MusicFontSymbol.Num7; - case 8: - return MusicFontSymbol.Num8; - case 9: - return MusicFontSymbol.Num9; - default: - return MusicFontSymbol.None; - } - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/DrumSticksGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/DrumSticksGlyph.cs deleted file mode 100644 index e5f38deb6..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/DrumSticksGlyph.cs +++ /dev/null @@ -1,35 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Rendering.Glyphs -{ - class DrumSticksGlyph : MusicFontGlyph - { - private readonly bool _isGrace; - - public DrumSticksGlyph(float x, float y, bool isGrace) - : base(x, y, isGrace ? NoteHeadGlyph.GraceScale : 1, MusicFontSymbol.NoteSideStick) - { - _isGrace = isGrace; - } - - public override void DoLayout() - { - Width = 9 * (_isGrace ? NoteHeadGlyph.GraceScale : 1) * Scale; - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/DynamicsGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/DynamicsGlyph.cs deleted file mode 100644 index ff3c709f8..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/DynamicsGlyph.cs +++ /dev/null @@ -1,61 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Model; - -namespace AlphaTab.Rendering.Glyphs -{ - class DynamicsGlyph : MusicFontGlyph - { - public DynamicsGlyph(float x, float y, DynamicValue dynamics) - : base(x, y, 0.6f, GetSymbol(dynamics)) - { - } - - public override void DoLayout() - { - base.DoLayout(); - Height = 17 * Scale; - Y += Height / 2; - } - - private static MusicFontSymbol GetSymbol(DynamicValue dynamics) - { - switch (dynamics) - { - case DynamicValue.PPP: - return MusicFontSymbol.DynamicPPP; - case DynamicValue.PP: - return MusicFontSymbol.DynamicPP; - case DynamicValue.P: - return MusicFontSymbol.DynamicP; - case DynamicValue.MP: - return MusicFontSymbol.DynamicMP; - case DynamicValue.MF: - return MusicFontSymbol.DynamicMF; - case DynamicValue.F: - return MusicFontSymbol.DynamicF; - case DynamicValue.FF: - return MusicFontSymbol.DynamicFF; - case DynamicValue.FFF: - return MusicFontSymbol.DynamicFFF; - default: - return MusicFontSymbol.None; - } - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/EffectGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/EffectGlyph.cs deleted file mode 100644 index ef2b2227b..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/EffectGlyph.cs +++ /dev/null @@ -1,53 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Model; - -namespace AlphaTab.Rendering.Glyphs -{ - /// - /// Effect-Glyphs implementing this public interface get notified - /// as they are expanded over multiple beats. - /// - class EffectGlyph : Glyph - { - /// - /// Gets or sets the beat where the glyph belongs to. - /// - public Beat Beat { get; set; } - - /// - /// Gets or sets the next glyph of the same type in case - /// the effect glyph is expanded when using . - /// - public EffectGlyph NextGlyph { get; set; } - - /// - /// Gets or sets the previous glyph of the same type in case - /// the effect glyph is expanded when using . - /// - public EffectGlyph PreviousGlyph { get; set; } - - public float Height { get; set; } - - protected EffectGlyph(float x, float y) - : base(x, y) - { - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/FadeInGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/FadeInGlyph.cs deleted file mode 100644 index 3952eb405..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/FadeInGlyph.cs +++ /dev/null @@ -1,52 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using AlphaTab.Platform; - -namespace AlphaTab.Rendering.Glyphs -{ - class FadeInGlyph : EffectGlyph - { - public FadeInGlyph(float x, float y) - : base(x, y) - { - } - - public override void DoLayout() - { - base.DoLayout(); - Height = 17 * Scale; - } - - public override void Paint(float cx, float cy, ICanvas canvas) - { - var size = 6 * Scale; - var width = Math.Max(Width, 14 * Scale); - - var offset = Height / 2; - - canvas.BeginPath(); - canvas.MoveTo(cx + X, cy + Y + offset); - canvas.QuadraticCurveTo(cx + X + (width / 2), cy + Y + offset, cx + X + width, cy + Y + offset - size); - canvas.MoveTo(cx + X, cy + Y + offset); - canvas.QuadraticCurveTo(cx + X + (width / 2), cy + Y + offset, cx + X + width, cy + Y + offset + size); - canvas.Stroke(); - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/FermataGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/FermataGlyph.cs deleted file mode 100644 index 37077997a..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/FermataGlyph.cs +++ /dev/null @@ -1,39 +0,0 @@ -using AlphaTab.Model; -using AlphaTab.Platform; - -namespace AlphaTab.Rendering.Glyphs -{ - class FermataGlyph : MusicFontGlyph - { - public FermataGlyph(float x, float y, FermataType fermata) - : base(x, y, 1, GetSymbol(fermata)) - { - } - - private static MusicFontSymbol GetSymbol(FermataType accentuation) - { - switch (accentuation) - { - case FermataType.Short: - return MusicFontSymbol.FermataShort; - case FermataType.Medium: - return MusicFontSymbol.FermataMedium; - case FermataType.Long: - return MusicFontSymbol.FermataLong; - default: - return MusicFontSymbol.None; - } - } - - public override void DoLayout() - { - Width = 23 * Scale; - Height = 12 * Scale; - } - - public override void Paint(float cx, float cy, ICanvas canvas) - { - base.Paint(cx - Width / 2, cy + Height, canvas); - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Glyphs/FlatGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/FlatGlyph.cs deleted file mode 100644 index 0d7a60215..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/FlatGlyph.cs +++ /dev/null @@ -1,59 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using AlphaTab.Model; - -namespace AlphaTab.Rendering.Glyphs -{ - class AccidentalGlyph : MusicFontGlyph - { - private readonly bool _isGrace; - - public AccidentalGlyph(float x, float y, AccidentalType accidentalType, bool isGrace = false) - : base(x, y, isGrace ? NoteHeadGlyph.GraceScale : 1,GetMusicSymbol(accidentalType)) - { - _isGrace = isGrace; - } - - private static MusicFontSymbol GetMusicSymbol(AccidentalType accidentalType) - { - switch (accidentalType) - { - case AccidentalType.Natural: - return MusicFontSymbol.AccidentalNatural; - case AccidentalType.Sharp: - return MusicFontSymbol.AccidentalSharp; - case AccidentalType.Flat: - return MusicFontSymbol.AccidentalFlat; - case AccidentalType.NaturalQuarterNoteUp: - return MusicFontSymbol.AccidentalQuarterToneNaturalArrowUp; - case AccidentalType.SharpQuarterNoteUp: - return MusicFontSymbol.AccidentalQuarterToneSharpArrowUp; - case AccidentalType.FlatQuarterNoteUp: - return MusicFontSymbol.AccidentalQuarterToneFlatArrowUp; - } - return MusicFontSymbol.None; - } - - public override void DoLayout() - { - Width = 8 * (_isGrace ? NoteHeadGlyph.GraceScale : 1) * Scale; - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/GhostNoteContainerGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/GhostNoteContainerGlyph.cs deleted file mode 100644 index dbb8567cb..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/GhostNoteContainerGlyph.cs +++ /dev/null @@ -1,111 +0,0 @@ -using System; -using AlphaTab.Collections; -using AlphaTab.Model; -using AlphaTab.Platform; - -namespace AlphaTab.Rendering.Glyphs -{ - class GhostNoteInfo - { - public int Line { get; set; } - public bool IsGhost { get; set; } - - public GhostNoteInfo(int line, bool isGhost) - { - Line = line; - IsGhost = isGhost; - } - } - - - class GhostNoteContainerGlyph : Glyph - { - private readonly bool _isOpen; - private readonly FastList _infos; - private readonly FastList _glyphs; - - public bool IsEmpty { get; private set; } - - public GhostNoteContainerGlyph(bool isOpen) : base(0, 0) - { - _isOpen = isOpen; - _infos = new FastList(); - _glyphs = new FastList(); - IsEmpty = true; - } - - public void AddParenthesis(Note n) - { - var sr = (ScoreBarRenderer)Renderer; - var line = sr.GetNoteLine(n); - var hasParenthesis = n.IsGhost || (IsTiedBend(n) && sr.Settings.ShowParenthesisForTiedBends); - AddParenthesisOnLine(line, hasParenthesis); - } - - public void AddParenthesisOnLine(int line, bool hasParenthesis) - { - var info = new GhostNoteInfo(line, hasParenthesis); - _infos.Add(info); - if (hasParenthesis) - { - IsEmpty = false; - } - } - - private bool IsTiedBend(Note note) - { - if (note.IsTieDestination) - { - if (note.TieOrigin.HasBend) return true; - return IsTiedBend(note.TieOrigin); - } - return false; - } - - public override void DoLayout() - { - var sr = (ScoreBarRenderer)Renderer; - _infos.Sort((a, b) => a.Line.CompareTo(b.Line)); - - GhostParenthesisGlyph previousGlyph = null; - - var sizePerLine = sr.GetScoreY(1); - - for (int i = 0, j = _infos.Count; i < j; i++) - { - GhostParenthesisGlyph g; - - if (!_infos[i].IsGhost) - { - previousGlyph = null; - } - else if (previousGlyph == null) - { - g = new GhostParenthesisGlyph(_isOpen); - g.Renderer = Renderer; - g.Y = sr.GetScoreY(_infos[i].Line) - sizePerLine; - g.Height = sizePerLine * 2; - g.DoLayout(); - _glyphs.Add(g); - previousGlyph = g; - } - else - { - var y = sr.GetScoreY(_infos[i].Line) + sizePerLine; - previousGlyph.Height = y - previousGlyph.Y; - } - } - - Width = _glyphs.Count > 0 ? _glyphs[0].Width : 0; - } - - public override void Paint(float cx, float cy, ICanvas canvas) - { - base.Paint(cx, cy, canvas); - foreach (var g in _glyphs) - { - g.Paint(cx + X, cy + Y, canvas); - } - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Glyphs/GhostParenthesisGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/GhostParenthesisGlyph.cs deleted file mode 100644 index 16e5540a8..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/GhostParenthesisGlyph.cs +++ /dev/null @@ -1,42 +0,0 @@ -using AlphaTab.Platform; - -namespace AlphaTab.Rendering.Glyphs -{ - class GhostParenthesisGlyph : Glyph - { - private readonly bool _isOpen; - private const int Size = 6; - public GhostParenthesisGlyph(bool isOpen) : base(0, 0) - { - _isOpen = isOpen; - } - - public float Height { get; set; } - - public override void DoLayout() - { - base.DoLayout(); - Width = Size * Scale; - } - - public override void Paint(float cx, float cy, ICanvas canvas) - { - if (_isOpen) - { - TieGlyph.PaintTie(canvas, Scale, - cx + X + Width, cy + Y + Height, - cx + X + Width, cy + Y, - false, Size, 3); - } - else - { - TieGlyph.PaintTie(canvas, Scale, - cx + X, cy + Y, - cx + X, cy + Y + Height, - false, Size, 3); - - } - canvas.Fill(); - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Glyphs/Glyph.cs b/Source/AlphaTab/Rendering/Glyphs/Glyph.cs deleted file mode 100644 index 30510eab7..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/Glyph.cs +++ /dev/null @@ -1,57 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Platform; - -namespace AlphaTab.Rendering.Glyphs -{ - /// - /// A glyph is a single symbol which can be added to a GlyphBarRenderer for automated - /// layouting and drawing of stacked symbols. - /// - class Glyph - { - public float X { get; set; } - public float Y { get; set; } - public float Width { get; set; } - public BarRendererBase Renderer { get; set; } - - public Glyph(float x, float y) - { - X = x; - Y = y; - } - - public float Scale - { - get - { - return Renderer.Scale; - } - } - - public virtual void DoLayout() - { - - } - - public virtual void Paint(float cx, float cy, ICanvas canvas) - { - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/GlyphGroup.cs b/Source/AlphaTab/Rendering/Glyphs/GlyphGroup.cs deleted file mode 100644 index 9bdd1f100..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/GlyphGroup.cs +++ /dev/null @@ -1,77 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.Collections; -using AlphaTab.Platform; - -namespace AlphaTab.Rendering.Glyphs -{ - /// - /// This glyph allows to group several other glyphs to be - /// drawn at the same x position - /// - class GlyphGroup : Glyph - { - protected FastList Glyphs; - - public bool IsEmpty - { - get { return Glyphs == null || Glyphs.Count == 0; } - } - - public GlyphGroup(float x, float y) - : base(x, y) - { - } - - public override void DoLayout() - { - if (Glyphs == null || Glyphs.Count == 0) - { - Width = 0; - return; - } - - var w = 0f; - for (int i = 0, j = Glyphs.Count; i < j; i++) - { - var g = Glyphs[i]; - g.Renderer = Renderer; - g.DoLayout(); - w = Math.Max(w, g.Width); - } - Width = w; - } - - public virtual void AddGlyph(Glyph g) - { - if (Glyphs == null) Glyphs = new FastList(); - Glyphs.Add(g); - } - - public override void Paint(float cx, float cy, ICanvas canvas) - { - var glyphs = Glyphs; - if (glyphs == null || glyphs.Count == 0) return; - foreach (var g in glyphs) - { - g.Paint(cx + X, cy + Y, canvas); - } - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/GroupedEffectGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/GroupedEffectGlyph.cs deleted file mode 100644 index 2a5630a8e..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/GroupedEffectGlyph.cs +++ /dev/null @@ -1,123 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Model; -using AlphaTab.Platform; - -namespace AlphaTab.Rendering.Glyphs -{ - abstract class GroupedEffectGlyph : EffectGlyph - { - protected BeatXPosition EndPosition { get; set; } - protected bool ForceGroupedRendering { get; set; } - protected bool EndOnBarLine { get; set; } - - protected GroupedEffectGlyph(BeatXPosition endPosition) : base(0, 0) - { - EndPosition = endPosition; - } - - /// - /// Gets a value whether this glyph is linked with a previous glyph for rendering. - /// This means this glyph will not be rendered itself, but rendered as part of the very first glyph of this link-group. - /// - public bool IsLinkedWithPrevious - { - get - { - return PreviousGlyph != null && PreviousGlyph.Renderer.Staff.StaveGroup == Renderer.Staff.StaveGroup; - } - } - - /// - /// Gets a value whether this glyph is linked with the next glyph for rendering. - /// - public bool IsLinkedWithNext - { - get - { - // we additionally check IsFinalized since the next renderer might not be part of the current partial - // and therefore not finalized yet. - return NextGlyph != null && NextGlyph.Renderer.IsFinalized && NextGlyph.Renderer.Staff.StaveGroup == Renderer.Staff.StaveGroup; - } - } - - public override void Paint(float cx, float cy, ICanvas canvas) - { - // if we are linked with the previous, the first glyph of the group will also render this one. - if (IsLinkedWithPrevious) - { - return; - } - - // we are not linked with any glyph therefore no expansion is required, we render a simple glyph. - if (!IsLinkedWithNext && !ForceGroupedRendering) - { - PaintNonGrouped(cx, cy, canvas); - return; - } - - // find last linked glyph that can be - GroupedEffectGlyph lastLinkedGlyph; - if (!IsLinkedWithNext && ForceGroupedRendering) - { - lastLinkedGlyph = this; - } - else - { - lastLinkedGlyph = (GroupedEffectGlyph) NextGlyph; - while (lastLinkedGlyph.IsLinkedWithNext) - { - lastLinkedGlyph = (GroupedEffectGlyph) lastLinkedGlyph.NextGlyph; - } - } - - // use start position of next beat when possible - var endBeatRenderer = lastLinkedGlyph.Renderer; - var endBeat = lastLinkedGlyph.Beat; - var position = EndPosition; - - // calculate end X-position - var cxRenderer = cx - Renderer.X; - var endX = CalculateEndX(endBeatRenderer, endBeat, cxRenderer, position); - - PaintGrouped(cx, cy, endX, canvas); - } - - protected virtual float CalculateEndX(BarRendererBase endBeatRenderer, Beat endBeat, float cx, BeatXPosition endPosition) - { - if (endBeat == null) - { - return cx + endBeatRenderer.X + X + Width; - } - else - { - return cx + endBeatRenderer.X + endBeatRenderer.GetBeatX(endBeat, endPosition); - } - } - - protected virtual void PaintNonGrouped(float cx, float cy, ICanvas canvas) - { - var cxRenderer = cx - Renderer.X; - var endX = CalculateEndX(Renderer, Beat, cxRenderer, EndPosition); - PaintGrouped(cx, cy, endX, canvas); - } - - protected abstract void PaintGrouped(float cx, float cy, float endX, ICanvas canvas); - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Glyphs/HiHatGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/HiHatGlyph.cs deleted file mode 100644 index 23b31fd01..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/HiHatGlyph.cs +++ /dev/null @@ -1,35 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Rendering.Glyphs -{ - class HiHatGlyph : MusicFontGlyph - { - private readonly bool _isGrace; - - public HiHatGlyph(float x, float y, bool isGrace) - : base(x, y, isGrace ? NoteHeadGlyph.GraceScale : 1, MusicFontSymbol.NoteHiHat) - { - _isGrace = isGrace; - } - - public override void DoLayout() - { - Width = 9 * (_isGrace ? NoteHeadGlyph.GraceScale : 1) * Scale; - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/LeftToRightLayoutingGlyphGroup.cs b/Source/AlphaTab/Rendering/Glyphs/LeftToRightLayoutingGlyphGroup.cs deleted file mode 100644 index d756281a3..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/LeftToRightLayoutingGlyphGroup.cs +++ /dev/null @@ -1,41 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Collections; - -namespace AlphaTab.Rendering.Glyphs -{ - class LeftToRightLayoutingGlyphGroup : GlyphGroup - { - public LeftToRightLayoutingGlyphGroup() - : base(0,0) - { - Glyphs = new FastList(); - } - - public override void AddGlyph(Glyph g) - { - g.X = Glyphs.Count == 0 - ? 0 - : (Glyphs[Glyphs.Count - 1].X + Glyphs[Glyphs.Count - 1].Width); - g.Renderer = Renderer; - g.DoLayout(); - Width = g.X + g.Width; - base.AddGlyph(g); - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Glyphs/LineRangedGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/LineRangedGlyph.cs deleted file mode 100644 index 61cafc8ef..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/LineRangedGlyph.cs +++ /dev/null @@ -1,87 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.Platform; -using AlphaTab.Platform.Model; - -namespace AlphaTab.Rendering.Glyphs -{ - class LineRangedGlyph : GroupedEffectGlyph - { - public const float LineSpacing = 3; - public const float LineTopPadding = 4; - public const float LineTopOffset = 5; - public const float LineSize = 8; - private readonly string _label; - - public LineRangedGlyph(string label) - : base(BeatXPosition.OnNotes) - { - _label = label; - } - - public override void DoLayout() - { - if (Renderer.Settings.ExtendLineEffectsToBeatEnd) - { - EndPosition = BeatXPosition.EndBeat; - ForceGroupedRendering = true; - } - base.DoLayout(); - Height = Renderer.Resources.EffectFont.Size; - } - - protected override void PaintNonGrouped(float cx, float cy, ICanvas canvas) - { - var res = Renderer.Resources; - canvas.Font = res.EffectFont; - var x = canvas.TextAlign; - canvas.TextAlign = TextAlign.Center; - canvas.FillText(_label, cx + X, cy + Y); - canvas.TextAlign = x; - } - - protected override void PaintGrouped(float cx, float cy, float endX, ICanvas canvas) - { - PaintNonGrouped(cx, cy, canvas); - - var lineSpacing = LineSpacing * Scale; - var textWidth = canvas.MeasureText(_label); - var startX = cx + X + textWidth/2f + lineSpacing; - var lineY = cy + Y + (LineTopPadding * Scale); - var lineSize = LineSize * Scale; - - if (endX > startX) - { - var lineX = startX; - while (lineX < endX) - { - canvas.BeginPath(); - canvas.MoveTo(lineX, (int)lineY); - canvas.LineTo(Math.Min(lineX + lineSize, endX), (int)lineY); - lineX += lineSize + lineSpacing; - canvas.Stroke(); - } - canvas.BeginPath(); - canvas.MoveTo(endX, (int)(lineY - LineTopOffset * Scale)); - canvas.LineTo(endX, (int)(lineY + LineTopOffset * Scale)); - canvas.Stroke(); - } - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/LyricsGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/LyricsGlyph.cs deleted file mode 100644 index 07984ec5e..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/LyricsGlyph.cs +++ /dev/null @@ -1,59 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Platform; -using AlphaTab.Platform.Model; - -namespace AlphaTab.Rendering.Glyphs -{ - class LyricsGlyph : EffectGlyph - { - private readonly string[] _lines; - - public Font Font { get; set; } - public TextAlign TextAlign { get; set; } - - public LyricsGlyph(float x, float y, string[] lines, Font font, TextAlign textAlign = TextAlign.Center) - : base(x, y) - { - _lines = lines; - Font = font; - TextAlign = textAlign; - } - - public override void DoLayout() - { - base.DoLayout(); - Height = Font.Size * _lines.Length; - } - - public override void Paint(float cx, float cy, ICanvas canvas) - { - canvas.Font = Font; - var old = canvas.TextAlign; - canvas.TextAlign = TextAlign; - for (int i = 0; i < _lines.Length; i++) - { - if (_lines[i] != null) - { - canvas.FillText(_lines[i], cx + X, cy + Y + i * Font.Size); - } - } - canvas.TextAlign = old; - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Glyphs/MusicFontGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/MusicFontGlyph.cs deleted file mode 100644 index a93a660ac..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/MusicFontGlyph.cs +++ /dev/null @@ -1,39 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Platform; - -namespace AlphaTab.Rendering.Glyphs -{ - class MusicFontGlyph : EffectGlyph - { - protected readonly float GlyphScale; - protected readonly MusicFontSymbol Symbol; - - public MusicFontGlyph(float x, float y, float glyphScale, MusicFontSymbol symbol) - : base(x, y) - { - GlyphScale = glyphScale; - Symbol = symbol; - } - - public override void Paint(float cx, float cy, ICanvas canvas) - { - canvas.FillMusicFontSymbol(cx + X, cy + Y, GlyphScale*Scale, Symbol); - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/MusicFontSymbol.cs b/Source/AlphaTab/Rendering/Glyphs/MusicFontSymbol.cs deleted file mode 100644 index af623985a..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/MusicFontSymbol.cs +++ /dev/null @@ -1,135 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Rendering.Glyphs -{ - enum MusicFontSymbol - { - None = -1, - - ClefG = 0xE050, - ClefC = 0xE05C, - ClefF = 0xE062, - ClefNeutral = 0xE069, - ClefTab = 0xE06D, - ClefTabSmall = 0xE06E, - - RestQuadrupleWhole = 0xE4E1, - RestDoubleWhole = 0xE4E2, - RestWhole = 0xE4E3, - RestHalf = 0xE4E4, - RestQuarter = 0xE4E5, - RestEighth = 0xE4E6, - RestSixteenth = 0xE4E7, - RestThirtySecond = 0xE4E8, - RestSixtyFourth = 0xE4E9, - RestOneHundredTwentyEighth = 0xE4EA, - RestTwoHundredFiftySixth = 0xE4EB, - - Trill = 0xE566, - - Num0 = 0xE080, - Num1 = 0xE081, - Num2 = 0xE082, - Num3 = 0xE083, - Num4 = 0xE084, - Num5 = 0xE085, - Num6 = 0xE086, - Num7 = 0xE087, - Num8 = 0xE088, - Num9 = 0xE089, - TimeSignatureCommon = 0xE08A, - TimeSignatureCutCommon = 0xE08b, - - NoteQuadrupleWhole = 0xE0A1, - NoteDoubleWhole = 0xE0A0, - NoteWhole = 0xE0A2, - NoteHalf = 0xE0A3, - NoteQuarter = 0xE0A4, - NoteDead = 0xE0AA, - NoteHarmonic = 0xE0DC, - NoteHarmonicWhole = 0xE0DE, - NoteHiHat = 0xE0B3, - NoteSideStick = 0xE0A9, - NoteHiHatHalf = 0xE0F7, - NoteChineseCymbal = 0xE0F9, - - FooterUpEighth = 0xE240, - FooterDownEighth = 0xE241, - - FooterUpSixteenth = 0xE242, - FooterDownSixteenth = 0xE243, - - FooterUpThirtySecond = 0xE244, - FooterDownThirtySecond = 0xE245, - - FooterUpSixtyFourth = 0xE246, - FooterDownSixtyFourth = 0xE247, - - FooterUpOneHundredTwentyEighth = 0xE248, - FooterDownOneHundredTwentyEighth = 0xE249, - - FooterUpTwoHundredFiftySixth = 0xE24A, - FooterDownTwoHundredFiftySixth = 0xE24B, - - DynamicPPP = 0xE52A, - DynamicPP = 0xE52B, - DynamicP = 0xE520, - DynamicMP = 0xE52C, - DynamicMF = 0xE52D, - DynamicF = 0xE522, - DynamicFF = 0xE52F, - DynamicFFF = 0xE530, - - Accentuation = 0xE4A0, - HeavyAccentuation = 0xE4AC, - - WaveHorizontalSlight = 0xEAA4, - WaveHorizontalWide = 0xEADE, - - PickStrokeDown = 0xE610, - PickStrokeUp = 0xE612, - TremoloPickingThirtySecond = 0xE222, - TremoloPickingSixteenth = 0xE221, - TremoloPickingEighth = 0xE220, - - Tempo = 0xE1D5, - NoteEighth = 0xE1D7, - - AccidentalFlat = 0xE260, - AccidentalNatural = 0xE261, - AccidentalSharp = 0xE262, - AccidentalQuarterToneFlatArrowUp = 0xE270, - AccidentalQuarterToneSharpArrowUp = 0xE274, - AccidentalQuarterToneNaturalArrowUp = 0xE272, - - Ottava8 = 0xE510, - Ottava8va = 0xE511, - Ottava8vb = 0xE51C, - Ottava15 = 0xE514, - Ottava15ma = 0xE515, - OttavaMBaseline = 0xEC95, - OttavaBBaseline = 0xEC93, - - SimileMarkSimple = 0xE500, - SimileMarkDouble = 0xE501, - - FermataMedium = 0xE4C0, - FermataShort = 0xE4C4, - FermataLong = 0xE4C6 - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Glyphs/NoteHeadGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/NoteHeadGlyph.cs deleted file mode 100644 index 17aceed7c..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/NoteHeadGlyph.cs +++ /dev/null @@ -1,86 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Model; -using AlphaTab.Platform; - -namespace AlphaTab.Rendering.Glyphs -{ - class NoteHeadGlyph : MusicFontGlyph - { - public const float GraceScale = 0.75f; - public const float NoteHeadHeight = 9; - public const int QuarterNoteHeadWidth = 8; - - private readonly bool _isGrace; - private readonly Duration _duration; - - public NoteHeadGlyph(float x, float y, Duration duration, bool isGrace) - : base(x, y, isGrace ? GraceScale : 1, GetSymbol(duration)) - { - _isGrace = isGrace; - _duration = duration; - } - - public override void Paint(float cx, float cy, ICanvas canvas) - { - var offset = _isGrace ? Scale : 0; - canvas.FillMusicFontSymbol(cx + X, cy + Y + offset, GlyphScale * Scale, Symbol); - } - - public override void DoLayout() - { - var scale = (_isGrace ? GraceScale : 1) * Scale; - switch (_duration) - { - case Duration.QuadrupleWhole: - Width = 14 * scale; - Height = NoteHeadHeight * scale; - break; - case Duration.DoubleWhole: - Width = 14 * (_isGrace ? GraceScale : 1) * Scale; - Height = NoteHeadHeight * scale; - break; - case Duration.Whole: - Width = 14 * (_isGrace ? GraceScale : 1) * Scale; - Height = NoteHeadHeight * scale; - break; - default: - Width = QuarterNoteHeadWidth * (_isGrace ? GraceScale : 1) * Scale; - Height = NoteHeadHeight * scale; - break; - } - } - - private static MusicFontSymbol GetSymbol(Duration duration) - { - switch (duration) - { - case Duration.QuadrupleWhole: - return MusicFontSymbol.NoteQuadrupleWhole; - case Duration.DoubleWhole: - return MusicFontSymbol.NoteDoubleWhole; - case Duration.Whole: - return MusicFontSymbol.NoteWhole; - case Duration.Half: - return MusicFontSymbol.NoteHalf; - default: - return MusicFontSymbol.NoteQuarter; - } - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Glyphs/NoteNumberGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/NoteNumberGlyph.cs deleted file mode 100644 index f5834e4d0..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/NoteNumberGlyph.cs +++ /dev/null @@ -1,159 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using AlphaTab.Model; -using AlphaTab.Platform; -using AlphaTab.Rendering.Utils; - -namespace AlphaTab.Rendering.Glyphs -{ - class NoteNumberGlyph : Glyph - { - private readonly Note _note; - private string _noteString; - private string _trillNoteString; - private float _trillNoteStringWidth; - - public bool IsEmpty { get; set; } - public float Height { get; set; } - public float NoteStringWidth { get; set; } - - public NoteNumberGlyph(float x, float y, Note note) - : base(x, y) - { - _note = note; - } - - public override void DoLayout() - { - var n = _note; - - double fret = n.Fret - n.Beat.Voice.Bar.Staff.TranspositionPitch; - if (n.HarmonicType == HarmonicType.Natural && n.HarmonicValue != 0) - { - fret = n.HarmonicValue - n.Beat.Voice.Bar.Staff.TranspositionPitch; - } - - if (!n.IsTieDestination) - { - _noteString = n.IsDead ? "x" : fret.ToString(); - if (n.IsGhost) - { - _noteString = "(" + _noteString + ")"; - } - else if (n.HarmonicType == HarmonicType.Natural) - { - // only first decimal char - var i = _noteString.IndexOf('.'); - if (i >= 0) - { - _noteString = _noteString.Substring(0, i + 2); - } - _noteString = "<" + _noteString + ">"; - } - } - else if (n.Beat.Index == 0 && Renderer.Settings.DisplayMode == DisplayMode.GuitarPro // GP shows tied notes on first beat - || n.BendType == BendType.Bend && Renderer.Settings.ShowTabNoteOnTiedBend && n.IsTieOrigin) - { - _noteString = "(" + (n.TieOrigin.Fret - n.Beat.Voice.Bar.Staff.TranspositionPitch) + ")"; - } - else - { - _noteString = ""; - } - - if (n.IsTrill) - { - _trillNoteString = "(" + (n.TrillFret - n.Beat.Voice.Bar.Staff.TranspositionPitch) + ")"; - } - else if (!n.HarmonicValue.IsAlmostEqualTo(0)) - { - switch (n.HarmonicType) - { - case HarmonicType.Artificial: - case HarmonicType.Pinch: - case HarmonicType.Tap: - case HarmonicType.Semi: - case HarmonicType.Feedback: - - var s = (fret + n.HarmonicValue).ToString(); - // only first decimal char - var i = s.IndexOf('.'); - if (i >= 0) - { - s = s.Substring(0, i + 2); - } - - _trillNoteString = "<" + s + ">"; - break; - default: - _trillNoteString = ""; - break; - } - } - else - { - _trillNoteString = ""; - } - - IsEmpty = string.IsNullOrEmpty(_noteString); - if (!IsEmpty) - { - Renderer.ScoreRenderer.Canvas.Font = Renderer.Resources.TablatureFont; - Width = NoteStringWidth = Renderer.ScoreRenderer.Canvas.MeasureText(_noteString); - Height = Renderer.ScoreRenderer.Canvas.Font.Size; - - var hasTrill = !string.IsNullOrEmpty(_trillNoteString); - if (hasTrill) - { - Renderer.ScoreRenderer.Canvas.Font = Renderer.Resources.GraceFont; - _trillNoteStringWidth = 3 * Scale + Renderer.ScoreRenderer.Canvas.MeasureText(_trillNoteString); - Width += _trillNoteStringWidth; - } - } - } - - public override void Paint(float cx, float cy, ICanvas canvas) - { - if (IsEmpty) return; - var textWidth = NoteStringWidth + _trillNoteStringWidth; - var x = cx + X + (Width - textWidth) / 2; - - Renderer.ScoreRenderer.Canvas.Font = Renderer.Resources.GraceFont; - canvas.FillText(_trillNoteString, x + NoteStringWidth + 3 * Scale, cy + Y); - - Renderer.ScoreRenderer.Canvas.Font = Renderer.Resources.TablatureFont; - canvas.FillText(_noteString, x, cy + Y); - - if (Renderer.Settings.IncludeNoteBounds) - { - var noteBounds = new NoteBounds(); - noteBounds.Note = _note; - noteBounds.NoteHeadBounds = new Bounds - { - X = cx + X, - Y = cy + Y, - W = Width, - H = Height - }; - Renderer.ScoreRenderer.BoundsLookup.AddNote(noteBounds); - } - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/NoteVibratoGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/NoteVibratoGlyph.cs deleted file mode 100644 index ecc01a719..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/NoteVibratoGlyph.cs +++ /dev/null @@ -1,83 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.Model; -using AlphaTab.Platform; - -namespace AlphaTab.Rendering.Glyphs -{ - class NoteVibratoGlyph : GroupedEffectGlyph - { - public const int SlightWaveOffset = 10; - public const float SlightWaveSize = 8.5f; - - private readonly VibratoType _type; - private readonly float _scale; - private MusicFontSymbol _symbol; - private float _symbolSize; - private float _symbolOffset; - - public NoteVibratoGlyph(float x, float y, VibratoType type, float scale = 1.2f) - : base(BeatXPosition.EndBeat) - { - _type = type; - _scale = scale; - X = x; - Y = y; - } - - public override void DoLayout() - { - base.DoLayout(); - - var symbolHeight = 0f; - switch (_type) - { - case VibratoType.Slight: - _symbol = MusicFontSymbol.WaveHorizontalSlight; - _symbolSize = SlightWaveSize * _scale; - _symbolOffset = SlightWaveOffset * _scale; - symbolHeight = 6 * _scale; - break; - case VibratoType.Wide: - _symbol = MusicFontSymbol.WaveHorizontalWide; - _symbolSize = 10 * _scale; - _symbolOffset = 7 * _scale; - symbolHeight = 10 * _scale; - break; - } - - Height = symbolHeight * Scale; - } - - protected override void PaintGrouped(float cx, float cy, float endX, ICanvas canvas) - { - var startX = cx + X; - var width = endX - startX; - var step = _symbolSize * Scale; - var loops = (int)Math.Max(1, width / step); - - var loopX = 0f; - for (var i = 0; i < loops; i++) - { - canvas.FillMusicFontSymbol(cx + X + loopX, cy + Y + _symbolOffset, _scale, _symbol); - loopX += step; - } - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/NumberGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/NumberGlyph.cs deleted file mode 100644 index 046e7a951..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/NumberGlyph.cs +++ /dev/null @@ -1,57 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Rendering.Glyphs -{ - class NumberGlyph : GlyphGroup - { - private readonly int _number; - private readonly float _scale; - - public NumberGlyph(float x, float y, int number, float scale = 1.0f) - : base(x, y) - { - _number = number; - _scale = scale; - } - - public override void DoLayout() - { - var i = _number; - while (i > 0) - { - var num = i % 10; - var gl = new DigitGlyph(0, 0, num, _scale); - AddGlyph(gl); - i = i / 10; - } - Glyphs.Reverse(); - - var cx = 0f; - for (int j = 0, k = Glyphs.Count; j < k; j++) - { - var g = Glyphs[j]; - g.X = cx; - g.Y = 0; - g.Renderer = Renderer; - g.DoLayout(); - cx += g.Width; - } - Width = cx; - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/OttavaGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/OttavaGlyph.cs deleted file mode 100644 index e2ea90d39..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/OttavaGlyph.cs +++ /dev/null @@ -1,103 +0,0 @@ -using System; -using AlphaTab.Model; -using AlphaTab.Platform; - -namespace AlphaTab.Rendering.Glyphs -{ - class OttavaGlyph : GroupedEffectGlyph - { - private Ottavia _ottava; - private bool _aboveStaff; - - public OttavaGlyph(Ottavia ottava, bool aboveStaff) - : base(BeatXPosition.PostNotes) - { - _ottava = ottava; - _aboveStaff = aboveStaff; - } - - public override void DoLayout() - { - base.DoLayout(); - Height = 13 * Scale; - } - - protected override void PaintNonGrouped(float cx, float cy, ICanvas canvas) - { - PaintOttava(cx, cy, canvas); - } - - private float PaintOttava(float cx, float cy, ICanvas canvas) - { - float size = 0; - switch (_ottava) - { - case Ottavia._15ma: - size = 37 * Scale; - canvas.FillMusicFontSymbol(cx + X - size / 2, cy + Y + Height, 0.8f, MusicFontSymbol.Ottava15ma); - break; - case Ottavia._8va: - size = 26 * Scale; - canvas.FillMusicFontSymbol(cx + X - size / 2, cy + Y + Height, 0.8f, MusicFontSymbol.Ottava8va); - break; - case Ottavia._8vb: - size = 23 * Scale; - canvas.FillMusicFontSymbol(cx + X - size / 2, cy + Y + Height, 0.8f, MusicFontSymbol.Ottava8vb); - break; - case Ottavia._15mb: - size = 36 * Scale; - // NOTE: SMUFL does not have a glyph for 15mb so we build it - canvas.FillMusicFontSymbols(cx + X - size / 2, cy + Y + Height, 0.8f, new[] - { - MusicFontSymbol.Ottava15, - MusicFontSymbol.OttavaMBaseline, - MusicFontSymbol.OttavaBBaseline, - }); - break; - } - - return size / 2; - } - - protected override void PaintGrouped(float cx, float cy, float endX, ICanvas canvas) - { - var size = PaintOttava(cx, cy, canvas); - - var lineSpacing = LineRangedGlyph.LineSpacing * Scale; - var startX = cx + X + size + lineSpacing; - var lineY = cy + Y; - lineY += _aboveStaff ? 2 * Scale : Height - 2 * Scale; - - var lineSize = LineRangedGlyph.LineSize * Scale; - - - if (endX > startX) - { - var lineX = startX; - while (lineX < endX) - { - canvas.BeginPath(); - canvas.MoveTo(lineX, (int)lineY); - canvas.LineTo(Math.Min(lineX + lineSize, endX), (int)lineY); - lineX += lineSize + lineSpacing; - canvas.Stroke(); - } - - canvas.BeginPath(); - if (_aboveStaff) - { - canvas.MoveTo(endX, lineY); - canvas.LineTo(endX, cy + Y + Height); - } - else - { - canvas.MoveTo(endX, lineY); - canvas.LineTo(endX, cy + Y); - } - - canvas.Stroke(); - } - } - - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Glyphs/PickStrokeGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/PickStrokeGlyph.cs deleted file mode 100644 index 58c4eb3fc..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/PickStrokeGlyph.cs +++ /dev/null @@ -1,51 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Model; -using AlphaTab.Platform; - -namespace AlphaTab.Rendering.Glyphs -{ - class PickStrokeGlyph : MusicFontGlyph - { - public PickStrokeGlyph(float x, float y, PickStroke pickStroke) - : base(x, y, 0.75f, GetSymbol(pickStroke)) - { - } - - public override void DoLayout() - { - Width = 9 * Scale; - Height = 10 * Scale; - } - - public override void Paint(float cx, float cy, ICanvas canvas) - { - base.Paint(cx, cy + Height, canvas); - } - - private static MusicFontSymbol GetSymbol(PickStroke pickStroke) - { - switch (pickStroke) - { - case PickStroke.Up: return MusicFontSymbol.PickStrokeUp; - case PickStroke.Down: return MusicFontSymbol.PickStrokeDown; - default: return MusicFontSymbol.None; - } - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/RepeatCloseGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/RepeatCloseGlyph.cs deleted file mode 100644 index a8ce1b9ff..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/RepeatCloseGlyph.cs +++ /dev/null @@ -1,63 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Platform; - -namespace AlphaTab.Rendering.Glyphs -{ - class RepeatCloseGlyph : Glyph - { - public RepeatCloseGlyph(float x, float y) - : base(x, y) - { - } - - public override void DoLayout() - { - Width = 11 * Scale; - } - - public override void Paint(float cx, float cy, ICanvas canvas) - { - var blockWidth = 4 * Scale; - - var top = cy + Y + Renderer.TopPadding; - var bottom = cy + Y + Renderer.Height - Renderer.BottomPadding; - var left = cx + X; - var h = bottom - top; - - //circles - var circleSize = 1.5f * Scale; - var middle = (top + bottom) / 2; - const int dotOffset = 3; - - canvas.FillCircle(left, middle - (circleSize * dotOffset), circleSize); - canvas.FillCircle(left, middle + (circleSize * dotOffset), circleSize); - - // line - left += (4 * Scale); - canvas.BeginPath(); - canvas.MoveTo(left, top); - canvas.LineTo(left, bottom); - canvas.Stroke(); - - // big bar - left += (3 * Scale) + 0.5f; - canvas.FillRect(left, top, blockWidth, h); - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/RepeatCountGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/RepeatCountGlyph.cs deleted file mode 100644 index f3ead4f06..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/RepeatCountGlyph.cs +++ /dev/null @@ -1,51 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Platform; -using AlphaTab.Platform.Model; - -namespace AlphaTab.Rendering.Glyphs -{ - class RepeatCountGlyph : Glyph - { - private readonly int _count; - - public RepeatCountGlyph(float x, float y, int count) - : base(x, y) - { - _count = count; - } - - public override void DoLayout() - { - Width = 0; - } - - public override void Paint(float cx, float cy, ICanvas canvas) - { - var res = Renderer.Resources; - var oldAlign = canvas.TextAlign; - canvas.Font = res.BarNumberFont; - canvas.TextAlign = TextAlign.Right; - var s = "x" + _count; - var w = canvas.MeasureText(s)/1.5f; - - canvas.FillText(s, cx + X - w, cy + Y); - canvas.TextAlign = oldAlign; - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Glyphs/RepeatOpenGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/RepeatOpenGlyph.cs deleted file mode 100644 index fbad84c3e..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/RepeatOpenGlyph.cs +++ /dev/null @@ -1,66 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Platform; - -namespace AlphaTab.Rendering.Glyphs -{ - class RepeatOpenGlyph : Glyph - { - private readonly float _dotOffset; - private readonly float _circleSize; - - public RepeatOpenGlyph(float x, float y, float circleSize, float dotOffset) - : base(x, y) - { - _dotOffset = dotOffset; - _circleSize = circleSize; - } - - public override void DoLayout() - { - Width = 13 * Scale; - } - - public override void Paint(float cx, float cy, ICanvas canvas) - { - var blockWidth = 4 * Scale; - - var top = cy + Y + Renderer.TopPadding; - var bottom = cy + Y + Renderer.Height - Renderer.BottomPadding; - var left = cx + X + 0.5f; - // big bar - var h = bottom - top; - canvas.FillRect(left, top, blockWidth, h); - - // line - left += (blockWidth * 2) - 0.5f; - canvas.BeginPath(); - canvas.MoveTo(left, top); - canvas.LineTo(left, bottom); - canvas.Stroke(); - - //circles - left += 3 * Scale; - - var circleSize = _circleSize * Scale; - var middle = (top + bottom) / 2; - canvas.FillCircle(left, middle - (circleSize * _dotOffset), circleSize); - canvas.FillCircle(left, middle + (circleSize * _dotOffset), circleSize); - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/RideCymbalGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/RideCymbalGlyph.cs deleted file mode 100644 index 1c3b126a4..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/RideCymbalGlyph.cs +++ /dev/null @@ -1,35 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Rendering.Glyphs -{ - class RideCymbalGlyph : MusicFontGlyph - { - private readonly bool _isGrace; - - public RideCymbalGlyph(float x, float y, bool isGrace) - : base(x, y, isGrace ? NoteHeadGlyph.GraceScale : 1, MusicFontSymbol.NoteHarmonicWhole) - { - _isGrace = isGrace; - } - - public override void DoLayout() - { - Width = 9 * (_isGrace ? NoteHeadGlyph.GraceScale : 1) * Scale; - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/ScoreBeatContainerGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/ScoreBeatContainerGlyph.cs deleted file mode 100644 index 470fb76db..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/ScoreBeatContainerGlyph.cs +++ /dev/null @@ -1,115 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Model; -using AlphaTab.Rendering.Glyphs; -using AlphaTab.Rendering.Staves; - -namespace AlphaTab.Rendering -{ - class ScoreBeatContainerGlyph : BeatContainerGlyph - { - private ScoreBendGlyph _bend; - - public ScoreBeatContainerGlyph(Beat beat, VoiceContainerGlyph voiceContainer) : base(beat, voiceContainer) - { - } - - public override void DoLayout() - { - base.DoLayout(); - if (Beat.IsLegatoOrigin) - { - // only create slur for very first origin of "group" - if (Beat.PreviousBeat == null || !Beat.PreviousBeat.IsLegatoOrigin) - { - // tie with end beat - Beat destination = Beat.NextBeat; - while (destination.NextBeat != null && destination.NextBeat.IsLegatoDestination) - { - destination = destination.NextBeat; - } - Ties.Add(new ScoreLegatoGlyph(Beat, destination)); - } - } - else if (Beat.IsLegatoDestination) - { - // only create slur for last destination of "group" - if (!Beat.IsLegatoOrigin) - { - Beat origin = Beat.PreviousBeat; - while (origin.PreviousBeat != null && origin.PreviousBeat.IsLegatoOrigin) - { - origin = origin.PreviousBeat; - } - Ties.Add(new ScoreLegatoGlyph(origin, Beat, true)); - } - } - if (_bend != null) - { - _bend.Renderer = Renderer; - _bend.DoLayout(); - UpdateWidth(); - } - } - - protected override void CreateTies(Note n) - { - // create a tie if any effect requires it - if (!n.IsVisible) return; - - // NOTE: we create 2 tie glyphs if we have a line break inbetween - // the two notes - if (n.IsTieOrigin && !n.HasBend && !n.Beat.HasWhammyBar && n.Beat.GraceType != GraceType.BendGrace && n.TieDestination.IsVisible) - { - var tie = new ScoreTieGlyph(n, n.TieDestination); - Ties.Add(tie); - } - - if (n.IsTieDestination && !n.TieOrigin.HasBend && !n.Beat.HasWhammyBar) - { - var tie = new ScoreTieGlyph(n.TieOrigin, n, true); - Ties.Add(tie); - } - - // TODO: depending on the type we have other positioning - // we should place glyphs in the preNotesGlyph or postNotesGlyph if needed - if (n.SlideType != SlideType.None) - { - var l = new ScoreSlideLineGlyph(n.SlideType, n, this); - Ties.Add(l); - } - - if (n.Beat.SlurOrigin != null && n.Index == 0) - { - var tie = new ScoreSlurGlyph(n.Beat); - Ties.Add(tie); - } - - if (n.HasBend) - { - if (_bend == null) - { - _bend = new ScoreBendGlyph(n.Beat); - _bend.Renderer = Renderer; - Ties.Add(_bend); - } - _bend.AddBends(n); - } - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/ScoreBeatGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/ScoreBeatGlyph.cs deleted file mode 100644 index 065e019b2..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/ScoreBeatGlyph.cs +++ /dev/null @@ -1,316 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Collections; -using AlphaTab.Model; - -namespace AlphaTab.Rendering.Glyphs -{ - class ScoreBeatGlyph : BeatOnNoteGlyphBase - { - public ScoreNoteChordGlyph NoteHeads { get; set; } - public ScoreRestGlyph RestGlyph { get; set; } - - public override void UpdateBeamingHelper() - { - if (NoteHeads != null) - { - NoteHeads.UpdateBeamingHelper(Container.X + X); - } - else if (RestGlyph != null) - { - RestGlyph.UpdateBeamingHelper(Container.X + X); - } - } - - public override void DoLayout() - { - // create glyphs - var sr = (ScoreBarRenderer)Renderer; - if (!Container.Beat.IsEmpty) - { - if (!Container.Beat.IsRest) - { - // - // Note heads - // - NoteHeads = new ScoreNoteChordGlyph(); - NoteHeads.Beat = Container.Beat; - NoteHeads.BeamingHelper = BeamingHelper; - - var ghost = new GhostNoteContainerGlyph(false); - ghost.Renderer = Renderer; - - foreach (var note in Container.Beat.Notes) - { - if (note.IsVisible) - { - CreateNoteGlyph(note); - ghost.AddParenthesis(note); - } - } - AddGlyph(NoteHeads); - - if (!ghost.IsEmpty) - { - AddGlyph(new SpacingGlyph(0, 0, 4 * (Container.Beat.GraceType != GraceType.None ? NoteHeadGlyph.GraceScale : 1) * Scale)); - AddGlyph(ghost); - } - - // - // Whammy Bar - if (Container.Beat.HasWhammyBar) - { - var whammy = new ScoreWhammyBarGlyph(Container.Beat); - whammy.Renderer = Renderer; - whammy.DoLayout(); - - Container.Ties.Add(whammy); - } - - // - // Note dots - // - if (Container.Beat.Dots > 0) - { - AddGlyph(new SpacingGlyph(0, 0, 5 * Scale)); - for (var i = 0; i < Container.Beat.Dots; i++) - { - var group = new GlyphGroup(0, 0); - foreach (var note in Container.Beat.Notes) - { - CreateBeatDot(sr.GetNoteLine(note), group); - } - AddGlyph(group); - } - } - } - else - { - var dotLine = 0; - var line = 0; - var offset = 0; - - switch (Container.Beat.Duration) - { - case Duration.QuadrupleWhole: - line = 6; - dotLine = 5; - break; - case Duration.DoubleWhole: - line = 6; - dotLine = 5; - break; - case Duration.Whole: - line = 4; - dotLine = 5; - break; - case Duration.Half: - line = 6; - dotLine = 5; - break; - case Duration.Quarter: - line = 6; - offset = -2; - dotLine = 5; - break; - case Duration.Eighth: - line = 6; - dotLine = 5; - break; - case Duration.Sixteenth: - line = 6; - dotLine = 5; - break; - case Duration.ThirtySecond: - line = 6; - dotLine = 3; - break; - case Duration.SixtyFourth: - line = 6; - dotLine = 3; - break; - case Duration.OneHundredTwentyEighth: - line = 6; - dotLine = 3; - break; - case Duration.TwoHundredFiftySixth: - line = 6; - dotLine = 3; - break; - } - - var y = sr.GetScoreY(line, offset); - - RestGlyph = new ScoreRestGlyph(0, y, Container.Beat.Duration); - RestGlyph.Beat = Container.Beat; - RestGlyph.BeamingHelper = BeamingHelper; - AddGlyph(RestGlyph); - - // - // Note dots - // - if (Container.Beat.Dots > 0) - { - AddGlyph(new SpacingGlyph(0, 0, 5 * Scale)); - for (var i = 0; i < Container.Beat.Dots; i++) - { - var group = new GlyphGroup(0, 0); - CreateBeatDot(dotLine, group); - AddGlyph(group); - } - } - } - } - - base.DoLayout(); - - if (Container.Beat.IsEmpty) - { - CenterX = Width / 2; - } - else if (Container.Beat.IsRest) - { - CenterX = RestGlyph.X + RestGlyph.Width / 2; - } - else - { - CenterX = NoteHeads.X + NoteHeads.Width / 2; - } - } - - private void CreateBeatDot(int line, GlyphGroup group) - { - var sr = (ScoreBarRenderer)Renderer; - group.AddGlyph(new CircleGlyph(0, sr.GetScoreY(line), 1.5f * Scale)); - } - - private static readonly FastDictionary NormalKeys; - private static readonly FastDictionary XKeys; - - static ScoreBeatGlyph() - { - // ReSharper disable ForCanBeConvertedToForeach - NormalKeys = new FastDictionary(); - var normalKeyNotes = new[] { 32, 34, 35, 36, 38, 39, 40, 41, 43, 45, 47, 48, 50, 55, 56, 58, 60, 61 }; - for (int i = 0; i < normalKeyNotes.Length; i++) - { - NormalKeys[normalKeyNotes[i]] = true; - } - XKeys = new FastDictionary(); - var xKeyNotes = new[] { 31, 33, 37, 42, 44, 54, 62, 63, 64, 65, 66 }; - for (int i = 0; i < xKeyNotes.Length; i++) - { - XKeys[xKeyNotes[i]] = true; - } - // ReSharper restore ForCanBeConvertedToForeach - } - - private EffectGlyph CreateNoteHeadGlyph(Note n) - { - var isGrace = Container.Beat.GraceType != GraceType.None; - if (n.Beat.Voice.Bar.Staff.StaffKind == StaffKind.Percussion) - { - var value = n.RealValue; - - if (value <= 30 || value >= 67 || NormalKeys.ContainsKey(value)) - { - return new NoteHeadGlyph(0, 0, Duration.Quarter, isGrace); - } - if (XKeys.ContainsKey(value)) - { - return new DrumSticksGlyph(0, 0, isGrace); - } - if (value == 46) - { - return new HiHatGlyph(0, 0, isGrace); - } - if (value == 49 || value == 57) - { - return new DiamondNoteHeadGlyph(0, 0, n.Beat.Duration, isGrace); - } - if (value == 52) - { - return new ChineseCymbalGlyph(0, 0, isGrace); - } - if (value == 51 || value == 53 || value == 59) - { - return new RideCymbalGlyph(0, 0, isGrace); - } - return new NoteHeadGlyph(0, 0, Duration.Quarter, isGrace); - } - if (n.IsDead) - { - return new DeadNoteHeadGlyph(0, 0, isGrace); - } - if (n.Beat.GraceType == GraceType.BendGrace) - { - return new NoteHeadGlyph(0, 0, Duration.Quarter, true); - } - - if (n.HarmonicType == HarmonicType.Natural) - { - return new DiamondNoteHeadGlyph(0, 0, n.Beat.Duration, isGrace); - } - - return new NoteHeadGlyph(0, 0, n.Beat.Duration, isGrace); - } - - private void CreateNoteGlyph(Note n) - { - if (n.Beat.GraceType == GraceType.BendGrace && !n.HasBend) - { - return; - } - - var sr = (ScoreBarRenderer)Renderer; - var noteHeadGlyph = CreateNoteHeadGlyph(n); - - // calculate y position - var line = sr.GetNoteLine(n); - - noteHeadGlyph.Y = sr.GetScoreY(line); - NoteHeads.AddNoteGlyph(noteHeadGlyph, n, line); - - if (n.HarmonicType != HarmonicType.None && n.HarmonicType != HarmonicType.Natural) - { - // create harmonic note head. - var harmonicFret = n.DisplayValue + n.HarmonicPitch; - noteHeadGlyph = new DiamondNoteHeadGlyph(0, 0, n.Beat.Duration, Container.Beat.GraceType != GraceType.None); - line = sr.AccidentalHelper.GetNoteLineForValue(harmonicFret); - - noteHeadGlyph.Y = sr.GetScoreY(line); - NoteHeads.AddNoteGlyph(noteHeadGlyph, n, line); - } - - if (n.IsStaccato && !NoteHeads.BeatEffects.ContainsKey("Staccato")) - { - NoteHeads.BeatEffects["Staccato"] = new CircleGlyph(0, 0, 1.5f); - } - - if (n.Accentuated == AccentuationType.Normal && !NoteHeads.BeatEffects.ContainsKey("Accent")) - { - NoteHeads.BeatEffects["Accent"] = new AccentuationGlyph(0, 0, AccentuationType.Normal); - } - if (n.Accentuated == AccentuationType.Heavy && !NoteHeads.BeatEffects.ContainsKey("HAccent")) - { - NoteHeads.BeatEffects["HAccent"] = new AccentuationGlyph(0, 0, AccentuationType.Heavy); - } - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/ScoreBeatPreNotesGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/ScoreBeatPreNotesGlyph.cs deleted file mode 100644 index 5ec9653d1..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/ScoreBeatPreNotesGlyph.cs +++ /dev/null @@ -1,115 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Model; - -namespace AlphaTab.Rendering.Glyphs -{ - class ScoreBeatPreNotesGlyph : BeatGlyphBase - { - private BendNoteHeadGroupGlyph _prebends; - public float PrebendNoteHeadOffset => _prebends.X + _prebends.NoteHeadOffset; - - public override void DoLayout() - { - if (!Container.Beat.IsRest) - { - var accidentals = new AccidentalGroupGlyph(); - var ghost = new GhostNoteContainerGlyph(true); - ghost.Renderer = Renderer; - _prebends = new BendNoteHeadGroupGlyph(Container.Beat, true); - _prebends.Renderer = Renderer; - foreach (var note in Container.Beat.Notes) - { - if (note.IsVisible) - { - if (note.HasBend) - { - switch (note.BendType) - { - case BendType.PrebendBend: - case BendType.Prebend: - case BendType.PrebendRelease: - _prebends.AddGlyph(note.DisplayValue - note.BendPoints[0].Value / 2); - break; - } - } - else if (note.Beat.HasWhammyBar) - { - switch (note.Beat.WhammyBarType) - { - case WhammyType.PrediveDive: - case WhammyType.Predive: - _prebends.AddGlyph(note.DisplayValue - note.Beat.WhammyBarPoints[0].Value / 2); - break; - } - } - - CreateAccidentalGlyph(note, accidentals); - ghost.AddParenthesis(note); - } - } - - if (!_prebends.IsEmpty) - { - AddGlyph(_prebends); - AddGlyph(new SpacingGlyph(0, 0, 4 * (Container.Beat.GraceType != GraceType.None ? NoteHeadGlyph.GraceScale : 1) * Scale)); - } - - if (Container.Beat.BrushType != BrushType.None) - { - AddGlyph(new ScoreBrushGlyph(Container.Beat)); - AddGlyph(new SpacingGlyph(0, 0, 4 * Scale)); - } - - if (!ghost.IsEmpty) - { - AddGlyph(ghost); - AddGlyph(new SpacingGlyph(0, 0, 4 * (Container.Beat.GraceType != GraceType.None ? NoteHeadGlyph.GraceScale : 1) * Scale)); - } - - if (!accidentals.IsEmpty) - { - AddGlyph(accidentals); - AddGlyph(new SpacingGlyph(0, 0, 4 * (Container.Beat.GraceType != GraceType.None ? NoteHeadGlyph.GraceScale : 1) * Scale)); - } - } - - base.DoLayout(); - } - - private void CreateAccidentalGlyph(Note n, AccidentalGroupGlyph accidentals) - { - var sr = (ScoreBarRenderer)Renderer; - var accidental = sr.AccidentalHelper.ApplyAccidental(n); - var noteLine = sr.GetNoteLine(n); - var isGrace = Container.Beat.GraceType != GraceType.None; - - if (accidental != AccidentalType.None) - { - accidentals.AddGlyph(new AccidentalGlyph(0, sr.GetScoreY(noteLine), accidental, isGrace)); - } - if (n.HarmonicType != HarmonicType.None && n.HarmonicType != HarmonicType.Natural) - { - var harmonicFret = n.DisplayValue + n.HarmonicPitch; - accidental = sr.AccidentalHelper.ApplyAccidentalForValue(n.Beat, harmonicFret, isGrace); - noteLine = sr.AccidentalHelper.GetNoteLineForValue(harmonicFret); - accidentals.AddGlyph(new AccidentalGlyph(0, sr.GetScoreY(noteLine), accidental, isGrace)); - } - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/ScoreBendGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/ScoreBendGlyph.cs deleted file mode 100644 index 355167cbe..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/ScoreBendGlyph.cs +++ /dev/null @@ -1,300 +0,0 @@ -using AlphaTab.Collections; -using AlphaTab.Model; -using AlphaTab.Platform; -using AlphaTab.Platform.Model; -using AlphaTab.Rendering.Utils; - -namespace AlphaTab.Rendering.Glyphs -{ - class ScoreBendGlyph : ScoreHelperNotesBaseGlyph - { - private readonly Beat _beat; - private FastList _notes; - private BendNoteHeadGroupGlyph _endNoteGlyph; - private BendNoteHeadGroupGlyph _middleNoteGlyph; - - public ScoreBendGlyph(Beat beat) - { - _beat = beat; - _notes = new FastList(); - _middleNoteGlyph = null; - _endNoteGlyph = null; - } - - public void AddBends(Note note) - { - _notes.Add(note); - if (note.IsTieOrigin) - { - return; - } - - switch (note.BendType) - { - case BendType.Bend: - case BendType.PrebendRelease: - case BendType.PrebendBend: - { - BendNoteHeadGroupGlyph endGlyphs = _endNoteGlyph; - if (endGlyphs == null) - { - endGlyphs = _endNoteGlyph = new BendNoteHeadGroupGlyph(note.Beat); - endGlyphs.Renderer = Renderer; - _bendNoteHeads.Add(endGlyphs); - } - - var lastBendPoint = note.BendPoints[note.BendPoints.Count - 1]; - endGlyphs.AddGlyph(GetBendNoteValue(note, lastBendPoint), (lastBendPoint.Value % 2) != 0); - } - - break; - case BendType.Release: - { - if (!note.IsTieOrigin) - { - BendNoteHeadGroupGlyph endGlyphs = _endNoteGlyph; - if (endGlyphs == null) - { - endGlyphs = _endNoteGlyph = new BendNoteHeadGroupGlyph(note.Beat); - endGlyphs.Renderer = Renderer; - _bendNoteHeads.Add(endGlyphs); - } - - var lastBendPoint = note.BendPoints[note.BendPoints.Count - 1]; - endGlyphs.AddGlyph(GetBendNoteValue(note, lastBendPoint), (lastBendPoint.Value % 2) != 0); - } - } - - break; - case BendType.BendRelease: - { - BendNoteHeadGroupGlyph middleGlyphs = _middleNoteGlyph; - if (middleGlyphs == null) - { - middleGlyphs = _middleNoteGlyph = new BendNoteHeadGroupGlyph(note.Beat); - middleGlyphs.Renderer = Renderer; - _bendNoteHeads.Add(middleGlyphs); - } - var middleBendPoint = note.BendPoints[1]; - middleGlyphs.AddGlyph(GetBendNoteValue(note, note.BendPoints[1]), (middleBendPoint.Value % 2) != 0); - - BendNoteHeadGroupGlyph endGlyphs = _endNoteGlyph; - if (endGlyphs == null) - { - endGlyphs = _endNoteGlyph = new BendNoteHeadGroupGlyph(note.Beat); - endGlyphs.Renderer = Renderer; - _bendNoteHeads.Add(endGlyphs); - } - var lastBendPoint = note.BendPoints[note.BendPoints.Count - 1]; - endGlyphs.AddGlyph(GetBendNoteValue(note, lastBendPoint), (lastBendPoint.Value % 2) != 0); - } - - break; - } - } - - public override void Paint(float cx, float cy, ICanvas canvas) - { - // Draw note heads - var startNoteRenderer = Renderer.ScoreRenderer.Layout.GetRendererForBar(Renderer.Staff.StaveId, _beat.Voice.Bar); - var startX = cx + startNoteRenderer.X + startNoteRenderer.GetBeatX(_beat, BeatXPosition.MiddleNotes); - var endBeatX = cx + startNoteRenderer.X; - if (_beat.IsLastOfVoice) - { - endBeatX += startNoteRenderer.PostBeatGlyphsStart; - } - else - { - endBeatX += startNoteRenderer.GetBeatX(_beat.NextBeat, BeatXPosition.PreNotes); - } - endBeatX -= EndPadding * Scale; - - var middleX = (startX + endBeatX) / 2; - - if (_middleNoteGlyph != null) - { - _middleNoteGlyph.X = middleX - _middleNoteGlyph.NoteHeadOffset; - _middleNoteGlyph.Y = cy + startNoteRenderer.Y; - _middleNoteGlyph.Paint(0, 0, canvas); - } - - if (_endNoteGlyph != null) - { - _endNoteGlyph.X = endBeatX - _endNoteGlyph.NoteHeadOffset; - _endNoteGlyph.Y = cy + startNoteRenderer.Y; - _endNoteGlyph.Paint(0, 0, canvas); - } - - _notes.Sort((a, b) => b.DisplayValue - a.DisplayValue); - - var directionBeat = _beat.GraceType == GraceType.BendGrace ? _beat.NextBeat : _beat; - var direction = _notes.Count == 1 ? GetBeamDirection(directionBeat, startNoteRenderer) : BeamDirection.Up; - - // draw slurs - for (var i = 0; i < _notes.Count; i++) - { - var note = _notes[i]; - if (i > 0 && i >= _notes.Count / 2) - { - direction = BeamDirection.Down; - } - - var startY = cy + startNoteRenderer.Y + startNoteRenderer.GetNoteY(note, true); - var heightOffset = (NoteHeadGlyph.NoteHeadHeight * Scale * NoteHeadGlyph.GraceScale) * 0.5f; - if (direction == BeamDirection.Down) - { - startY += NoteHeadGlyph.NoteHeadHeight * Scale; - } - - var slurText = note.BendStyle == BendStyle.Gradual ? "grad." : ""; - - if (note.IsTieOrigin) - { - var endNote = note.TieDestination; - var endNoteRenderer = endNote == null - ? null - : Renderer.ScoreRenderer.Layout.GetRendererForBar(Renderer.Staff.StaveId, - endNote.Beat.Voice.Bar); - - // if we have a line break we draw only a line until the end - if (endNoteRenderer == null || endNoteRenderer.Staff != startNoteRenderer.Staff) - { - var endX = cx + startNoteRenderer.X + startNoteRenderer.Width; - var noteValueToDraw = note.TieDestination.RealValue; - - var accidental = - startNoteRenderer.AccidentalHelper.ApplyAccidentalForValue(note.Beat, noteValueToDraw, false); - var endY = cy + startNoteRenderer.Y + - startNoteRenderer.GetScoreY( - startNoteRenderer.AccidentalHelper.GetNoteLineForValue(noteValueToDraw)); - - if (note.BendType == BendType.Hold || note.BendType == BendType.Prebend) - { - TieGlyph.PaintTie(canvas, Scale, startX, startY, endX, endY, - direction == BeamDirection.Down); - canvas.Fill(); - } - else - { - DrawBendSlur(canvas, startX, startY, endX, endY, direction == BeamDirection.Down, Scale, - slurText); - } - } - // otherwise we draw a line to the target note - else - { - var endX = cx + endNoteRenderer.X + - endNoteRenderer.GetBeatX(endNote.Beat, BeatXPosition.MiddleNotes); - var endY = cy + endNoteRenderer.Y + endNoteRenderer.GetNoteY(endNote, true); - if (direction == BeamDirection.Down) - { - endY += NoteHeadGlyph.NoteHeadHeight * Scale; - } - - if (note.BendType == BendType.Hold || note.BendType == BendType.Prebend) - { - TieGlyph.PaintTie(canvas, Scale, startX, startY, endX, endY, - direction == BeamDirection.Down); - canvas.Fill(); - } - else - { - DrawBendSlur(canvas, startX, startY, endX, endY, direction == BeamDirection.Down, Scale, - slurText); - } - } - - switch (note.BendType) - { - case BendType.Prebend: - case BendType.PrebendBend: - case BendType.PrebendRelease: - var preX = cx + startNoteRenderer.X + - startNoteRenderer.GetBeatX(note.Beat, BeatXPosition.PreNotes); - preX += ((ScoreBeatPreNotesGlyph)startNoteRenderer.GetBeatContainer(note.Beat).PreNotes) - .PrebendNoteHeadOffset; - - var preY = cy + startNoteRenderer.Y + - startNoteRenderer.GetScoreY( - startNoteRenderer.AccidentalHelper.GetNoteLineForValue(note.DisplayValue - note.BendPoints[0].Value / 2)) + - heightOffset; - - DrawBendSlur(canvas, preX, preY, startX, startY, direction == BeamDirection.Down, Scale); - break; - } - } - else - { - if (direction == BeamDirection.Up) heightOffset = -heightOffset; - int endValue; - float endY; - - switch (note.BendType) - { - case BendType.Bend: - endValue = GetBendNoteValue(note, note.BendPoints[note.BendPoints.Count - 1]); - endY = _endNoteGlyph.GetNoteValueY(endValue) + heightOffset; - DrawBendSlur(canvas, startX, startY, endBeatX, endY, direction == BeamDirection.Down, Scale, - slurText); - - break; - case BendType.BendRelease: - var middleValue = GetBendNoteValue(note, note.BendPoints[1]); - var middleY = _middleNoteGlyph.GetNoteValueY(middleValue) + heightOffset; - DrawBendSlur(canvas, startX, startY, middleX, middleY, direction == BeamDirection.Down, - Scale, slurText); - - endValue = GetBendNoteValue(note, note.BendPoints[note.BendPoints.Count - 1]); - endY = _endNoteGlyph.GetNoteValueY(endValue) + heightOffset; - DrawBendSlur(canvas, middleX, middleY, endBeatX, endY, direction == BeamDirection.Down, - Scale, slurText); - - break; - case BendType.Release: - if (_bendNoteHeads.Count > 0) - { - endValue = GetBendNoteValue(note, note.BendPoints[note.BendPoints.Count - 1]); - endY = _bendNoteHeads[0].GetNoteValueY(endValue) + heightOffset; - DrawBendSlur(canvas, startX, startY, endBeatX, endY, direction == BeamDirection.Down, - Scale, slurText); - } - - break; - case BendType.Prebend: - case BendType.PrebendBend: - case BendType.PrebendRelease: - - var preX = cx + startNoteRenderer.X + - startNoteRenderer.GetBeatX(note.Beat, BeatXPosition.PreNotes); - preX += ((ScoreBeatPreNotesGlyph)startNoteRenderer.GetBeatContainer(note.Beat).PreNotes) - .PrebendNoteHeadOffset; - - var preY = cy + startNoteRenderer.Y + - startNoteRenderer.GetScoreY( - startNoteRenderer.AccidentalHelper.GetNoteLineForValue(note.DisplayValue - note.BendPoints[0].Value / 2)) + - heightOffset; - - DrawBendSlur(canvas, preX, preY, startX, startY, direction == BeamDirection.Down, Scale); - - if (_bendNoteHeads.Count > 0) - { - endValue = GetBendNoteValue(note, note.BendPoints[note.BendPoints.Count - 1]); - endY = _bendNoteHeads[0].GetNoteValueY(endValue) + heightOffset; - DrawBendSlur(canvas, startX, startY, endBeatX, endY, direction == BeamDirection.Down, - Scale, slurText); - } - - break; - } - } - } - } - - private int GetBendNoteValue(Note note, BendPoint bendPoint) - { - // NOTE: bendpoints are in 1/4 tones, but the note values are in 1/2 notes. - return note.DisplayValueWithoutBend + bendPoint.Value / 2; - } - - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Glyphs/ScoreBrushGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/ScoreBrushGlyph.cs deleted file mode 100644 index 5150a39d5..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/ScoreBrushGlyph.cs +++ /dev/null @@ -1,96 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using AlphaTab.Model; -using AlphaTab.Platform; - -namespace AlphaTab.Rendering.Glyphs -{ - class ScoreBrushGlyph : Glyph - { - private readonly Beat _beat; - - public ScoreBrushGlyph(Beat beat) - : base(0, 0) - { - _beat = beat; - } - - public override void DoLayout() - { - Width = 10 * Scale; - } - - public override void Paint(float cx, float cy, ICanvas canvas) - { - //TODO: Create webfont version - - var scoreBarRenderer = (ScoreBarRenderer)Renderer; - var lineSize = scoreBarRenderer.LineOffset; - var startY = cy + Y + (scoreBarRenderer.GetNoteY(_beat.MaxNote) - lineSize); - var endY = cy + Y + scoreBarRenderer.GetNoteY(_beat.MinNote) + lineSize; - var arrowX = cx + X + Width / 2; - var arrowSize = 8 * Scale; - - if (_beat.BrushType != BrushType.None) - { - if (_beat.BrushType == BrushType.ArpeggioUp) - { - var lineStartY = startY - arrowSize; - var lineEndY = endY - arrowSize; - - canvas.BeginRotate(cx + X + 2 * Scale, lineEndY, -90); - var glyph = new NoteVibratoGlyph(0, 0, VibratoType.Slight); - glyph.Renderer = Renderer; - glyph.DoLayout(); - glyph.Width = Math.Abs(lineEndY - lineStartY); - glyph.Paint(0, 0, canvas); - canvas.EndRotate(); - - canvas.BeginPath(); - canvas.MoveTo(arrowX, endY); - canvas.LineTo(arrowX + arrowSize / 2, endY - arrowSize); - canvas.LineTo(arrowX - arrowSize / 2, endY - arrowSize); - canvas.ClosePath(); - canvas.Fill(); - } - else if (_beat.BrushType == BrushType.ArpeggioDown) - { - var lineStartY = startY + arrowSize; - var lineEndY = endY + arrowSize; - - canvas.BeginRotate(cx + X + 7 * Scale, lineStartY, 90); - var glyph = new NoteVibratoGlyph(0, 0, VibratoType.Slight); - glyph.Renderer = Renderer; - glyph.DoLayout(); - glyph.Width = Math.Abs(lineEndY - lineStartY); - glyph.Paint(0, 0, canvas); - canvas.EndRotate(); - - canvas.BeginPath(); - canvas.MoveTo(arrowX, startY); - canvas.LineTo(arrowX + arrowSize / 2, startY + arrowSize); - canvas.LineTo(arrowX - arrowSize / 2, startY + arrowSize); - canvas.ClosePath(); - canvas.Fill(); - } - } - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/ScoreHelperNotesBaseGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/ScoreHelperNotesBaseGlyph.cs deleted file mode 100644 index 466ddf238..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/ScoreHelperNotesBaseGlyph.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System; -using AlphaTab.Collections; -using AlphaTab.Model; -using AlphaTab.Platform; -using AlphaTab.Rendering.Utils; - -namespace AlphaTab.Rendering.Glyphs -{ - class ScoreHelperNotesBaseGlyph : Glyph - { - private const int SlurHeight = 11; - public const int EndPadding = (NoteHeadGlyph.QuarterNoteHeadWidth / 2) + 3; - protected FastList _bendNoteHeads; - - public ScoreHelperNotesBaseGlyph() - : base(0, 0) - { - _bendNoteHeads = new FastList(); - } - - protected void DrawBendSlur(ICanvas canvas, float x1, float y1, float x2, float y2, bool down, float scale, string slurText = null) - { - var normalVectorX = (y2 - y1); - var normalVectorY = (x2 - x1); - var length = (float)Math.Sqrt((normalVectorX * normalVectorX) + (normalVectorY * normalVectorY)); - if (down) - normalVectorX *= -1; - else - normalVectorY *= -1; - - // make to unit vector - normalVectorX /= length; - normalVectorY /= length; - - // center of connection - // TODO: should be 1/3 - var centerX = (x2 + x1) / 2; - var centerY = (y2 + y1) / 2; - - var offset = SlurHeight * scale; - if (x2 - x1 < 20) - { - offset /= 2; - } - var cp1X = centerX + (offset * normalVectorX); - var cp1Y = centerY + (offset * normalVectorY); - - canvas.BeginPath(); - - canvas.MoveTo(x1, y1); - canvas.LineTo(cp1X, cp1Y); - canvas.LineTo(x2, y2); - - canvas.Stroke(); - - if (!string.IsNullOrEmpty(slurText)) - { - var w = canvas.MeasureText(slurText); - var textOffset = down ? 0 : -canvas.Font.Size; - canvas.FillText(slurText, cp1X - w / 2, cp1Y + textOffset); - } - } - - public override void DoLayout() - { - base.DoLayout(); - - Width = 0; - foreach (var noteHeads in _bendNoteHeads) - { - noteHeads.DoLayout(); - Width += noteHeads.Width + 10 * Scale; - } - } - - protected BeamDirection GetBeamDirection(Beat beat, ScoreBarRenderer noteRenderer) - { - // invert direction (if stems go up, ties go down to not cross them) - switch (noteRenderer.GetBeatDirection(beat)) - { - case BeamDirection.Up: - return BeamDirection.Down; - case BeamDirection.Down: - default: - return BeamDirection.Up; - } - } - - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Glyphs/ScoreLegatoGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/ScoreLegatoGlyph.cs deleted file mode 100644 index cc10c2ba2..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/ScoreLegatoGlyph.cs +++ /dev/null @@ -1,120 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Model; -using AlphaTab.Rendering.Utils; - -namespace AlphaTab.Rendering.Glyphs -{ - class ScoreLegatoGlyph : TieGlyph - { - public ScoreLegatoGlyph(Beat startBeat, Beat endBeat, bool forEnd = false) - : base(startBeat, endBeat, forEnd) - { - } - - public override void DoLayout() - { - base.DoLayout(); - YOffset = (NoteHeadGlyph.NoteHeadHeight/2); - } - - protected override BeamDirection GetBeamDirection(Beat beat, BarRendererBase noteRenderer) - { - if (beat.IsRest) - { - return BeamDirection.Up; - } - - // invert direction (if stems go up, ties go down to not cross them) - switch (((ScoreBarRenderer)noteRenderer).GetBeatDirection(beat)) - { - case BeamDirection.Up: - return BeamDirection.Down; - case BeamDirection.Down: - default: - return BeamDirection.Up; - } - } - - protected override float GetStartY(BarRendererBase noteRenderer, BeamDirection direction) - { - if (StartBeat.IsRest) - { - // below all lines - return ((ScoreBarRenderer)noteRenderer).GetScoreY(9); - } - - switch (direction) - { - case BeamDirection.Up: - // below lowest note - return noteRenderer.GetNoteY(StartBeat.MinNote); - default: - return noteRenderer.GetNoteY(StartBeat.MaxNote); - } - } - - protected override float GetEndY(BarRendererBase noteRenderer, BeamDirection direction) - { - if (EndBeat.IsRest) - { - switch (direction) - { - case BeamDirection.Up: - return ((ScoreBarRenderer)noteRenderer).GetScoreY(9); - default: - return ((ScoreBarRenderer)noteRenderer).GetScoreY(0); - } - } - - switch (direction) - { - case BeamDirection.Up: - // below lowest note - return ((ScoreBarRenderer)noteRenderer).GetNoteY(EndBeat.MinNote); - default: - return ((ScoreBarRenderer)noteRenderer).GetNoteY(EndBeat.MaxNote); - } - } - - protected override float GetStartX(BarRendererBase noteRenderer) - { - if (StartBeat.IsRest) - { - return noteRenderer.GetBeatX(StartBeat); - } - else - { - return noteRenderer.GetNoteX(StartBeat.MinNote); - } - } - - protected override float GetEndX(BarRendererBase noteRenderer) - { - if (EndBeat.IsRest) - { - return noteRenderer.GetBeatX(EndBeat); - } - else - { - return noteRenderer.GetNoteX(EndBeat.MinNote, false); - } - } - - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Glyphs/ScoreNoteChordGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/ScoreNoteChordGlyph.cs deleted file mode 100644 index 74b27b16d..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/ScoreNoteChordGlyph.cs +++ /dev/null @@ -1,186 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.Collections; -using AlphaTab.Model; -using AlphaTab.Platform; -using AlphaTab.Platform.Model; -using AlphaTab.Rendering.Utils; - -namespace AlphaTab.Rendering.Glyphs -{ - class ScoreNoteChordGlyph : ScoreNoteChordGlyphBase - { - private readonly FastDictionary _noteGlyphLookup; - private readonly FastList _notes; - private Glyph _tremoloPicking; - - public FastDictionary BeatEffects { get; set; } - - public Beat Beat { get; set; } - public BeamingHelper BeamingHelper { get; set; } - - public ScoreNoteChordGlyph() - { - BeatEffects = new FastDictionary(); - _noteGlyphLookup = new FastDictionary(); - _notes = new FastList(); - } - - public override BeamDirection Direction - { - get - { - return BeamingHelper.Direction; - } - } - - public float GetNoteX(Note note, bool onEnd = true) - { - if (_noteGlyphLookup.ContainsKey(note.Id)) - { - var n = _noteGlyphLookup[note.Id]; - var pos = X + n.X; - if (onEnd) - { - pos += n.Width; - } - return pos; - } - return 0; - } - - public float GetNoteY(Note note, bool aboveNote = false) - { - if (_noteGlyphLookup.ContainsKey(note.Id)) - { - return Y + _noteGlyphLookup[note.Id].Y + (aboveNote ? -(NoteHeadGlyph.NoteHeadHeight * Scale) / 2 : 0); - } - return 0; - } - - public void AddNoteGlyph(EffectGlyph noteGlyph, Note note, int noteLine) - { - base.Add(noteGlyph, noteLine); - _noteGlyphLookup[note.Id] = noteGlyph; - _notes.Add(note); - } - - public void UpdateBeamingHelper(float cx) - { - if (BeamingHelper != null) - { - BeamingHelper.RegisterBeatLineX(ScoreBarRenderer.StaffId, Beat, cx + X + UpLineX, cx + X + DownLineX); - } - } - - public override void DoLayout() - { - base.DoLayout(); - - var direction = Direction; - foreach (var effectKey in BeatEffects) - { - var effect = BeatEffects[effectKey]; - effect.Renderer = Renderer; - effect.DoLayout(); - } - - if (Beat.IsTremolo) - { - int offset; - var baseNote = direction == BeamDirection.Up ? MinNote : MaxNote; - var tremoloX = direction == BeamDirection.Up ? DisplacedX : 0; - var speed = Beat.TremoloSpeed.Value; - switch (speed) - { - case Duration.ThirtySecond: - offset = direction == BeamDirection.Up ? -15 : 15; - break; - case Duration.Sixteenth: - offset = direction == BeamDirection.Up ? -12 : 15; - break; - case Duration.Eighth: - offset = direction == BeamDirection.Up ? -10 : 10; - break; - default: - offset = direction == BeamDirection.Up ? -10 : 15; - break; - } - - _tremoloPicking = new TremoloPickingGlyph(tremoloX, baseNote.Glyph.Y + offset * Scale, Beat.TremoloSpeed.Value); - _tremoloPicking.Renderer = Renderer; - _tremoloPicking.DoLayout(); - } - } - - public override void Paint(float cx, float cy, ICanvas canvas) - { - // TODO: this method seems to be quite heavy according to the profiler, why? - var scoreRenderer = (ScoreBarRenderer)Renderer; - - // - // Note Effects only painted once - // - var effectY = BeamingHelper.Direction == BeamDirection.Up - ? scoreRenderer.GetScoreY(MaxNote.Line, 1.5f * NoteHeadGlyph.NoteHeadHeight) - : scoreRenderer.GetScoreY(MinNote.Line, -1.0f * NoteHeadGlyph.NoteHeadHeight); - // TODO: take care of actual glyph height - var effectSpacing = (BeamingHelper.Direction == BeamDirection.Up) - ? 7 * Scale - : -7 * Scale; - - foreach (var effectKey in BeatEffects) - { - var g = BeatEffects[effectKey]; - g.Y = effectY; - g.X = Width / 2; - g.Paint(cx + X, cy + Y, canvas); - effectY += effectSpacing; - } - - if (Renderer.Settings.IncludeNoteBounds) - { - foreach (var note in _notes) - { - if (_noteGlyphLookup.ContainsKey(note.Id)) - { - var glyph = _noteGlyphLookup[note.Id]; - var noteBounds = new NoteBounds(); - noteBounds.Note = note; - noteBounds.NoteHeadBounds = new Bounds - { - X = cx + X + glyph.X, - Y = cy + Y + glyph.Y, - W = glyph.Width, - H = glyph.Height - }; - Renderer.ScoreRenderer.BoundsLookup.AddNote(noteBounds); - } - } - } - - base.Paint(cx, cy, canvas); - - if (_tremoloPicking != null) - { - _tremoloPicking.Paint(cx, cy, canvas); - } - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/ScoreNoteChordGlyphBase.cs b/Source/AlphaTab/Rendering/Glyphs/ScoreNoteChordGlyphBase.cs deleted file mode 100644 index bbb5c76cf..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/ScoreNoteChordGlyphBase.cs +++ /dev/null @@ -1,208 +0,0 @@ -using System; -using AlphaTab.Collections; -using AlphaTab.Platform; -using AlphaTab.Rendering.Utils; - -namespace AlphaTab.Rendering.Glyphs -{ - class ScoreNoteGlyphInfo - { - public Glyph Glyph { get; set; } - public int Line { get; set; } - - public ScoreNoteGlyphInfo(Glyph glyph, int line) - { - Glyph = glyph; - Line = line; - } - } - - abstract class ScoreNoteChordGlyphBase : Glyph - { - private readonly FastList _infos; - private float _noteHeadPadding; - - public ScoreNoteGlyphInfo MinNote { get; set; } - public ScoreNoteGlyphInfo MaxNote { get; set; } - - public Action SpacingChanged { get; set; } - public float UpLineX { get; set; } - public float DownLineX { get; set; } - public float DisplacedX { get; set; } - public float NoteStartX { get; set; } - - public ScoreNoteChordGlyphBase() - : base(0, 0) - { - _infos = new FastList(); - } - - public abstract BeamDirection Direction { get; } - - protected virtual void Add(Glyph noteGlyph, int noteLine) - { - var info = new ScoreNoteGlyphInfo(noteGlyph, noteLine); - _infos.Add(info); - if (MinNote == null || MinNote.Line > info.Line) - { - MinNote = info; - } - if (MaxNote == null || MaxNote.Line < info.Line) - { - MaxNote = info; - } - } - - public bool HasTopOverflow - { - get - { - return MinNote != null && MinNote.Line <= 0; - } - } - - public bool HasBottomOverflow - { - get - { - return MaxNote != null && MaxNote.Line > 8; - } - } - - public override void DoLayout() - { - _infos.Sort((a, b) => b.Line.CompareTo(a.Line)); - - var displacedX = 0f; - - var lastDisplaced = false; - var lastLine = 0; - var anyDisplaced = false; - var direction = Direction; - - var w = 0f; - for (int i = 0, j = _infos.Count; i < j; i++) - { - var g = _infos[i].Glyph; - g.Renderer = Renderer; - g.DoLayout(); - - var displace = false; - if (i == 0) - { - displacedX = g.Width; - } - else - { - // check if note needs to be repositioned - if (Math.Abs(lastLine - _infos[i].Line) <= 1) - { - // reposition if needed - if (!lastDisplaced) - { - displace = true; - g.X = displacedX - (Scale); - anyDisplaced = true; - lastDisplaced = true; // let next iteration know we are displace now - } - else - { - lastDisplaced = false; // let next iteration know that we weren't displaced now - } - } - else // offset is big enough? no displacing needed - { - lastDisplaced = false; - } - } - - // for beat direction down we invert the displacement. - // this means: displaced is on the left side of the stem and not displaced is right - if (direction == BeamDirection.Down) - { - g.X = displace - ? 0 - : displacedX; - } - else - { - g.X = displace - ? displacedX - : 0; - } - g.X += NoteStartX; - - lastLine = _infos[i].Line; - w = Math.Max(w, g.X + g.Width); - } - - if (anyDisplaced) - { - _noteHeadPadding = 0; - UpLineX = displacedX; - DownLineX = displacedX; - } - else - { - _noteHeadPadding = direction == BeamDirection.Down ? -displacedX : 0; - w += _noteHeadPadding; - UpLineX = w; - DownLineX = 0; - } - - DisplacedX = displacedX; - - Width = w; - } - - - public override void Paint(float cx, float cy, ICanvas canvas) - { - cx += X; - cy += Y; - // TODO: this method seems to be quite heavy according to the profiler, why? - var scoreRenderer = (ScoreBarRenderer)Renderer; - - // TODO: Take care of beateffects in overflow - - var linePadding = 3 * Scale; - var lineWidth = Width - NoteStartX + linePadding * 2; - if (HasTopOverflow) - { - var color = canvas.Color; - canvas.Color = Renderer.ScoreRenderer.RenderingResources.StaveLineColor; - var l = 0; - while (l >= MinNote.Line) - { - // + 1 Because we want to place the line in the center of the note, not at the top - var lY = cy + scoreRenderer.GetScoreY(l); - canvas.FillRect(cx - linePadding + NoteStartX, lY, lineWidth, Scale); - l -= 2; - } - canvas.Color = color; - } - - if (HasBottomOverflow) - { - var color = canvas.Color; - canvas.Color = Renderer.ScoreRenderer.RenderingResources.StaveLineColor; - var l = 12; - while (l <= MaxNote.Line) - { - var lY = cy + scoreRenderer.GetScoreY(l); - canvas.FillRect(cx - linePadding + NoteStartX, lY, lineWidth, Scale); - l += 2; - } - canvas.Color = color; - } - - var infos = _infos; - var x = cx + _noteHeadPadding; - foreach (var g in infos) - { - g.Glyph.Renderer = Renderer; - g.Glyph.Paint(x, cy, canvas); - } - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Glyphs/ScoreRestGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/ScoreRestGlyph.cs deleted file mode 100644 index 1957208fb..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/ScoreRestGlyph.cs +++ /dev/null @@ -1,102 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Model; -using AlphaTab.Rendering.Utils; - -namespace AlphaTab.Rendering.Glyphs -{ - class ScoreRestGlyph : MusicFontGlyph - { - private readonly Duration _duration; - - public BeamingHelper BeamingHelper { get; set; } - - public ScoreRestGlyph(float x, float y, Duration duration) - : base(x, y, 1, GetSymbol(duration)) - { - _duration = duration; - } - - public static MusicFontSymbol GetSymbol(Duration duration) - { - switch (duration) - { - case Duration.QuadrupleWhole: - return MusicFontSymbol.RestQuadrupleWhole; - case Duration.DoubleWhole: - return MusicFontSymbol.RestDoubleWhole; - case Duration.Whole: - return MusicFontSymbol.RestWhole; - case Duration.Half: - return MusicFontSymbol.RestHalf; - case Duration.Quarter: - return MusicFontSymbol.RestQuarter; - case Duration.Eighth: - return MusicFontSymbol.RestEighth; - case Duration.Sixteenth: - return MusicFontSymbol.RestSixteenth; - case Duration.ThirtySecond: - return MusicFontSymbol.RestThirtySecond; - case Duration.SixtyFourth: - return MusicFontSymbol.RestSixtyFourth; - case Duration.OneHundredTwentyEighth: - return MusicFontSymbol.RestOneHundredTwentyEighth; - case Duration.TwoHundredFiftySixth: - return MusicFontSymbol.RestTwoHundredFiftySixth; - default: - return MusicFontSymbol.None; - } - } - public static float GetSize(Duration duration) - { - switch (duration) - { - case Duration.QuadrupleWhole: - case Duration.DoubleWhole: - case Duration.Whole: - case Duration.Half: - case Duration.Quarter: - case Duration.Eighth: - case Duration.Sixteenth: - return 9; - case Duration.ThirtySecond: - return 12; - case Duration.SixtyFourth: - return 14; - case Duration.OneHundredTwentyEighth: - case Duration.TwoHundredFiftySixth: - return 20; - } - - return 10; - } - - public override void DoLayout() - { - Width = GetSize(_duration) * Scale; - } - - public void UpdateBeamingHelper(float cx) - { - if (BeamingHelper != null) - { - BeamingHelper.RegisterBeatLineX(ScoreBarRenderer.StaffId, Beat, cx + X + Width / 2, cx + X + Width / 2); - } - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/ScoreSlideLineGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/ScoreSlideLineGlyph.cs deleted file mode 100644 index 10bf58c0e..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/ScoreSlideLineGlyph.cs +++ /dev/null @@ -1,170 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using AlphaTab.Model; -using AlphaTab.Platform; - -namespace AlphaTab.Rendering.Glyphs -{ - class ScoreSlideLineGlyph : Glyph - { - private readonly Note _startNote; - private readonly SlideType _type; - private readonly BeatContainerGlyph _parent; - - public ScoreSlideLineGlyph(SlideType type, Note startNote, BeatContainerGlyph parent) - : base(0, 0) - { - _type = type; - _startNote = startNote; - _parent = parent; - } - - public override void DoLayout() - { - Width = 0; - } - - public override void Paint(float cx, float cy, ICanvas canvas) - { - var startNoteRenderer = (ScoreBarRenderer)Renderer; - - var sizeX = 12 * Scale; - var offsetX = 1 * Scale; - float startX; - float startY; - float endX; - float endY; - bool waves = false; - - switch (_type) - { - case SlideType.Shift: - case SlideType.Legato: - startX = cx + startNoteRenderer.X + startNoteRenderer.GetBeatX(_startNote.Beat, BeatXPosition.PostNotes) + offsetX; - var isUp = _startNote.SlideTarget.RealValue > _startNote.RealValue; - startY = cy + startNoteRenderer.Y + startNoteRenderer.GetNoteY(_startNote); - var lineOffset = 0.25f * NoteHeadGlyph.NoteHeadHeight * Scale; - if (isUp) - { - startY += lineOffset; - } - else - { - startY -= lineOffset; - } - - if (_startNote.SlideTarget != null) - { - var endNoteRenderer = Renderer.ScoreRenderer.Layout.GetRendererForBar(Renderer.Staff.StaveId, _startNote.SlideTarget.Beat.Voice.Bar); - if (endNoteRenderer == null || endNoteRenderer.Staff != startNoteRenderer.Staff) - { - endX = cx + startNoteRenderer.X + _parent.X; - endY = startY; - } - else - { - endX = cx + endNoteRenderer.X + endNoteRenderer.GetBeatX(_startNote.SlideTarget.Beat, BeatXPosition.PreNotes) - offsetX; - endY = cy + endNoteRenderer.Y + endNoteRenderer.GetNoteY(_startNote.SlideTarget); - if (isUp) - { - endY -= lineOffset; - } - else - { - endY += lineOffset; - } - } - } - else - { - endX = cx + startNoteRenderer.X + _parent.X; - endY = startY; - } - break; - case SlideType.IntoFromBelow: - endX = cx + startNoteRenderer.X + startNoteRenderer.GetNoteX(_startNote, false) - offsetX; - endY = cy + startNoteRenderer.Y + startNoteRenderer.GetNoteY(_startNote) + NoteHeadGlyph.NoteHeadHeight / 2; - - startX = endX - sizeX; - startY = cy + startNoteRenderer.Y + startNoteRenderer.GetNoteY(_startNote) + NoteHeadGlyph.NoteHeadHeight; - break; - case SlideType.IntoFromAbove: - endX = cx + startNoteRenderer.X + startNoteRenderer.GetNoteX(_startNote, false) - offsetX; - endY = cy + startNoteRenderer.Y + startNoteRenderer.GetNoteY(_startNote) + NoteHeadGlyph.NoteHeadHeight / 2; - - startX = endX - sizeX; - startY = cy + startNoteRenderer.Y + startNoteRenderer.GetNoteY(_startNote); - break; - case SlideType.OutUp: - startX = cx + startNoteRenderer.X + startNoteRenderer.GetNoteX(_startNote) + offsetX; - startY = cy + startNoteRenderer.Y + startNoteRenderer.GetNoteY(_startNote) + NoteHeadGlyph.NoteHeadHeight / 2; - endX = startX + sizeX; - endY = cy + startNoteRenderer.Y + startNoteRenderer.GetNoteY(_startNote); - break; - case SlideType.OutDown: - startX = cx + startNoteRenderer.X + startNoteRenderer.GetNoteX(_startNote) + offsetX; - startY = cy + startNoteRenderer.Y + startNoteRenderer.GetNoteY(_startNote) + NoteHeadGlyph.NoteHeadHeight / 2; - endX = startX + sizeX; - endY = cy + startNoteRenderer.Y + startNoteRenderer.GetNoteY(_startNote) + NoteHeadGlyph.NoteHeadHeight; - break; - case SlideType.PickSlideUp: - startX = cx + startNoteRenderer.X + startNoteRenderer.GetNoteX(_startNote); - startY = cy + startNoteRenderer.Y + startNoteRenderer.GetNoteY(_startNote) + NoteHeadGlyph.NoteHeadHeight / 2; - endX = cx + startNoteRenderer.X + startNoteRenderer.GetBeatX(_startNote.Beat, BeatXPosition.EndBeat); - endY = cy + startNoteRenderer.Y + startNoteRenderer.GetNoteY(_startNote) - NoteHeadGlyph.NoteHeadHeight; - waves = true; - break; - case SlideType.PickSlideDown: - startX = cx + startNoteRenderer.X + startNoteRenderer.GetNoteX(_startNote); - startY = cy + startNoteRenderer.Y + startNoteRenderer.GetNoteY(_startNote) - NoteHeadGlyph.NoteHeadHeight / 2; - endX = cx + startNoteRenderer.X + startNoteRenderer.GetBeatX(_startNote.Beat, BeatXPosition.EndBeat); - endY = cy + startNoteRenderer.Y + startNoteRenderer.GetNoteY(_startNote) + NoteHeadGlyph.NoteHeadHeight; - waves = true; - break; - default: - return; - } - - if (waves) - { - var b = endX - startX; - var a = endY - startY; - var c = Math.Sqrt(Math.Pow(a, 2) + Math.Pow(b, 2)); - var angle = (float)(Math.Asin(a / c) * (180 / Math.PI)); - canvas.BeginRotate(startX, startY, angle); - - var glyph = new NoteVibratoGlyph(0, 0, VibratoType.Slight); - glyph.Renderer = Renderer; - glyph.DoLayout(); - glyph.Width = b; - glyph.Paint(0, 0, canvas); - - canvas.EndRotate(); - } - else - { - canvas.BeginPath(); - canvas.MoveTo(startX, startY); - canvas.LineTo(endX, endY); - canvas.Stroke(); - } - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/ScoreSlurGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/ScoreSlurGlyph.cs deleted file mode 100644 index 6090255f0..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/ScoreSlurGlyph.cs +++ /dev/null @@ -1,91 +0,0 @@ -using AlphaTab.Collections; -using AlphaTab.Model; -using AlphaTab.Platform; -using AlphaTab.Rendering.Utils; - -namespace AlphaTab.Rendering.Glyphs -{ - class ScoreSlurGlyph : Glyph - { - private readonly Beat _startBeat; - - public ScoreSlurGlyph(Beat startBeat) - : base(0, 0) - { - _startBeat = startBeat; - } - - protected BeamDirection GetBeamDirection(Beat beat, BarRendererBase noteRenderer) - { - // invert direction (if stems go up, ties go down to not cross them) - switch (((ScoreBarRenderer)noteRenderer).GetBeatDirection(beat)) - { - case BeamDirection.Up: - return BeamDirection.Down; - case BeamDirection.Down: - default: - return BeamDirection.Up; - } - } - - public override void Paint(float cx, float cy, ICanvas canvas) - { - // only render slur once per staff - var slurId = "score.slur." + _startBeat.SlurOrigin.Id; - var renderer = Renderer; - var isSlurRendered = renderer.Staff.GetSharedLayoutData(slurId, false); - if (!isSlurRendered) - { - renderer.Staff.SetSharedLayoutData(slurId, true); - - var startNoteRenderer = Renderer.ScoreRenderer.Layout.GetRendererForBar(Renderer.Staff.StaveId, _startBeat.Voice.Bar); - var direction = GetBeamDirection(_startBeat, startNoteRenderer); - - var startX = cx + startNoteRenderer.X; - var startY = cy + startNoteRenderer.Y; - if (_startBeat.SlurOrigin.Id == _startBeat.Id) - { - startX += startNoteRenderer.GetBeatX(_startBeat, BeatXPosition.MiddleNotes); - var note = direction == BeamDirection.Down ? _startBeat.MinNote : _startBeat.MaxNote; - startY += startNoteRenderer.GetNoteY(note); - } - else - { - startY += startNoteRenderer.Height; - } - var endBeat = _startBeat.SlurOrigin.SlurDestination; - - var endNoteRenderer = Renderer.ScoreRenderer.Layout.GetRendererForBar(Renderer.Staff.StaveId, endBeat.Voice.Bar); - float endX; - float endY; - if (endNoteRenderer == null || startNoteRenderer.Staff != endNoteRenderer.Staff) - { - endNoteRenderer = (ScoreBarRenderer)startNoteRenderer.Staff.BarRenderers[startNoteRenderer.Staff.BarRenderers.Count - 1]; - endX = cx + endNoteRenderer.X + endNoteRenderer.Width; - endY = cy + endNoteRenderer.Y + endNoteRenderer.Height; - } - else - { - endX = cx + endNoteRenderer.X + endNoteRenderer.GetBeatX(endBeat, BeatXPosition.MiddleNotes); - - // if the note stem is pointing towards the slur, we need to let it end on top of the stem. - var endBeatHelper = endNoteRenderer.Helpers.GetBeamingHelperForBeat(endBeat); - if (endBeatHelper.Direction == direction) - { - endY = cy + endNoteRenderer.Y + endNoteRenderer.CalculateBeamY(endBeatHelper, endX); - } - else - { - var note = direction == BeamDirection.Down ? endBeat.MinNote : endBeat.MaxNote; - endY = cy + endNoteRenderer.Y + endNoteRenderer.GetNoteY(note); - } - } - - var height = (endX - startX) * Renderer.Settings.SlurHeightFactor; - - TieGlyph.PaintTie(canvas, Scale, startX, startY, endX, endY, direction == BeamDirection.Down, height); - canvas.Fill(); - } - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/ScoreTieGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/ScoreTieGlyph.cs deleted file mode 100644 index 7b8de1d07..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/ScoreTieGlyph.cs +++ /dev/null @@ -1,76 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using AlphaTab.Model; -using AlphaTab.Rendering.Utils; - -namespace AlphaTab.Rendering.Glyphs -{ - class ScoreTieGlyph : TieGlyph - { - protected readonly Note StartNote; - protected readonly Note EndNote; - - public ScoreTieGlyph(Note startNote, Note endNote, bool forEnd = false) - : base(startNote == null ? null : startNote.Beat, endNote == null ? null : endNote.Beat, forEnd) - { - StartNote = startNote; - EndNote = endNote; - } - - public override void DoLayout() - { - base.DoLayout(); - YOffset = (NoteHeadGlyph.NoteHeadHeight/2); - } - - protected override BeamDirection GetBeamDirection(Beat beat, BarRendererBase noteRenderer) - { - // invert direction (if stems go up, ties go down to not cross them) - switch (((ScoreBarRenderer) noteRenderer).GetBeatDirection(beat)) - { - case BeamDirection.Up: - return BeamDirection.Down; - case BeamDirection.Down: - default: - return BeamDirection.Up; - } - } - - protected override float GetStartY(BarRendererBase noteRenderer, BeamDirection direction) - { - return noteRenderer.GetNoteY(StartNote); - } - - protected override float GetEndY(BarRendererBase noteRenderer, BeamDirection direction) - { - return noteRenderer.GetNoteY(EndNote); - } - - protected override float GetStartX(BarRendererBase noteRenderer) - { - return noteRenderer.GetBeatX(StartNote.Beat, BeatXPosition.MiddleNotes); - } - - protected override float GetEndX(BarRendererBase noteRenderer) - { - return noteRenderer.GetNoteX(EndNote, false); - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/ScoreTimeSignatureGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/ScoreTimeSignatureGlyph.cs deleted file mode 100644 index 3f77cb0c7..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/ScoreTimeSignatureGlyph.cs +++ /dev/null @@ -1,51 +0,0 @@ -namespace AlphaTab.Rendering.Glyphs -{ - class ScoreTimeSignatureGlyph : TimeSignatureGlyph - { - protected override float CommonY - { - get - { - var renderer = (ScoreBarRenderer)Renderer; - return renderer.GetScoreY(4); - } - } - - protected override float NumeratorY - { - get - { - return 2 * Scale; - } - } - - protected override float DenominatorY - { - get - { - return 20 * Scale; - } - } - - protected override float CommonScale - { - get - { - return 1; - } - } - - protected override float NumberScale - { - get - { - return 1; - } - } - - public ScoreTimeSignatureGlyph(float x, float y, int numerator, int denominator, bool isCommon) - : base(x, y, numerator, denominator, isCommon) - { - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Glyphs/ScoreWhammyBarGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/ScoreWhammyBarGlyph.cs deleted file mode 100644 index 389bccd1a..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/ScoreWhammyBarGlyph.cs +++ /dev/null @@ -1,320 +0,0 @@ -using AlphaTab.Model; -using AlphaTab.Platform; -using AlphaTab.Platform.Model; -using AlphaTab.Rendering.Utils; - -namespace AlphaTab.Rendering.Glyphs -{ - class ScoreWhammyBarGlyph : ScoreHelperNotesBaseGlyph - { - public const int SimpleDipHeight = TabWhammyBarGlyph.PerHalfSize * 2; - public const int SimpleDipPadding = 2; - private readonly Beat _beat; - - public ScoreWhammyBarGlyph(Beat beat) - { - _beat = beat; - } - - public override void DoLayout() - { - var whammyMode = Renderer.Settings.DisplayMode; - switch (_beat.WhammyBarType) - { - case WhammyType.None: - case WhammyType.Custom: - case WhammyType.Hold: - return; - case WhammyType.Dive: - case WhammyType.PrediveDive: - { - var endGlyphs = new BendNoteHeadGroupGlyph(_beat); - endGlyphs.Renderer = Renderer; - - var lastWhammyPoint = _beat.WhammyBarPoints[_beat.WhammyBarPoints.Count - 1]; - foreach (var note in _beat.Notes) - { - if (!note.IsTieOrigin) - { - endGlyphs.AddGlyph(GetBendNoteValue(note, lastWhammyPoint), (lastWhammyPoint.Value % 2) != 0); - } - } - - endGlyphs.DoLayout(); - _bendNoteHeads.Add(endGlyphs); - } - break; - case WhammyType.Dip: - { - if (whammyMode == DisplayMode.SongBook) - { - var res = Renderer.Resources; - ((ScoreBarRenderer)Renderer).SimpleWhammyOverflow = - res.TablatureFont.Size * 1.5f + SimpleDipHeight * Scale + 2*Scale; - } - else - { - var middleGlyphs = new BendNoteHeadGroupGlyph(_beat); - middleGlyphs.Renderer = Renderer; - if (Renderer.Settings.DisplayMode == DisplayMode.GuitarPro) - { - var middleBendPoint = _beat.WhammyBarPoints[1]; - foreach (var note in _beat.Notes) - { - middleGlyphs.AddGlyph(GetBendNoteValue(note, _beat.WhammyBarPoints[1]), (middleBendPoint.Value % 2) != 0); - } - } - - middleGlyphs.DoLayout(); - _bendNoteHeads.Add(middleGlyphs); - - var endGlyphs = new BendNoteHeadGroupGlyph(_beat); - endGlyphs.Renderer = Renderer; - if (Renderer.Settings.DisplayMode == DisplayMode.GuitarPro) - { - var lastBendPoint = _beat.WhammyBarPoints[_beat.WhammyBarPoints.Count - 1]; - foreach (var note in _beat.Notes) - { - endGlyphs.AddGlyph(GetBendNoteValue(note, lastBendPoint), (lastBendPoint.Value % 2) != 0); - } - } - endGlyphs.DoLayout(); - - _bendNoteHeads.Add(endGlyphs); - } - } - break; - case WhammyType.Predive: - break; - } - base.DoLayout(); - } - - public override void Paint(float cx, float cy, ICanvas canvas) - { - var beat = _beat; - switch (beat.WhammyBarType) - { - case WhammyType.None: - case WhammyType.Custom: - return; - } - - var whammyMode = Renderer.Settings.DisplayMode; - var startNoteRenderer = Renderer.ScoreRenderer.Layout.GetRendererForBar(Renderer.Staff.StaveId, beat.Voice.Bar); - var startX = cx + startNoteRenderer.X + startNoteRenderer.GetBeatX(beat, BeatXPosition.MiddleNotes); - var beatDirection = GetBeamDirection(beat, startNoteRenderer); - var direction = _beat.Notes.Count == 1 ? beatDirection : BeamDirection.Up; - - var textalign = canvas.TextAlign; - for (var i = 0; i < beat.Notes.Count; i++) - { - var note = beat.Notes[i]; - var startY = cy + startNoteRenderer.Y + startNoteRenderer.GetNoteY(note, true); - if (direction == BeamDirection.Down) - { - startY += NoteHeadGlyph.NoteHeadHeight * Scale; - } - - if (i > 0 && i >= _beat.Notes.Count / 2) - { - direction = BeamDirection.Down; - } - - var endX = cx + startNoteRenderer.X; - if (beat.IsLastOfVoice) - { - endX += startNoteRenderer.Width; - } - else - { - endX += startNoteRenderer.GetBeatX(beat, BeatXPosition.EndBeat); - } - - endX -= EndPadding * Scale; - - var slurText = beat.WhammyStyle == BendStyle.Gradual && i == 0 ? "grad." : ""; - - ScoreBarRenderer endNoteRenderer = null; - if (note.IsTieOrigin) - { - endNoteRenderer = - Renderer.ScoreRenderer.Layout.GetRendererForBar(Renderer.Staff.StaveId, - note.TieDestination.Beat.Voice.Bar); - if (endNoteRenderer != null && endNoteRenderer.Staff == startNoteRenderer.Staff) - { - endX = cx + endNoteRenderer.X + - endNoteRenderer.GetBeatX(note.TieDestination.Beat, BeatXPosition.MiddleNotes); - } - else - { - endNoteRenderer = null; - } - } - - var heightOffset = (NoteHeadGlyph.NoteHeadHeight * Scale * NoteHeadGlyph.GraceScale) * 0.5f; - if (direction == BeamDirection.Up) heightOffset = -heightOffset; - int endValue; - float endY; - - switch (beat.WhammyBarType) - { - case WhammyType.Hold: - if (note.IsTieOrigin) - { - if (endNoteRenderer == null) - { - endY = startY; - } - else - { - endY = cy + endNoteRenderer.Y + endNoteRenderer.GetNoteY(note.TieDestination, true); - } - TieGlyph.PaintTie(canvas, Scale, startX, startY, endX, endY, - beatDirection == BeamDirection.Down); - canvas.Fill(); - } - break; - case WhammyType.Dive: - if (i == 0) - { - _bendNoteHeads[0].X = endX - _bendNoteHeads[0].NoteHeadOffset; - _bendNoteHeads[0].Y = cy + startNoteRenderer.Y; - _bendNoteHeads[0].Paint(0, 0, canvas); - } - - endValue = GetBendNoteValue(note, beat.WhammyBarPoints[beat.WhammyBarPoints.Count - 1]); - if (_bendNoteHeads[0].ContainsNoteValue(endValue)) - { - endY = _bendNoteHeads[0].GetNoteValueY(endValue) + heightOffset; - DrawBendSlur(canvas, startX, startY, endX, endY, direction == BeamDirection.Down, Scale, slurText); - } - else if (endNoteRenderer != null && (note.IsTieOrigin && note.TieDestination.Beat.HasWhammyBar || (note.Beat.IsContinuedWhammy))) - { - endY = cy + endNoteRenderer.Y + endNoteRenderer.GetNoteY(note.TieDestination, true); - DrawBendSlur(canvas, startX, startY, endX, endY, direction == BeamDirection.Down, Scale, slurText); - } - else if (note.IsTieOrigin) - { - if (endNoteRenderer == null) - { - endY = startY; - } - else - { - endY = cy + endNoteRenderer.Y + endNoteRenderer.GetNoteY(note.TieDestination, true); - } - TieGlyph.PaintTie(canvas, Scale, startX, startY, endX, endY, - beatDirection == BeamDirection.Down); - canvas.Fill(); - } - break; - case WhammyType.Dip: - if (whammyMode == DisplayMode.SongBook) - { - if (i == 0) - { - var simpleStartX = cx + startNoteRenderer.X + - startNoteRenderer.GetBeatX(_beat, BeatXPosition.OnNotes) - - SimpleDipPadding * Scale; - var simpleEndX = cx + startNoteRenderer.X + - startNoteRenderer.GetBeatX(_beat, BeatXPosition.PostNotes) - + SimpleDipPadding * Scale; - var middleX = (simpleStartX + simpleEndX) / 2; - var text = ((_beat.WhammyBarPoints[1].Value - _beat.WhammyBarPoints[0].Value) / 4) - .ToString(); - canvas.Font = Renderer.Resources.TablatureFont; - canvas.FillText(text, middleX, cy + Y); - - var simpleStartY = cy + Y + canvas.Font.Size + 2 * Scale; - var simpleEndY = simpleStartY + SimpleDipHeight * Scale; - - if (_beat.WhammyBarPoints[1].Value > _beat.WhammyBarPoints[0].Value) - { - canvas.MoveTo(simpleStartX, simpleEndY); - canvas.LineTo(middleX, simpleStartY); - canvas.LineTo(simpleEndX, simpleEndY); - } - else - { - canvas.MoveTo(simpleStartX, simpleStartY); - canvas.LineTo(middleX, simpleEndY); - canvas.LineTo(simpleEndX, simpleStartY); - } - canvas.Stroke(); - } - - if (note.IsTieOrigin) - { - if (endNoteRenderer == null) - { - endY = startY; - } - else - { - endY = cy + endNoteRenderer.Y + endNoteRenderer.GetNoteY(note.TieDestination, true); - } - TieGlyph.PaintTie(canvas, Scale, startX, startY, endX, endY, - beatDirection == BeamDirection.Down); - canvas.Fill(); - } - } - else - { - var middleX = (startX + endX) / 2; - - _bendNoteHeads[0].X = middleX - _bendNoteHeads[0].NoteHeadOffset; - _bendNoteHeads[0].Y = cy + startNoteRenderer.Y; - _bendNoteHeads[0].Paint(0, 0, canvas); - var middleValue = GetBendNoteValue(note, beat.WhammyBarPoints[1]); - var middleY = _bendNoteHeads[0].GetNoteValueY(middleValue) + heightOffset; - DrawBendSlur(canvas, startX, startY, middleX, middleY, direction == BeamDirection.Down, - Scale, slurText); - - _bendNoteHeads[1].X = endX - _bendNoteHeads[1].NoteHeadOffset; - _bendNoteHeads[1].Y = cy + startNoteRenderer.Y; - _bendNoteHeads[1].Paint(0, 0, canvas); - endValue = GetBendNoteValue(note, beat.WhammyBarPoints[beat.WhammyBarPoints.Count - 1]); - endY = _bendNoteHeads[1].GetNoteValueY(endValue) + heightOffset; - DrawBendSlur(canvas, middleX, middleY, endX, endY, direction == BeamDirection.Down, Scale, slurText); - } - - break; - case WhammyType.PrediveDive: - case WhammyType.Predive: - var preX = cx + startNoteRenderer.X + - startNoteRenderer.GetBeatX(note.Beat, BeatXPosition.PreNotes); - preX += ((ScoreBeatPreNotesGlyph)startNoteRenderer.GetBeatContainer(note.Beat).PreNotes) - .PrebendNoteHeadOffset; - - var preY = cy + startNoteRenderer.Y + - startNoteRenderer.GetScoreY(startNoteRenderer.AccidentalHelper.GetNoteLineForValue(note.DisplayValue - note.Beat.WhammyBarPoints[0].Value / 2)) - + heightOffset; - - DrawBendSlur(canvas, preX, preY, startX, startY, direction == BeamDirection.Down, Scale, slurText); - - if (_bendNoteHeads.Count > 0) - { - _bendNoteHeads[0].X = endX - _bendNoteHeads[0].NoteHeadOffset; - _bendNoteHeads[0].Y = cy + startNoteRenderer.Y; - _bendNoteHeads[0].Paint(0, 0, canvas); - - endValue = GetBendNoteValue(note, beat.WhammyBarPoints[beat.WhammyBarPoints.Count - 1]); - endY = _bendNoteHeads[0].GetNoteValueY(endValue) + heightOffset; - DrawBendSlur(canvas, startX, startY, endX, endY, direction == BeamDirection.Down, Scale, slurText); - } - - break; - } - } - - canvas.TextAlign = textalign; - } - - private int GetBendNoteValue(Note note, BendPoint bendPoint) - { - // NOTE: bendpoints are in 1/4 tones, but the note values are in 1/2 notes. - return note.DisplayValueWithoutBend + bendPoint.Value / 2; - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Glyphs/SpacingGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/SpacingGlyph.cs deleted file mode 100644 index bf902c9fb..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/SpacingGlyph.cs +++ /dev/null @@ -1,31 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Rendering.Glyphs -{ - /// - /// This simple glyph allows to put an empty region in to a BarRenderer. - /// - class SpacingGlyph : Glyph - { - public SpacingGlyph(float x, float y, float width) - : base(x, y) - { - Width = width; - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/TabBeatContainerGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/TabBeatContainerGlyph.cs deleted file mode 100644 index 6979c02ac..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/TabBeatContainerGlyph.cs +++ /dev/null @@ -1,86 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Collections; -using AlphaTab.Model; -using AlphaTab.Platform; -using AlphaTab.Rendering.Staves; - -namespace AlphaTab.Rendering.Glyphs -{ - class TabBeatContainerGlyph : BeatContainerGlyph - { - private TabBendGlyph _bend; - - public TabBeatContainerGlyph(Beat beat, VoiceContainerGlyph voiceContainer) - : base(beat, voiceContainer) - { - } - - public override void DoLayout() - { - base.DoLayout(); - if (_bend != null) - { - _bend.Renderer = Renderer; - _bend.DoLayout(); - UpdateWidth(); - } - } - - protected override void CreateTies(Note n) - { - if (!n.IsVisible) return; - - var renderer = (TabBarRenderer)Renderer; - if (n.IsTieOrigin && renderer.ShowTiedNotes && n.TieDestination.IsVisible) - { - var tie = new TabTieGlyph(n, n.TieDestination, false); - Ties.Add(tie); - } - if (n.IsTieDestination && renderer.ShowTiedNotes) - { - var tie = new TabTieGlyph(n.TieOrigin, n, false, true); - Ties.Add(tie); - } - - if (n.SlurOrigin != null) - { - var tie = new TabSlurGlyph(n); - Ties.Add(tie); - } - - if (n.SlideType != SlideType.None) - { - var l = new TabSlideLineGlyph(n.SlideType, n, this); - Ties.Add(l); - } - - if (n.HasBend) - { - if (_bend == null) - { - _bend = new TabBendGlyph(n.Beat); - _bend.Renderer = Renderer; - Ties.Add(_bend); - } - _bend.AddBends(n); - } - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/TabBeatGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/TabBeatGlyph.cs deleted file mode 100644 index eb32f6876..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/TabBeatGlyph.cs +++ /dev/null @@ -1,207 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Model; - -namespace AlphaTab.Rendering.Glyphs -{ - class TabBeatGlyph : BeatOnNoteGlyphBase - { - public TabNoteChordGlyph NoteNumbers { get; set; } - public TabRestGlyph RestGlyph { get; set; } - - public override void DoLayout() - { - var tabRenderer = (TabBarRenderer)Renderer; - if (!Container.Beat.IsRest) - { - // - // Note numbers - var isGrace = Renderer.Settings.SmallGraceTabNotes && Container.Beat.GraceType != GraceType.None; - NoteNumbers = new TabNoteChordGlyph(0, 0, isGrace); - NoteNumbers.Beat = Container.Beat; - NoteNumbers.BeamingHelper = BeamingHelper; - foreach (var note in Container.Beat.Notes) - { - if (note.IsVisible) - { - CreateNoteGlyph(note); - } - } - AddGlyph(NoteNumbers); - - // - // Whammy Bar - if (Container.Beat.HasWhammyBar) - { - var whammy = new TabWhammyBarGlyph(Container.Beat); - whammy.Renderer = Renderer; - whammy.DoLayout(); - - Container.Ties.Add(whammy); - } - - // - // Tremolo Picking - if (Container.Beat.IsTremolo && !NoteNumbers.BeatEffects.ContainsKey("Tremolo")) - { - int offset = 0; - var speed = Container.Beat.TremoloSpeed.Value; - switch (speed) - { - case Duration.ThirtySecond: - offset = 10; - break; - case Duration.Sixteenth: - offset = 5; - break; - case Duration.Eighth: - offset = 0; - break; - } - - NoteNumbers.BeatEffects["Tremolo"] = new TremoloPickingGlyph(5 * Scale, offset * Scale, - Container.Beat.TremoloSpeed.Value); - } - - // - // Note dots - // - if (Container.Beat.Dots > 0 && tabRenderer.RenderRhythm) - { - AddGlyph(new SpacingGlyph(0, 0, 5 * Scale)); - for (var i = 0; i < Container.Beat.Dots; i++) - { - AddGlyph(new CircleGlyph(0, tabRenderer.LineOffset * tabRenderer.Bar.Staff.Tuning.Length + tabRenderer.RhythmHeight, 1.5f * Scale)); - } - } - } - else - { - var line = 0f; - var offset = 0; - - switch (Container.Beat.Duration) - { - case Duration.QuadrupleWhole: - line = 3; - break; - case Duration.DoubleWhole: - line = 3; - break; - case Duration.Whole: - line = 2; - break; - case Duration.Half: - line = 3; - break; - case Duration.Quarter: - line = 3; - break; - case Duration.Eighth: - line = 2; - offset = 5; - break; - case Duration.Sixteenth: - line = 2; - offset = 5; - break; - case Duration.ThirtySecond: - line = 3; - break; - case Duration.SixtyFourth: - line = 3; - break; - case Duration.OneHundredTwentyEighth: - line = 3; - break; - case Duration.TwoHundredFiftySixth: - line = 3; - break; - } - - var y = tabRenderer.GetTabY(line, offset); - - RestGlyph = new TabRestGlyph(0, y, tabRenderer.ShowRests, Container.Beat.Duration); - RestGlyph.Beat = Container.Beat; - RestGlyph.BeamingHelper = BeamingHelper; - AddGlyph(RestGlyph); - - // - // Note dots - // - if (Container.Beat.Dots > 0 && tabRenderer.ShowRests) - { - AddGlyph(new SpacingGlyph(0, 0, 5 * Scale)); - for (var i = 0; i < Container.Beat.Dots; i++) - { - AddGlyph(new CircleGlyph(0, y, 1.5f * Scale)); - } - } - } - - // left to right layout - if (Glyphs == null) return; - var w = 0f; - for (int i = 0, j = Glyphs.Count; i < j; i++) - { - var g = Glyphs[i]; - g.X = w; - g.Renderer = Renderer; - g.DoLayout(); - w += g.Width; - } - Width = w; - - if (Container.Beat.IsEmpty) - { - CenterX = Width / 2; - } - else if (Container.Beat.IsRest) - { - CenterX = RestGlyph.X + RestGlyph.Width / 2; - } - else - { - CenterX = NoteNumbers.X + NoteNumbers.NoteStringWidth / 2; - } - } - - public override void UpdateBeamingHelper() - { - if (!Container.Beat.IsRest) - { - NoteNumbers.UpdateBeamingHelper(Container.X + X); - } - else - { - RestGlyph.UpdateBeamingHelper(Container.X + X); - } - } - - private void CreateNoteGlyph(Note n) - { - var tr = (TabBarRenderer)Renderer; - var noteNumberGlyph = new NoteNumberGlyph(0, 0, n); - var l = n.Beat.Voice.Bar.Staff.Tuning.Length - n.String + 1; - noteNumberGlyph.Y = tr.GetTabY(l, -2); - noteNumberGlyph.Renderer = Renderer; - noteNumberGlyph.DoLayout(); - NoteNumbers.AddNoteGlyph(noteNumberGlyph, n); - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/TabBeatPreNotesGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/TabBeatPreNotesGlyph.cs deleted file mode 100644 index 7a3a7a8c9..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/TabBeatPreNotesGlyph.cs +++ /dev/null @@ -1,34 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Model; - -namespace AlphaTab.Rendering.Glyphs -{ - class TabBeatPreNotesGlyph : BeatGlyphBase - { - public override void DoLayout() - { - if (Container.Beat.BrushType != BrushType.None && !Container.Beat.IsRest) - { - AddGlyph(new TabBrushGlyph(Container.Beat)); - AddGlyph(new SpacingGlyph(0, 0, 4 * Scale)); - } - base.DoLayout(); - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/TabBendGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/TabBendGlyph.cs deleted file mode 100644 index ce78d8879..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/TabBendGlyph.cs +++ /dev/null @@ -1,622 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.Collections; -using AlphaTab.Model; -using AlphaTab.Platform; - -namespace AlphaTab.Rendering.Glyphs -{ - class TabBendRenderPoint : BendPoint - { - public int LineValue { get; set; } - - public TabBendRenderPoint(int offset = 0, int value = 0) : base(offset, value) - { - LineValue = value; - } - } - class TabBendGlyph : Glyph - { - private readonly Beat _beat; - private FastList _notes; - - private const int ArrowSize = 6; - private const int DashSize = 3; - private const int BendValueHeight = 6; - private FastDictionary> _renderPoints; - - // the values where to draw the arrow heads as multistring bends end at the same position - - private int _preBendMinValue; // directly above the note - private int _bendMiddleMinValue; // in the middle between note and end - private int _bendEndMinValue; // at the end on the helper note - private int _bendEndContinuedMinValue; // at the end on the next note - private int _releaseMinValue; // at the end for releases on the helper note - private int _releaseContinuedMinValue; // at the end for releases on the next note - - private int _maxBendValue; - - - public TabBendGlyph(Beat beat) - : base(0, 0) - { - _beat = beat; - _notes = new FastList(); - _renderPoints = new FastDictionary>(); - _preBendMinValue = -1; - _bendMiddleMinValue = -1; - _bendEndMinValue = -1; - _bendEndContinuedMinValue = -1; - _releaseMinValue = -1; - _releaseContinuedMinValue = -1; - _maxBendValue = -1; - } - - public void AddBends(Note note) - { - _notes.Add(note); - var renderPoints = CreateRenderingPoints(note); - _renderPoints[note.Id] = renderPoints; - - if (_maxBendValue == -1 || _maxBendValue < note.MaxBendPoint.Value) - { - _maxBendValue = note.MaxBendPoint.Value; - } - - // compute arrow end values for common bend types - int value; - switch (note.BendType) - { - case BendType.Bend: - value = renderPoints[1].Value; - if (note.IsTieOrigin) - { - if (_bendEndContinuedMinValue == -1 || value < _bendEndContinuedMinValue) - { - _bendEndContinuedMinValue = value; - } - } - else - { - if (_bendEndMinValue == -1 || value < _bendEndMinValue) - { - _bendEndMinValue = value; - } - } - break; - case BendType.Release: - value = renderPoints[1].Value; - if (note.IsTieOrigin) - { - if (_releaseContinuedMinValue == -1 || value < _releaseContinuedMinValue) - { - _releaseContinuedMinValue = value; - } - } - else - { - if (value > 0 && (_releaseMinValue == -1 || value < _releaseMinValue)) - { - _releaseMinValue = value; - } - } - break; - case BendType.BendRelease: - value = renderPoints[1].Value; - if (_bendMiddleMinValue == -1 || value < _bendMiddleMinValue) - { - _bendMiddleMinValue = value; - } - value = renderPoints[2].Value; - if (note.IsTieOrigin) - { - if (_releaseContinuedMinValue == -1 || value < _releaseContinuedMinValue) - { - _releaseContinuedMinValue = value; - } - } - else - { - if (value > 0 && (_releaseMinValue == -1 || value < _releaseMinValue)) - { - _releaseMinValue = value; - } - } - break; - case BendType.Prebend: - value = renderPoints[0].Value; - if (_preBendMinValue == -1 || value < _preBendMinValue) - { - _preBendMinValue = value; - } - break; - case BendType.PrebendBend: - value = renderPoints[0].Value; - if (_preBendMinValue == -1 || value < _preBendMinValue) - { - _preBendMinValue = value; - } - value = renderPoints[1].Value; - if (note.IsTieOrigin) - { - if (_bendEndContinuedMinValue == -1 || value < _bendEndContinuedMinValue) - { - _bendEndContinuedMinValue = value; - } - } - else - { - if (_bendEndMinValue == -1 || value < _bendEndMinValue) - { - _bendEndMinValue = value; - } - } - break; - case BendType.PrebendRelease: - value = renderPoints[0].Value; - if (_preBendMinValue == -1 || value < _preBendMinValue) - { - _preBendMinValue = value; - } - value = renderPoints[1].Value; - if (note.IsTieOrigin) - { - if (_releaseContinuedMinValue == -1 || value < _releaseContinuedMinValue) - { - _releaseContinuedMinValue = value; - } - } - else - { - if (value > 0 && (_releaseMinValue == -1 || value < _releaseMinValue)) - { - _releaseMinValue = value; - } - } - break; - } - } - - public override void DoLayout() - { - base.DoLayout(); - - var bendHeight = _maxBendValue * BendValueHeight * Scale; - Renderer.RegisterOverflowTop(bendHeight); - - int value; - foreach (var note in _notes) - { - var renderPoints = _renderPoints[note.Id]; - switch (note.BendType) - { - case BendType.Bend: - renderPoints[1].LineValue = note.IsTieOrigin ? _bendEndContinuedMinValue : _bendEndMinValue; - break; - case BendType.Release: - value = note.IsTieOrigin ? _releaseContinuedMinValue : _releaseMinValue; - if (value >= 0) - { - renderPoints[1].LineValue = value; - } - break; - case BendType.BendRelease: - renderPoints[1].LineValue = _bendMiddleMinValue; - value = note.IsTieOrigin ? _releaseContinuedMinValue : _releaseMinValue; - if (value >= 0) - { - renderPoints[2].LineValue = value; - } - break; - case BendType.Prebend: - renderPoints[0].LineValue = _preBendMinValue; - break; - case BendType.PrebendBend: - renderPoints[0].LineValue = _preBendMinValue; - renderPoints[1].LineValue = note.IsTieOrigin ? _bendEndContinuedMinValue : _bendEndMinValue; - break; - case BendType.PrebendRelease: - renderPoints[0].LineValue = _preBendMinValue; - value = note.IsTieOrigin ? _releaseContinuedMinValue : _releaseMinValue; - if (value >= 0) - { - renderPoints[1].LineValue = value; - } - break; - } - } - - Width = 0; - - _notes.Sort((a, b) => - { - if (a.IsStringed) - { - return a.String - b.String; - } - - return a.RealValue - b.RealValue; - }); - } - - private FastList CreateRenderingPoints(Note note) - { - var renderingPoints = new FastList(); - - // Guitar Pro Rendering Note: - // Last point of bend is always at end of the note even - // though it might not be 100% correct from timing perspective. - - switch (note.BendType) - { - case BendType.Custom: - foreach (var bendPoint in note.BendPoints) - { - renderingPoints.Add(new TabBendRenderPoint(bendPoint.Offset, bendPoint.Value)); - } - break; - case BendType.BendRelease: - renderingPoints.Add(new TabBendRenderPoint(0, note.BendPoints[0].Value)); - renderingPoints.Add(new TabBendRenderPoint(BendPoint.MaxPosition / 2, note.BendPoints[1].Value)); - renderingPoints.Add(new TabBendRenderPoint(BendPoint.MaxPosition, note.BendPoints[3].Value)); - break; - case BendType.Bend: - case BendType.Hold: - case BendType.Prebend: - case BendType.PrebendBend: - case BendType.PrebendRelease: - case BendType.Release: - renderingPoints.Add(new TabBendRenderPoint(0, note.BendPoints[0].Value)); - renderingPoints.Add(new TabBendRenderPoint(BendPoint.MaxPosition, note.BendPoints[1].Value)); - break; - } - - return renderingPoints; - } - - public override void Paint(float cx, float cy, ICanvas canvas) - { - var color = canvas.Color; - if (_notes.Count > 1) - { - canvas.Color = Renderer.Resources.SecondaryGlyphColor; - } - foreach (var note in _notes) - { - var renderPoints = _renderPoints[note.Id]; - var startNoteRenderer = Renderer; - Note endNote = note; - bool isMultiBeatBend = false; - TabBarRenderer endNoteRenderer; - bool endNoteHasBend = false; - var slurText = note.BendStyle == BendStyle.Gradual ? "grad." : ""; - - Beat endBeat = null; - while (endNote.IsTieOrigin) - { - var nextNote = endNote.TieDestination; - - endNoteRenderer = Renderer.ScoreRenderer.Layout.GetRendererForBar(Renderer.Staff.StaveId, nextNote.Beat.Voice.Bar); - - if (endNoteRenderer == null || startNoteRenderer.Staff != endNoteRenderer.Staff) - { - break; - } - - endNote = nextNote; - isMultiBeatBend = true; - - if (endNote.HasBend || !Renderer.Settings.ExtendBendArrowsOnTiedNotes) - { - endNoteHasBend = true; - break; - } - } - - endBeat = endNote.Beat; - endNoteRenderer = Renderer.ScoreRenderer.Layout.GetRendererForBar(Renderer.Staff.StaveId, endBeat.Voice.Bar); - - if (endBeat.IsLastOfVoice && !endNote.HasBend && Renderer.Settings.ExtendBendArrowsOnTiedNotes) - { - endBeat = null; - } - - float startX = 0; - float endX = 0; - - float topY = cy + startNoteRenderer.Y; - float bottomY = cy + startNoteRenderer.Y + startNoteRenderer.GetNoteY(note); - - startX = cx + startNoteRenderer.X; - if (renderPoints[0].Value > 0 || note.IsContinuedBend) - { - startX += startNoteRenderer.GetBeatX(note.Beat, BeatXPosition.MiddleNotes); - } - else - { - startX += startNoteRenderer.GetNoteX(note); - } - - //canvas.Color = Color.Random(); - //canvas.FillRect( - // cx + startNoteRenderer.X + startNoteRenderer.GetBeatX(_note.Beat, BeatXPosition.MiddleNotes), - // cy + startNoteRenderer.Y, 10, 10); - //canvas.FillRect( - // cx + startNoteRenderer.X + startNoteRenderer.GetBeatX(_note.Beat, BeatXPosition.EndBeat), - // cy + startNoteRenderer.Y + 10, 10, 10); - - if (endBeat == null || (endBeat.IsLastOfVoice && !endNoteHasBend)) - { - endX = cx + endNoteRenderer.X + endNoteRenderer.PostBeatGlyphsStart; - } - else if (endNoteHasBend || endBeat.NextBeat == null) - { - endX = cx + endNoteRenderer.X + endNoteRenderer.GetBeatX(endBeat, BeatXPosition.MiddleNotes); - } - else if (note.BendType == BendType.Hold) - { - endX = cx + endNoteRenderer.X + endNoteRenderer.GetBeatX(endBeat.NextBeat, BeatXPosition.OnNotes); - } - else - { - endX = cx + endNoteRenderer.X + endNoteRenderer.GetBeatX(endBeat.NextBeat, BeatXPosition.PreNotes); - } - - if (!isMultiBeatBend) - { - endX -= (ArrowSize * Scale); - } - - // we need some pixels for the arrow. otherwise we might draw into the next - // note - var width = endX - startX; - - // calculate offsets per step - - var dX = width / BendPoint.MaxPosition; - - canvas.BeginPath(); - for (int i = 0, j = renderPoints.Count - 1; i < j; i++) - { - var firstPt = renderPoints[i]; - var secondPt = renderPoints[i + 1]; - - // draw pre-bend if previous - if (i == 0 && firstPt.Value != 0 && !note.IsTieDestination) - { - PaintBend(note, new TabBendRenderPoint(), firstPt, startX, topY, dX, slurText, canvas); - } - - if (note.BendType != BendType.Prebend) - { - PaintBend(note, firstPt, secondPt, startX, topY, dX, slurText, canvas); - } - else if (note.IsTieOrigin && note.TieDestination.HasBend) - { - PaintBend(note, firstPt, new TabBendRenderPoint(BendPoint.MaxPosition, firstPt.Value) - { - LineValue = firstPt.LineValue - }, startX, topY, dX, slurText, canvas); - } - } - - canvas.Color = color; - } - } - - private void PaintBend(Note note, TabBendRenderPoint firstPt, TabBendRenderPoint secondPt, float cx, float cy, float dX, string slurText, ICanvas canvas) - { - var r = (TabBarRenderer)Renderer; - var res = Renderer.Resources; - - var overflowOffset = r.LineOffset / 2; - - var x1 = cx + (dX * firstPt.Offset); - var bendValueHeight = BendValueHeight * Scale; - var y1 = cy - (bendValueHeight * firstPt.LineValue); - if (firstPt.Value == 0) - { - if (secondPt.Offset == firstPt.Offset) - { - y1 += r.GetNoteY(note.Beat.MaxStringNote, true); - } - else - { - y1 += r.GetNoteY(note); - } - } - else - { - y1 += overflowOffset; - } - - var x2 = cx + (dX * secondPt.Offset); - var y2 = cy - (bendValueHeight * secondPt.LineValue); - if (secondPt.LineValue == 0) - { - y2 += r.GetNoteY(note); - } - else - { - y2 += overflowOffset; - } - - // what type of arrow? (up/down) - var arrowOffset = 0f; - var arrowSize = ArrowSize * Scale; - if (secondPt.Value > firstPt.Value) - { - if (y2 + arrowSize > y1) - { - y2 = y1 - arrowSize; - } - canvas.BeginPath(); - canvas.MoveTo(x2, y2); - canvas.LineTo(x2 - arrowSize * 0.5f, y2 + arrowSize); - canvas.LineTo(x2 + arrowSize * 0.5f, y2 + arrowSize); - canvas.ClosePath(); - canvas.Fill(); - arrowOffset = arrowSize; - } - else if (secondPt.Value != firstPt.Value) - { - if (y2 < y1) - { - y2 = y1 + arrowSize; - } - canvas.BeginPath(); - canvas.MoveTo(x2, y2); - canvas.LineTo(x2 - arrowSize * 0.5f, y2 - arrowSize); - canvas.LineTo(x2 + arrowSize * 0.5f, y2 - arrowSize); - canvas.ClosePath(); - canvas.Fill(); - arrowOffset = -arrowSize; - } - canvas.Stroke(); - - if (firstPt.Value == secondPt.Value) - { - // draw horizontal dashed line - // to really have the line ending at the right position - // we draw from right to left. it's okay if the space is at the beginning - if (firstPt.LineValue > 0) - { - var dashX = x2; - var dashSize = DashSize * Scale; - var end = (x1 + dashSize); - var dashes = (dashX - x1) / (dashSize * 2); - if (dashes < 1) - { - canvas.MoveTo(dashX, y1); - canvas.LineTo(x1, y1); - } - else - { - while (dashX > end) - { - canvas.MoveTo(dashX, y1); - canvas.LineTo(dashX - dashSize, y1); - dashX -= dashSize * 2; - } - } - - canvas.Stroke(); - } - } - else - { - if (x2 > x1) - { - // draw bezier lien from first to second point - canvas.MoveTo(x1, y1); - canvas.BezierCurveTo((x1 + x2) / 2, y1, x2, y1, x2, y2 + arrowOffset); - canvas.Stroke(); - } - else - { - canvas.MoveTo(x1, y1); - canvas.LineTo(x2, y2); - canvas.Stroke(); - } - } - - if (!string.IsNullOrEmpty(slurText) && firstPt.Offset < secondPt.Offset) - { - canvas.Font = res.GraceFont; - var size = canvas.MeasureText(slurText); - float y; - float x; - if (y1 > y2) - { - var h = Math.Abs(y1 - y2); - y = h > canvas.Font.Size * 1.3f ? y1 - h / 2 : y1; - x = (x1 + x2 - size) / 2; - } - else - { - y = y1; - x = x2 - size; - } - - canvas.FillText(slurText, x, y); - } - - if (secondPt.Value != 0 && firstPt.Value != secondPt.Value) - { - var dV = secondPt.Value; - var up = secondPt.Value > firstPt.Value; - dV = Math.Abs(dV); - - // calculate label - var s = ""; - // Full Steps - if (dV == 4) - { - s = "full"; - dV -= 4; - } - else if (dV >= 4 || dV <= -4) - { - int steps = dV / 4; - s += steps; - // Quaters - dV -= steps * 4; - } - - if (dV > 0) - { - s += GetFractionSign(dV); - } - - if (s != "") - { - y2 = cy - (bendValueHeight * secondPt.Value); - var startY = y2; - if (!up) - { - startY = y1 + Math.Abs(y2 - y1) * 1f / 3; - } - - // draw label - canvas.Font = res.TablatureFont; - var size = canvas.MeasureText(s); - var y = startY - res.TablatureFont.Size * 0.5f - (2 * Scale); - var x = x2 - size / 2; - - canvas.FillText(s, x, y); - } - } - } - - public static string GetFractionSign(int steps) - { - switch (steps) - { - case 1: - return "¼"; - case 2: - return "½"; - case 3: - return "¾"; - default: - return steps + "/ 4"; - } - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/TabBrushGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/TabBrushGlyph.cs deleted file mode 100644 index 06ed95682..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/TabBrushGlyph.cs +++ /dev/null @@ -1,107 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.Model; -using AlphaTab.Platform; - -namespace AlphaTab.Rendering.Glyphs -{ - class TabBrushGlyph : Glyph - { - private readonly Beat _beat; - - public TabBrushGlyph(Beat beat) - : base(0, 0) - { - _beat = beat; - } - - public override void DoLayout() - { - Width = 10 * Scale; - } - - public override void Paint(float cx, float cy, ICanvas canvas) - { - // TODO: Create webfont version - - var tabBarRenderer = (TabBarRenderer)Renderer; - var res = Renderer.Resources; - var startY = cy + X + (tabBarRenderer.GetNoteY(_beat.MaxNote) - res.TablatureFont.Size / 2); - var endY = cy + Y + tabBarRenderer.GetNoteY(_beat.MinNote) + res.TablatureFont.Size / 2; - var arrowX = (int)(cx + X + Width / 2); - var arrowSize = 8 * Scale; - - if (_beat.BrushType != BrushType.None) - { - if (_beat.BrushType == BrushType.BrushUp || _beat.BrushType == BrushType.BrushDown) - { - canvas.BeginPath(); - canvas.MoveTo(arrowX, startY); - canvas.LineTo(arrowX, endY); - canvas.Stroke(); - } - else if(_beat.BrushType == BrushType.ArpeggioUp) - { - var lineStartY = startY - arrowSize; - var lineEndY = endY - arrowSize; - - canvas.BeginRotate(cx + X + 2 * Scale, lineEndY, -90); - var glyph = new NoteVibratoGlyph(0, 0, VibratoType.Slight); - glyph.Renderer = Renderer; - glyph.DoLayout(); - glyph.Width = Math.Abs(lineEndY - lineStartY); - glyph.Paint(0, 0, canvas); - canvas.EndRotate(); - } - else if(_beat.BrushType == BrushType.ArpeggioDown) - { - var lineStartY = startY + arrowSize; - var lineEndY = endY + arrowSize; - - canvas.BeginRotate(cx + X + 7 * Scale, lineStartY, 90); - var glyph = new NoteVibratoGlyph(0, 0, VibratoType.Slight); - glyph.Renderer = Renderer; - glyph.DoLayout(); - glyph.Width = Math.Abs(lineEndY - lineStartY); - glyph.Paint(0, 0, canvas); - canvas.EndRotate(); - } - - if (_beat.BrushType == BrushType.BrushUp || _beat.BrushType == BrushType.ArpeggioUp) - { - canvas.BeginPath(); - canvas.MoveTo(arrowX, endY); - canvas.LineTo(arrowX + arrowSize / 2, endY - arrowSize); - canvas.LineTo(arrowX - arrowSize / 2, endY - arrowSize); - canvas.ClosePath(); - canvas.Fill(); - } - else - { - canvas.BeginPath(); - canvas.MoveTo(arrowX, startY); - canvas.LineTo(arrowX + arrowSize / 2, startY + arrowSize); - canvas.LineTo(arrowX - arrowSize / 2, startY + arrowSize); - canvas.ClosePath(); - canvas.Fill(); - } - } - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/TabClefGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/TabClefGlyph.cs deleted file mode 100644 index 02d723ad8..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/TabClefGlyph.cs +++ /dev/null @@ -1,44 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Platform; - -namespace AlphaTab.Rendering.Glyphs -{ - class TabClefGlyph : Glyph - { - public TabClefGlyph(float x, float y) - : base(x, y) - { - } - - public override void DoLayout() - { - Width = 28 * Scale; - } - public override void Paint(float cx, float cy, ICanvas canvas) - { - var strings = Renderer.Bar.Staff.Tuning.Length; - - var correction = strings * Scale * 0.5f; - var symbol = strings <= 4 ? MusicFontSymbol.ClefTabSmall : MusicFontSymbol.ClefTab; - var scale = strings <= 4 ? strings / 4.5f : strings / 6.5f; - canvas.FillMusicFontSymbol(cx + X + 5 * Scale, cy + Y - correction, scale * Scale, symbol); - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/TabNoteChordGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/TabNoteChordGlyph.cs deleted file mode 100644 index 70504f370..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/TabNoteChordGlyph.cs +++ /dev/null @@ -1,152 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Collections; -using AlphaTab.Model; -using AlphaTab.Platform; -using AlphaTab.Platform.Model; -using AlphaTab.Rendering.Utils; - -namespace AlphaTab.Rendering.Glyphs -{ - class TabNoteChordGlyph : Glyph - { - private readonly FastList _notes; - private readonly bool _isGrace; - - public Beat Beat { get; set; } - public BeamingHelper BeamingHelper { get; set; } - public Note MinStringNote { get; set; } - public FastDictionary BeatEffects { get; set; } - public FastDictionary NotesPerString { get; set; } - public float NoteStringWidth { get; set; } - - public TabNoteChordGlyph(float x, float y, bool isGrace) - : base(x, y) - { - _isGrace = isGrace; - _notes = new FastList(); - BeatEffects = new FastDictionary(); - NotesPerString = new FastDictionary(); - } - - public float GetNoteX(Note note, bool onEnd = true) - { - if (NotesPerString.ContainsKey(note.String)) - { - var n = NotesPerString[note.String]; - var pos = X + n.X; - if (onEnd) - { - pos += n.Width; - } - return pos; - } - return 0; - } - - public float GetNoteY(Note note, bool aboveNote = false) - { - if (NotesPerString.ContainsKey(note.String)) - { - return Y + NotesPerString[note.String].Y + (aboveNote ? -NotesPerString[note.String].Height / 2 : 0); - } - return 0; - } - - public override void DoLayout() - { - var w = 0f; - var noteStringWidth = 0f; - for (int i = 0, j = _notes.Count; i < j; i++) - { - var g = _notes[i]; - g.Renderer = Renderer; - g.DoLayout(); - if (g.Width > w) - { - w = g.Width; - } - - if (g.NoteStringWidth > noteStringWidth) - { - noteStringWidth = g.NoteStringWidth; - } - } - - NoteStringWidth = noteStringWidth; - - var tabHeight = Renderer.Resources.TablatureFont.Size; - var effectY = GetNoteY(MinStringNote) + tabHeight / 2; - // TODO: take care of actual glyph height - var effectSpacing = 7 * Scale; - foreach (var beatEffectKey in BeatEffects) - { - var g = BeatEffects[beatEffectKey]; - g.Y += effectY; - g.X += Width / 2; - g.Renderer = Renderer; - effectY += effectSpacing; - g.DoLayout(); - } - - Width = w; - } - - public void AddNoteGlyph(NoteNumberGlyph noteGlyph, Note note) - { - _notes.Add(noteGlyph); - NotesPerString[note.String] = noteGlyph; - if (MinStringNote == null || note.String < MinStringNote.String) MinStringNote = note; - } - - public override void Paint(float cx, float cy, ICanvas canvas) - { - cx += X; - cy += Y; - - var res = Renderer.Resources; - var oldBaseLine = canvas.TextBaseline; - canvas.TextBaseline = TextBaseline.Middle; - canvas.Font = _isGrace ? res.GraceFont : res.TablatureFont; - var notes = _notes; - var w = Width; - foreach (var g in notes) - { - g.Renderer = Renderer; - g.Width = w; - g.Paint(cx, cy, canvas); - } - canvas.TextBaseline = oldBaseLine; - - foreach (var beatEffectKey in BeatEffects) - { - var g = BeatEffects[beatEffectKey]; - g.Paint(cx, cy, canvas); - } - } - - public void UpdateBeamingHelper(float cx) - { - if (BeamingHelper != null && BeamingHelper.IsPositionFrom(TabBarRenderer.StaffId, Beat)) - { - BeamingHelper.RegisterBeatLineX(TabBarRenderer.StaffId, Beat, cx + X + Width, cx + X); - } - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/TabRestGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/TabRestGlyph.cs deleted file mode 100644 index 0099c04e6..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/TabRestGlyph.cs +++ /dev/null @@ -1,66 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Model; -using AlphaTab.Platform; -using AlphaTab.Rendering.Utils; - -namespace AlphaTab.Rendering.Glyphs -{ - class TabRestGlyph : MusicFontGlyph - { - private readonly bool _isVisibleRest; - private readonly Duration _duration; - - public BeamingHelper BeamingHelper { get; set; } - - public TabRestGlyph(float x, float y, bool isVisibleRest, Duration duration) - : base(x, y, 1, ScoreRestGlyph.GetSymbol(duration)) - { - _isVisibleRest = isVisibleRest; - _duration = duration; - } - - public override void DoLayout() - { - if (_isVisibleRest) - { - Width = ScoreRestGlyph.GetSize(_duration) * Scale; - } - else - { - Width = 10 * Scale; - } - } - - public void UpdateBeamingHelper(float cx) - { - if (BeamingHelper != null && BeamingHelper.IsPositionFrom(TabBarRenderer.StaffId, Beat)) - { - BeamingHelper.RegisterBeatLineX(TabBarRenderer.StaffId, Beat, cx + X + Width, cx + X); - } - } - - public override void Paint(float cx, float cy, ICanvas canvas) - { - if (_isVisibleRest) - { - base.Paint(cx, cy, canvas); - } - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Glyphs/TabSlideLineGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/TabSlideLineGlyph.cs deleted file mode 100644 index f4df7a6bd..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/TabSlideLineGlyph.cs +++ /dev/null @@ -1,183 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using AlphaTab.Model; -using AlphaTab.Platform; - -namespace AlphaTab.Rendering.Glyphs -{ - class TabSlideLineGlyph : Glyph - { - private readonly Note _startNote; - private readonly SlideType _type; - private readonly BeatContainerGlyph _parent; - - public TabSlideLineGlyph(SlideType type, Note startNote, BeatContainerGlyph parent) - : base(0, 0) - { - _type = type; - _startNote = startNote; - _parent = parent; - } - - public override void DoLayout() - { - Width = 0; - } - - public override void Paint(float cx, float cy, ICanvas canvas) - { - var startNoteRenderer = (TabBarRenderer)Renderer; - - var sizeX = 12 * Scale; - var sizeY = 3 * Scale; - float startX; - float startY; - float endX; - float endY; - bool waves = false; - - switch (_type) - { - case SlideType.Shift: - case SlideType.Legato: - float startOffsetY; - float endOffsetY; - - if (_startNote.SlideTarget == null) - { - startOffsetY = 0; - endOffsetY = 0; - } - else if (_startNote.SlideTarget.Fret > _startNote.Fret) - { - startOffsetY = sizeY; - endOffsetY = sizeY * -1; - } - else - { - startOffsetY = sizeY * -1; - endOffsetY = sizeY; - } - - startX = cx + startNoteRenderer.X + startNoteRenderer.GetBeatX(_startNote.Beat, BeatXPosition.PostNotes); - startY = cy + startNoteRenderer.Y + startNoteRenderer.GetNoteY(_startNote) + startOffsetY; - if (_startNote.SlideTarget != null) - { - var endNoteRenderer = Renderer.ScoreRenderer.Layout.GetRendererForBar(Renderer.Staff.StaveId, _startNote.SlideTarget.Beat.Voice.Bar); - if (endNoteRenderer == null || endNoteRenderer.Staff != startNoteRenderer.Staff) - { - endX = cx + startNoteRenderer.X + _parent.X; - endY = startY; - } - else - { - endX = cx + endNoteRenderer.X + endNoteRenderer.GetBeatX(_startNote.SlideTarget.Beat, BeatXPosition.OnNotes); - endY = cy + endNoteRenderer.Y + endNoteRenderer.GetNoteY(_startNote.SlideTarget) + endOffsetY; - } - } - else - { - endX = cx + startNoteRenderer.X + _parent.X; - endY = startY; - } - break; - - case SlideType.IntoFromBelow: - endX = cx + startNoteRenderer.X + startNoteRenderer.GetNoteX(_startNote, false); - endY = cy + startNoteRenderer.Y + startNoteRenderer.GetNoteY(_startNote); - - startX = endX - sizeX; - startY = cy + startNoteRenderer.Y + startNoteRenderer.GetNoteY(_startNote) + sizeY; - break; - case SlideType.IntoFromAbove: - endX = cx + startNoteRenderer.X + startNoteRenderer.GetNoteX(_startNote, false); - endY = cy + startNoteRenderer.Y + startNoteRenderer.GetNoteY(_startNote); - - startX = endX - sizeX; - startY = cy + startNoteRenderer.Y + startNoteRenderer.GetNoteY(_startNote) - sizeY; - break; - case SlideType.OutUp: - startX = cx + startNoteRenderer.X + startNoteRenderer.GetNoteX(_startNote); - startY = cy + startNoteRenderer.Y + startNoteRenderer.GetNoteY(_startNote); - - endX = startX + sizeX; - endY = cy + startNoteRenderer.Y + startNoteRenderer.GetNoteY(_startNote) - sizeY; - break; - - case SlideType.OutDown: - startX = cx + startNoteRenderer.X + startNoteRenderer.GetNoteX(_startNote); - startY = cy + startNoteRenderer.Y + startNoteRenderer.GetNoteY(_startNote); - - endX = startX + sizeX; - endY = cy + startNoteRenderer.Y + startNoteRenderer.GetNoteY(_startNote) + sizeY; - break; - - case SlideType.PickSlideDown: - - startX = cx + startNoteRenderer.X + startNoteRenderer.GetNoteX(_startNote); - startY = cy + startNoteRenderer.Y + startNoteRenderer.GetNoteY(_startNote) - sizeY * 2; - - endX = cx + startNoteRenderer.X + startNoteRenderer.GetBeatX(_startNote.Beat, BeatXPosition.EndBeat); - endY = startY + sizeY * 3; - - waves = true; - break; - - case SlideType.PickSlideUp: - - startX = cx + startNoteRenderer.X + startNoteRenderer.GetNoteX(_startNote); - startY = cy + startNoteRenderer.Y + startNoteRenderer.GetNoteY(_startNote) + sizeY; - - endX = cx + startNoteRenderer.X + startNoteRenderer.GetBeatX(_startNote.Beat, BeatXPosition.EndBeat); - endY = startY - sizeY * 3; - - waves = true; - break; - default: - return; - } - - if (waves) - { - var b = endX - startX; - var a = endY - startY; - var c = Math.Sqrt(Math.Pow(a, 2) + Math.Pow(b, 2)); - var angle = (float)(Math.Asin(a / c) * (180 / Math.PI)); - canvas.BeginRotate(startX, startY, angle); - - var glyph = new NoteVibratoGlyph(0, 0, VibratoType.Slight); - glyph.Renderer = Renderer; - glyph.DoLayout(); - glyph.Width = b; - glyph.Paint(0, 0, canvas); - - canvas.EndRotate(); - } - else - { - canvas.BeginPath(); - canvas.MoveTo(startX, startY); - canvas.LineTo(endX, endY); - canvas.Stroke(); - } - - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/TabSlurGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/TabSlurGlyph.cs deleted file mode 100644 index 6ff56d110..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/TabSlurGlyph.cs +++ /dev/null @@ -1,71 +0,0 @@ -using AlphaTab.Model; -using AlphaTab.Platform; -using AlphaTab.Rendering.Utils; - -namespace AlphaTab.Rendering.Glyphs -{ - class TabSlurGlyph : Glyph - { - private readonly Note _startNote; - - public TabSlurGlyph(Note startNote) - : base(0, 0) - { - _startNote = startNote; - } - - private BeamDirection GetBeamDirection(Note note) - { - return note.String > 3 || note.Beat.Notes.Count > 1 && note.String == note.Beat.MaxStringNote.String - ? BeamDirection.Up - : BeamDirection.Down; - } - - public override void Paint(float cx, float cy, ICanvas canvas) - { - // only render slur once per staff - var slurId = "tab.slur." + _startNote.SlurOrigin.Beat.Id + "." + _startNote.SlurOrigin.SlurDestination.Beat.Id; - var renderer = (TabBarRenderer)Renderer; - var isSlurRendered = renderer.Staff.GetSharedLayoutData(slurId, false); - if (!isSlurRendered) - { - renderer.Staff.SetSharedLayoutData(slurId, true); - - var startNoteRenderer = Renderer.ScoreRenderer.Layout.GetRendererForBar(Renderer.Staff.StaveId, _startNote.Beat.Voice.Bar); - var direction = GetBeamDirection(_startNote); - - var startX = cx + startNoteRenderer.X; - var startY = cy + startNoteRenderer.Y + - startNoteRenderer.GetNoteY(_startNote, true); - if (direction == BeamDirection.Down) - { - startY += renderer.GetTabY(1); - } - - - if (_startNote.SlurOrigin.Id == _startNote.Id) - { - startX += startNoteRenderer.GetBeatX(_startNote.Beat, BeatXPosition.MiddleNotes); - } - - var endNote = _startNote.SlurOrigin.SlurDestination; - var endNoteRenderer = Renderer.ScoreRenderer.Layout.GetRendererForBar(Renderer.Staff.StaveId, endNote.Beat.Voice.Bar); - float endX; - float endY = startY; - if (endNoteRenderer == null || startNoteRenderer.Staff != endNoteRenderer.Staff) - { - endNoteRenderer = startNoteRenderer.Staff.BarRenderers[startNoteRenderer.Staff.BarRenderers.Count - 1]; - endX = cx + endNoteRenderer.X + endNoteRenderer.Width; - } - else - { - endX = cx + endNoteRenderer.X + endNoteRenderer.GetBeatX(endNote.Beat, BeatXPosition.MiddleNotes); - } - - TieGlyph.PaintTie(canvas, Scale, startX, startY, endX, endY, direction == BeamDirection.Down); - canvas.Fill(); - } - - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Glyphs/TabTieGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/TabTieGlyph.cs deleted file mode 100644 index 4fe9dcf1c..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/TabTieGlyph.cs +++ /dev/null @@ -1,69 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Model; -using AlphaTab.Rendering.Utils; - -namespace AlphaTab.Rendering.Glyphs -{ - class TabTieGlyph : TieGlyph - { - private readonly Note _startNote; - private readonly Note _endNote; - private readonly bool _forSlide; - - public TabTieGlyph(Note startNote, Note endNote, bool forSlide, bool forEnd = false) - : base(startNote.Beat, endNote.Beat, forEnd) - { - _startNote = startNote; - _endNote = endNote; - _forSlide = forSlide; - } - - private float Offset - { - get { return _forSlide ? 5 * Scale : 0; } - } - - protected override BeamDirection GetBeamDirection(Beat beat, BarRendererBase noteRenderer) - { - return _startNote.String > 3 - ? BeamDirection.Up - : BeamDirection.Down; - } - - protected override float GetStartY(BarRendererBase noteRenderer, BeamDirection direction) - { - return noteRenderer.GetNoteY(_startNote) - Offset; - } - - protected override float GetEndY(BarRendererBase noteRenderer, BeamDirection direction) - { - return noteRenderer.GetNoteY(_endNote) - Offset; - } - - protected override float GetStartX(BarRendererBase noteRenderer) - { - return noteRenderer.GetNoteX(_startNote); - } - - protected override float GetEndX(BarRendererBase noteRenderer) - { - return noteRenderer.GetNoteX(_endNote, false); - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Glyphs/TabTimeSignatureGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/TabTimeSignatureGlyph.cs deleted file mode 100644 index 4211b99cc..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/TabTimeSignatureGlyph.cs +++ /dev/null @@ -1,64 +0,0 @@ -namespace AlphaTab.Rendering.Glyphs -{ - class TabTimeSignatureGlyph : TimeSignatureGlyph - { - protected override float CommonY - { - get - { - var renderer = (TabBarRenderer)Renderer; - return renderer.GetTabY(0); - } - } - - protected override float NumeratorY - { - get - { - var renderer = (TabBarRenderer)Renderer; - var offset = renderer.Bar.Staff.Tuning.Length <= 4 ? 1 / 4f : 1 / 3f; - return renderer.LineOffset * renderer.Bar.Staff.Tuning.Length * offset * Scale; - - } - } - - protected override float DenominatorY - { - get - { - var renderer = (TabBarRenderer)Renderer; - var offset = renderer.Bar.Staff.Tuning.Length <= 4 ? 3 / 5f : 3 / 5f; - return renderer.LineOffset * renderer.Bar.Staff.Tuning.Length * offset * Scale; - } - } - - protected override float CommonScale - { - get - { - return 1; - } - } - - protected override float NumberScale - { - get - { - var renderer = (TabBarRenderer)Renderer; - if (renderer.Bar.Staff.Tuning.Length <= 4) - { - return 0.75f; - } - else - { - return 1; - } - } - } - - public TabTimeSignatureGlyph(float x, float y, int numerator, int denominator, bool isCommon) - : base(x, y, numerator, denominator, isCommon) - { - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Glyphs/TabWhammyBarGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/TabWhammyBarGlyph.cs deleted file mode 100644 index f88e4e846..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/TabWhammyBarGlyph.cs +++ /dev/null @@ -1,348 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.Collections; -using AlphaTab.Model; -using AlphaTab.Platform; -using AlphaTab.Platform.Model; - -namespace AlphaTab.Rendering.Glyphs -{ - class TabWhammyBarGlyph : Glyph - { - private const string TopOffsetSharedDataKey = "tab.whammy.topoffset"; - public const int PerHalfSize = 6; - private const int DashSize = 3; - - private readonly Beat _beat; - private FastList _renderPoints; - private bool _isSimpleDip; - - public TabWhammyBarGlyph(Beat beat) - : base(0, 0) - { - _beat = beat; - _renderPoints = CreateRenderingPoints(beat); - _isSimpleDip = false; - } - - private FastList CreateRenderingPoints(Beat beat) - { - // advanced rendering - if (beat.WhammyBarType == WhammyType.Custom) - { - return beat.WhammyBarPoints; - } - - var renderingPoints = new FastList(); - - // Guitar Pro Rendering Note: - // Last point of bend is always at end of the beat even - // though it might not be 100% correct from timing perspective. - - switch (beat.WhammyBarType) - { - case WhammyType.Dive: - case WhammyType.Hold: - case WhammyType.PrediveDive: - case WhammyType.Predive: - renderingPoints.Add(new BendPoint(0, beat.WhammyBarPoints[0].Value)); - renderingPoints.Add(new BendPoint(BendPoint.MaxPosition, beat.WhammyBarPoints[1].Value)); - break; - case WhammyType.Dip: - renderingPoints.Add(new BendPoint(0, beat.WhammyBarPoints[0].Value)); - renderingPoints.Add(new BendPoint(BendPoint.MaxPosition / 2, beat.WhammyBarPoints[1].Value)); - renderingPoints.Add(new BendPoint(BendPoint.MaxPosition, beat.WhammyBarPoints[beat.WhammyBarPoints.Count - 1].Value)); - break; - } - - return renderingPoints; - } - - - - public override void DoLayout() - { - base.DoLayout(); - - _isSimpleDip = Renderer.Settings.DisplayMode == DisplayMode.SongBook && - _beat.WhammyBarType == WhammyType.Dip; - - // - // Get the min and max values for all combined whammys - BendPoint minValue = null; - BendPoint maxValue = null; - - Beat beat = _beat; - while (beat != null && beat.HasWhammyBar) - { - if (minValue == null || minValue.Value > beat.MinWhammyPoint.Value) - { - minValue = beat.MinWhammyPoint; - } - - if (maxValue == null || maxValue.Value < beat.MaxWhammyPoint.Value) - { - maxValue = beat.MaxWhammyPoint; - } - - beat = beat.NextBeat; - } - - var topOffset = maxValue.Value > 0 ? Math.Abs(GetOffset(maxValue.Value)) : 0; - - if (topOffset > 0 || _beat.WhammyBarPoints[0].Value != 0 || Renderer.Settings.ShowZeroOnDiveWhammy) - { - topOffset += Renderer.Resources.TablatureFont.Size * 2f; - } - - var bottomOffset = minValue.Value < 0 ? Math.Abs(GetOffset(minValue.Value)) : 0; - - Renderer.RegisterOverflowTop(topOffset + bottomOffset); - - var currentOffset = Renderer.Staff.GetSharedLayoutData(TopOffsetSharedDataKey, -1); - if (topOffset > currentOffset) - { - Renderer.Staff.SetSharedLayoutData(TopOffsetSharedDataKey, topOffset); - } - } - - private float GetOffset(int value) - { - if (value == 0) return 0; - - var offset = PerHalfSize * Scale + Platform.Platform.Log2(Math.Abs(value) / 2f) * PerHalfSize * Scale; - if (value < 0) - { - offset = -offset; - } - return offset; - } - - public override void Paint(float cx, float cy, ICanvas canvas) - { - var startNoteRenderer = Renderer; - - Beat endBeat = _beat.NextBeat; - TabBarRenderer endNoteRenderer = null; - BeatXPosition endXPositionType = BeatXPosition.PreNotes; - if (endBeat != null) - { - endNoteRenderer = Renderer.ScoreRenderer.Layout.GetRendererForBar(Renderer.Staff.StaveId, endBeat.Voice.Bar); - if (endNoteRenderer == null || endNoteRenderer.Staff != startNoteRenderer.Staff) - { - endBeat = null; - endNoteRenderer = null; - } - else if (endNoteRenderer != startNoteRenderer && !endBeat.HasWhammyBar) - { - endBeat = null; - endNoteRenderer = null; - } - else - { - endXPositionType = endBeat.HasWhammyBar - && (startNoteRenderer.Settings.DisplayMode != DisplayMode.SongBook || - endBeat.WhammyBarType != WhammyType.Dip) - ? BeatXPosition.MiddleNotes - : BeatXPosition.PreNotes; - } - } - - float startX; - float endX; - - if (_isSimpleDip) - { - startX = cx + startNoteRenderer.X + startNoteRenderer.GetBeatX(_beat, BeatXPosition.OnNotes) - - ScoreWhammyBarGlyph.SimpleDipPadding * Scale; - endX = cx + startNoteRenderer.X + startNoteRenderer.GetBeatX(_beat, BeatXPosition.PostNotes) - + ScoreWhammyBarGlyph.SimpleDipPadding * Scale; - } - else - { - startX = cx + startNoteRenderer.X + startNoteRenderer.GetBeatX(_beat, BeatXPosition.MiddleNotes); - endX = endNoteRenderer == null - ? cx + startNoteRenderer.X + startNoteRenderer.Width - 2 * Scale - : cx + endNoteRenderer.X + endNoteRenderer.GetBeatX(endBeat, endXPositionType); - } - - - var old = canvas.TextAlign; - canvas.TextAlign = TextAlign.Center; - if (_renderPoints.Count >= 2) - { - var dx = (endX - startX) / BendPoint.MaxPosition; - canvas.BeginPath(); - - var zeroY = cy + Renderer.Staff.GetSharedLayoutData(TopOffsetSharedDataKey, 0); - - var slurText = _beat.WhammyStyle == BendStyle.Gradual ? "grad." : ""; - - for (int i = 0, j = _renderPoints.Count - 1; i < j; i++) - { - var firstPt = _renderPoints[i]; - var secondPt = _renderPoints[i + 1]; - var nextPt = i < j - 2 ? _renderPoints[i + 2] : null; - - var isFirst = i == 0; - // draw pre-bend if previous - if (i == 0 && firstPt.Value != 0 && !_beat.IsContinuedWhammy) - { - PaintWhammy(false, new BendPoint(), firstPt, secondPt, startX, zeroY, dx, canvas); - isFirst = false; - } - - PaintWhammy(isFirst, firstPt, secondPt, nextPt, startX, zeroY, dx, canvas, slurText); - - slurText = ""; - } - canvas.Stroke(); - } - canvas.TextAlign = old; - } - - private void PaintWhammy(bool isFirst, BendPoint firstPt, BendPoint secondPt, BendPoint nextPt, float cx, float cy, float dx, ICanvas canvas, string slurText = null) - { - var x1 = cx + dx * firstPt.Offset; - var x2 = cx + dx * secondPt.Offset; - - var y1 = cy - GetOffset(firstPt.Value); - var y2 = cy - GetOffset(secondPt.Value); - - if (firstPt.Offset == secondPt.Offset) - { - var dashSize = DashSize * Scale; - var dashes = Math.Abs(y2 - y1) / (dashSize * 2); - if (dashes < 1) - { - canvas.MoveTo(x1, y1); - canvas.LineTo(x2, y2); - } - else - { - var dashEndY = Math.Max(y1, y2); - var dashStartY = Math.Min(y1, y2); - - while (dashEndY > dashStartY) - { - canvas.MoveTo(x1, dashStartY); - canvas.LineTo(x1, dashStartY + dashSize); - - dashStartY += dashSize * 2; - } - } - canvas.Stroke(); - } - else if (firstPt.Value == secondPt.Value) - { - var dashSize = DashSize * Scale; - var dashes = Math.Abs(x2 - x1) / (dashSize * 2); - if (dashes < 1) - { - canvas.MoveTo(x1, y1); - canvas.LineTo(x2, y2); - } - else - { - var dashEndX = Math.Max(x1, x2); - var dashStartX = Math.Min(x1, x2); - - while (dashEndX > dashStartX) - { - canvas.MoveTo(dashEndX, y1); - canvas.LineTo(dashEndX - dashSize, y1); - - dashEndX -= dashSize * 2; - } - } - canvas.Stroke(); - } - else - { - canvas.MoveTo(x1, y1); - canvas.LineTo(x2, y2); - } - - var res = canvas.Resources; - - if (isFirst && !_beat.IsContinuedWhammy && !_isSimpleDip) - { - float y = y1; - y -= res.TablatureFont.Size + (2 * Scale); - if (Renderer.Settings.ShowZeroOnDiveWhammy) - { - canvas.FillText("0", x1, y); - } - - if (slurText != null) - { - y -= res.TablatureFont.Size + (2 * Scale); - canvas.FillText(slurText, x1, y); - } - } - - var dV = Math.Abs(secondPt.Value); - if ((dV != 0 || (Renderer.Settings.ShowZeroOnDiveWhammy && !_isSimpleDip)) && firstPt.Value != secondPt.Value) - { - - var s = ""; - if (secondPt.Value < 0) - { - s += "-"; - } - - if (dV >= 4) - { - int steps = dV / 4; - s += steps; - // Quaters - dV -= steps * 4; - } - else if (dV == 0) - { - s += "0"; - } - - if (dV > 0) - { - s += TabBendGlyph.GetFractionSign(dV); - } - - float y; - if (_isSimpleDip) - { - y = Math.Min(y1, y2) - res.TablatureFont.Size - (2 * Scale); - } - else - { - y = firstPt.Offset == secondPt.Offset ? Math.Min(y1, y2) : y2; - y -= res.TablatureFont.Size + (2 * Scale); - if (nextPt != null && nextPt.Value > secondPt.Value) - { - y -= (2 * Scale); - } - } - - var x = x2; - - canvas.FillText(s, x, y); - } - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Glyphs/TempoGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/TempoGlyph.cs deleted file mode 100644 index c06a3c80d..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/TempoGlyph.cs +++ /dev/null @@ -1,47 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Platform; - -namespace AlphaTab.Rendering.Glyphs -{ - class TempoGlyph : EffectGlyph - { - private readonly int _tempo; - - public TempoGlyph(float x, float y, int tempo) - : base(x, y) - { - _tempo = tempo; - } - - public override void DoLayout() - { - base.DoLayout(); - Height = 25 * Scale; - } - - public override void Paint(float cx, float cy, ICanvas canvas) - { - var res = Renderer.Resources; - canvas.Font = res.MarkerFont; - - canvas.FillMusicFontSymbol(cx + X, cy + Y + Height * 0.8f, Scale * 0.75f, MusicFontSymbol.Tempo); - canvas.FillText("= " + _tempo, cx + X + (Height / 2), cy + Y + canvas.Font.Size / 2); - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/TextGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/TextGlyph.cs deleted file mode 100644 index ff81de968..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/TextGlyph.cs +++ /dev/null @@ -1,58 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Platform; -using AlphaTab.Platform.Model; - -namespace AlphaTab.Rendering.Glyphs -{ - class TextGlyph : EffectGlyph - { - private readonly string[] _lines; - - public Font Font { get; set; } - public TextAlign TextAlign { get; set; } - - public TextGlyph(float x, float y, string text, Font font, TextAlign textAlign = TextAlign.Left) - : base(x, y) - { - _lines = text.Split('\n'); - Font = font; - TextAlign = textAlign; - } - - public override void DoLayout() - { - base.DoLayout(); - Height = Font.Size * _lines.Length; - } - - public override void Paint(float cx, float cy, ICanvas canvas) - { - canvas.Font = Font; - var old = canvas.TextAlign; - var y = cy + Y; - foreach (var line in _lines) - { - canvas.TextAlign = TextAlign; - canvas.FillText(line, cx + X, y); - canvas.TextAlign = old; - y += Font.Size; - } - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/TieGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/TieGlyph.cs deleted file mode 100644 index 53b435da3..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/TieGlyph.cs +++ /dev/null @@ -1,197 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.Model; -using AlphaTab.Platform; -using AlphaTab.Rendering.Utils; - -namespace AlphaTab.Rendering.Glyphs -{ - class TieGlyph : Glyph - { - protected Beat StartBeat; - protected Beat EndBeat; - protected float YOffset; - private readonly bool _forEnd; - - public TieGlyph(Beat startBeat, Beat endBeat, bool forEnd) - : base(0, 0) - { - StartBeat = startBeat; - EndBeat = endBeat; - _forEnd = forEnd; - } - - public override void DoLayout() - { - Width = 0; - } - - - - public override void Paint(float cx, float cy, ICanvas canvas) - { - if (EndBeat == null) return; - - var startNoteRenderer = Renderer.ScoreRenderer.Layout.GetRendererForBar(Renderer.Staff.StaveId, StartBeat.Voice.Bar); - var endNoteRenderer = Renderer.ScoreRenderer.Layout.GetRendererForBar(Renderer.Staff.StaveId, EndBeat.Voice.Bar); - - float startX = 0; - float endX = 0; - - float startY = 0; - float endY = 0; - - var shouldDraw = false; - - var direction = GetBeamDirection(StartBeat, startNoteRenderer); - // if we are on the tie start, we check if we - // either can draw till the end note, or we just can draw till the bar end - if (!_forEnd) - { - // line break or bar break - if (startNoteRenderer != endNoteRenderer) - { - startX = cx + startNoteRenderer.X + GetStartX(startNoteRenderer); - startY = cy + startNoteRenderer.Y + GetStartY(startNoteRenderer, direction) + YOffset; - - // line break: to bar end - if (endNoteRenderer == null || startNoteRenderer.Staff != endNoteRenderer.Staff) - { - endX = cx + startNoteRenderer.X + startNoteRenderer.Width; - endY = startY; - } - // bar break: to tie destination - // differs only by addition of EndNote X coordinate - else - { - endX = cx + endNoteRenderer.X + GetEndX(endNoteRenderer); - endY = cy + endNoteRenderer.Y + GetEndY(endNoteRenderer, direction) + YOffset; - } - } - else - { - startX = cx + startNoteRenderer.X + GetStartX(startNoteRenderer); - endX = cx + endNoteRenderer.X + GetEndX(endNoteRenderer); - startY = cy + startNoteRenderer.Y + GetStartY(startNoteRenderer, direction) + YOffset; - endY = cy + endNoteRenderer.Y + GetEndY(endNoteRenderer, direction) + YOffset; - } - shouldDraw = true; - } - // if we draw for the tie end, we only draw a tie if we had a line break between start and end - // in this case there will be a tie from bar start to the note - else if (startNoteRenderer.Staff != endNoteRenderer.Staff) - { - startX = cx + endNoteRenderer.X; - endX = cx + endNoteRenderer.X + GetEndX(endNoteRenderer); - - startY = cy + endNoteRenderer.Y + GetEndY(endNoteRenderer, direction) + YOffset; - endY = startY; - - shouldDraw = true; - } - - if (shouldDraw) - { - PaintTie(canvas, Scale, startX, startY, endX, endY, direction == BeamDirection.Down); - - canvas.Fill(); - } - } - - protected virtual BeamDirection GetBeamDirection(Beat beat, BarRendererBase noteRenderer) - { - return BeamDirection.Down; - } - - - protected virtual float GetStartY(BarRendererBase noteRenderer, BeamDirection direction) - { - return 0; - } - - protected virtual float GetEndY(BarRendererBase noteRenderer, BeamDirection direction) - { - return 0; - } - - protected virtual float GetStartX(BarRendererBase noteRenderer) - { - return 0; - } - - protected virtual float GetEndX(BarRendererBase noteRenderer) - { - return 0; - } - - public static void PaintTie(ICanvas canvas, float scale, float x1, float y1, float x2, float y2, - bool down = false, float offset = 22, float size = 4) - { - if (x1 == x2 && y1 == y2) - { - return; - } - // ensure endX > startX - if (x2 < x1) - { - var t = x1; - x1 = x2; - x2 = t; - t = y1; - y1 = y2; - y2 = t; - } - // - // calculate control points - // - - offset *= scale; - size *= scale; - // normal vector - var normalVectorX = (y2 - y1); - var normalVectorY = (x2 - x1); - var length = (float)Math.Sqrt((normalVectorX * normalVectorX) + (normalVectorY * normalVectorY)); - if (down) - normalVectorX *= -1; - else - normalVectorY *= -1; - - // make to unit vector - normalVectorX /= length; - normalVectorY /= length; - - // center of connection - var centerX = (x2 + x1) / 2; - var centerY = (y2 + y1) / 2; - - // control points - var cp1X = centerX + (offset * normalVectorX); - var cp1Y = centerY + (offset * normalVectorY); - var cp2X = centerX + ((offset - size) * normalVectorX); - var cp2Y = centerY + ((offset - size) * normalVectorY); - - - canvas.BeginPath(); - canvas.MoveTo(x1, y1); - canvas.QuadraticCurveTo(cp1X, cp1Y, x2, y2); - canvas.QuadraticCurveTo(cp2X, cp2Y, x1, y1); - canvas.ClosePath(); - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/TimeSignatureGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/TimeSignatureGlyph.cs deleted file mode 100644 index 879af9966..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/TimeSignatureGlyph.cs +++ /dev/null @@ -1,74 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Rendering.Glyphs -{ - abstract class TimeSignatureGlyph : GlyphGroup - { - private readonly int _numerator; - private readonly int _denominator; - private readonly bool _isCommon; - - public TimeSignatureGlyph(float x, float y, int numerator, int denominator, bool isCommon) - : base(x, y) - { - _numerator = numerator; - _denominator = denominator; - _isCommon = isCommon; - } - - protected abstract float CommonY { get; } - protected abstract float NumeratorY { get; } - protected abstract float DenominatorY { get; } - protected abstract float CommonScale { get; } - protected abstract float NumberScale { get; } - - public override void DoLayout() - { - if (_isCommon && _numerator == 2 && _denominator == 2) - { - var common = new MusicFontGlyph(0, CommonY, CommonScale, MusicFontSymbol.TimeSignatureCutCommon); - common.Width = 14 * Scale; - AddGlyph(common); - base.DoLayout(); - } - else if (_isCommon && _numerator == 4 && _denominator == 4) - { - var common = new MusicFontGlyph(0, CommonY, CommonScale, MusicFontSymbol.TimeSignatureCommon); - common.Width = 14 * Scale; - AddGlyph(common); - base.DoLayout(); - } - else - { - var numerator = new NumberGlyph(0, NumeratorY, _numerator, NumberScale); - var denominator = new NumberGlyph(0, DenominatorY, _denominator, NumberScale); - - AddGlyph(numerator); - AddGlyph(denominator); - - base.DoLayout(); - - for (int i = 0, j = Glyphs.Count; i < j; i++) - { - var g = Glyphs[i]; - g.X = (Width - g.Width) / 2; - } - } - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/TremoloPickingGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/TremoloPickingGlyph.cs deleted file mode 100644 index 95b8fcd27..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/TremoloPickingGlyph.cs +++ /dev/null @@ -1,49 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Model; - -namespace AlphaTab.Rendering.Glyphs -{ - class TremoloPickingGlyph : MusicFontGlyph - { - public TremoloPickingGlyph(float x, float y, Duration duration) - : base(x, y, 1, GetSymbol(duration)) - { - } - - public override void DoLayout() - { - Width = 12 * Scale; - } - - private static MusicFontSymbol GetSymbol(Duration duration) - { - switch (duration) - { - case Duration.ThirtySecond: - return MusicFontSymbol.TremoloPickingThirtySecond; - case Duration.Sixteenth: - return MusicFontSymbol.TremoloPickingSixteenth; - case Duration.Eighth: - return MusicFontSymbol.TremoloPickingEighth; - default: - return MusicFontSymbol.None; - } - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/TrillGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/TrillGlyph.cs deleted file mode 100644 index 62d8594c0..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/TrillGlyph.cs +++ /dev/null @@ -1,62 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.Platform; - -namespace AlphaTab.Rendering.Glyphs -{ - class TrillGlyph : EffectGlyph - { - - public TrillGlyph(float x, float y) - : base(x, y) - { - } - - public override void DoLayout() - { - base.DoLayout(); - Height = 20*Scale; - } - - public override void Paint(float cx, float cy, ICanvas canvas) - { - var res = Renderer.Resources; - - canvas.Font = res.MarkerFont; - - var textw = canvas.MeasureText("tr"); - canvas.FillText("tr", cx + X, cy + Y + canvas.Font.Size / 2); - - var startX = textw + 3 * Scale; - var endX = Width - startX; - var waveScale = 1.2f; - var step = 11 * Scale * waveScale; - var loops = Math.Max(1, ((endX - startX) / step)); - - - var loopX = startX; - var loopY = cy + Y + Height; - for (var i = 0; i < loops; i++) - { - canvas.FillMusicFontSymbol(cx + X + loopX, loopY, waveScale, MusicFontSymbol.WaveHorizontalSlight); - loopX += step; - } - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/TripletFeelGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/TripletFeelGlyph.cs deleted file mode 100644 index 60004553b..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/TripletFeelGlyph.cs +++ /dev/null @@ -1,167 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Model; -using AlphaTab.Platform; -using AlphaTab.Platform.Model; - -namespace AlphaTab.Rendering.Glyphs -{ - class TripletFeelGlyph : EffectGlyph - { - private const float NoteScale = 0.40f; - private const int NoteHeight = 12; - private const int NoteSeparation = 12; - private const int BarHeight = 2; - private const int BarSeparation = 3; - - private readonly TripletFeel _tripletFeel; - - public TripletFeelGlyph(TripletFeel tripletFeel) : base(0, 0) - { - _tripletFeel = tripletFeel; - } - - public override void DoLayout() - { - base.DoLayout(); - Height = 25 * Scale; - } - - public override void Paint(float cx, float cy, ICanvas canvas) - { - cx += X; - cy += Y; - - var noteY = cy + Height * 0.75f; - canvas.Font = Renderer.Resources.EffectFont; - - canvas.FillText("(", cx, cy + Height * 0.3f); - - var leftNoteX = cx + (10 * Scale); - var rightNoteX = cx + (40 * Scale); - - switch (_tripletFeel) - { - case TripletFeel.NoTripletFeel: - RenderBarNote(leftNoteX, noteY, NoteScale, canvas, new[] { BarType.Full }); - RenderBarNote(rightNoteX, noteY, NoteScale, canvas, new[] { BarType.Full }); - break; - - case TripletFeel.Triplet8th: - RenderBarNote(leftNoteX, noteY, NoteScale, canvas, new[] { BarType.Full }); - - canvas.FillMusicFontSymbol(rightNoteX, noteY, NoteScale, MusicFontSymbol.Tempo); - canvas.FillMusicFontSymbol(rightNoteX + (NoteSeparation * Scale), noteY, NoteScale, MusicFontSymbol.NoteEighth); - - RenderTriplet(rightNoteX, cy, canvas); - break; - - case TripletFeel.Triplet16th: - RenderBarNote(leftNoteX, noteY, NoteScale, canvas, new[] { BarType.Full, BarType.Full }); - - RenderBarNote(rightNoteX, noteY, NoteScale, canvas, new[] { BarType.Full, BarType.PartialRight }); - RenderTriplet(rightNoteX, cy, canvas); - break; - - case TripletFeel.Dotted8th: - RenderBarNote(leftNoteX, noteY, NoteScale, canvas, new[] { BarType.Full }); - - RenderBarNote(rightNoteX, noteY, NoteScale, canvas, new[] { BarType.Full, BarType.PartialRight }); - canvas.FillCircle(rightNoteX + (9 * Scale), noteY, Scale); - break; - - case TripletFeel.Dotted16th: - RenderBarNote(leftNoteX, noteY, NoteScale, canvas, new[] { BarType.Full, BarType.Full }); - - RenderBarNote(rightNoteX, noteY, NoteScale, canvas, new[] { BarType.Full, BarType.Full, BarType.PartialRight }); - canvas.FillCircle(rightNoteX + (9 * Scale), noteY, Scale); - break; - - case TripletFeel.Scottish8th: - RenderBarNote(leftNoteX, noteY, NoteScale, canvas, new[] { BarType.Full }); - - RenderBarNote(rightNoteX, noteY, NoteScale, canvas, new[] { BarType.Full, BarType.PartialLeft, }); - canvas.FillCircle(rightNoteX + (NoteSeparation * Scale) + (8 * Scale), noteY, Scale); - break; - - case TripletFeel.Scottish16th: - RenderBarNote(leftNoteX, noteY, NoteScale, canvas, new[] { BarType.Full, BarType.Full }); - - RenderBarNote(rightNoteX, noteY, NoteScale, canvas, new[] { BarType.Full, BarType.Full, BarType.PartialLeft, }); - canvas.FillCircle(rightNoteX + (NoteSeparation * Scale) + (8 * Scale), noteY, Scale); - break; - } - - canvas.FillText("=", cx + (30 * Scale), cy + (5 * Scale)); - canvas.FillText(")", cx + (65 * Scale), cy + Height * 0.3f); - } - - private enum BarType - { - Full, - PartialLeft, - PartialRight - } - - private void RenderBarNote(float cx, float noteY, float noteScale, ICanvas canvas, BarType[] bars) - { - canvas.FillMusicFontSymbol(cx, noteY, noteScale, MusicFontSymbol.Tempo); - var partialBarWidth = (NoteSeparation / 2f) * Scale; - for (int i = 0; i < bars.Length; i++) - { - switch (bars[i]) - { - case BarType.Full: - canvas.FillRect(cx + (4 * Scale), noteY - (NoteHeight * Scale) + (BarSeparation * Scale * i), NoteSeparation * Scale, BarHeight * Scale); - break; - case BarType.PartialLeft: - canvas.FillRect(cx + (4 * Scale), noteY - (NoteHeight * Scale) + (BarSeparation * Scale * i), partialBarWidth, BarHeight * Scale); - break; - case BarType.PartialRight: - canvas.FillRect(cx + (4 * Scale) + partialBarWidth, noteY - (NoteHeight * Scale) + (BarSeparation * Scale * i), partialBarWidth, BarHeight * Scale); - break; - } - } - canvas.FillMusicFontSymbol(cx + (NoteSeparation * Scale), noteY, noteScale, MusicFontSymbol.Tempo); - } - - private void RenderTriplet(float cx, float cy, ICanvas canvas) - { - cy += 2 * Scale; - var font = Renderer.Resources.EffectFont; - canvas.Font = new Font(font.Family, font.Size * 0.8f, font.Style); - - var rightX = cx + NoteSeparation * Scale + 3 * Scale; - - canvas.BeginPath(); - canvas.MoveTo(cx, cy + (3 * Scale)); - canvas.LineTo(cx, cy); - canvas.LineTo(cx + (5 * Scale), cy); - - canvas.MoveTo(rightX + (5 * Scale), cy + (3 * Scale)); - canvas.LineTo(rightX + (5 * Scale), cy); - canvas.LineTo(rightX, cy); - - canvas.Stroke(); - - canvas.FillText("3", cx + (7 * Scale), cy - (10 * Scale)); - - canvas.Font = font; - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/TuningGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/TuningGlyph.cs deleted file mode 100644 index e5c0c8af9..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/TuningGlyph.cs +++ /dev/null @@ -1,67 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.Model; - -namespace AlphaTab.Rendering.Glyphs -{ - class TuningGlyph : GlyphGroup - { - private readonly float _scale; - private readonly RenderingResources _resources; - public float Height { get; set; } - - public TuningGlyph(float x, float y, - float scale, RenderingResources resources, - Tuning tuning) : base(x, y) - { - _scale = scale; - _resources = resources; - CreateGlyphs(tuning); - } - - private void CreateGlyphs(Tuning tuning) - { - // Name - AddGlyph(new TextGlyph(0, 0, tuning.Name, _resources.EffectFont)); - Height += 15 * _scale; - - if (!tuning.IsStandard) - { - // Strings - var stringsPerColumn = (int)Math.Ceiling(tuning.Tunings.Length / 2.0); - - var currentX = 0f; - var currentY = Height; - for (int i = 0, j = tuning.Tunings.Length; i < j; i++) - { - var str = "(" + (i + 1) + ") = " + Tuning.GetTextForTuning(tuning.Tunings[i], false); - AddGlyph(new TextGlyph(currentX, currentY, str, _resources.EffectFont)); - currentY += Height; - if (i == stringsPerColumn - 1) - { - currentY = Height; - currentX += (43 * _scale); - } - } - - Height += (stringsPerColumn * (15 * _scale)); - } - } - } -} diff --git a/Source/AlphaTab/Rendering/Glyphs/VoiceContainerGlyph.cs b/Source/AlphaTab/Rendering/Glyphs/VoiceContainerGlyph.cs deleted file mode 100644 index dc8c2f8d4..000000000 --- a/Source/AlphaTab/Rendering/Glyphs/VoiceContainerGlyph.cs +++ /dev/null @@ -1,168 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using AlphaTab.Collections; -using AlphaTab.Model; -using AlphaTab.Platform; -using AlphaTab.Platform.Model; -using AlphaTab.Rendering.Staves; - -namespace AlphaTab.Rendering.Glyphs -{ - /// - /// This glyph acts as container for handling - /// multiple voice rendering - /// - class VoiceContainerGlyph : GlyphGroup - { - public const string KeySizeBeat = "Beat"; - - public FastList BeatGlyphs { get; set; } - - public Voice Voice { get; set; } - public float MinWidth { get; set; } - - public VoiceContainerGlyph(float x, float y, Voice voice) - : base(x, y) - { - Voice = voice; - BeatGlyphs = new FastList(); - } - - public void ScaleToWidth(float width) - { - var force = Renderer.LayoutingInfo.SpaceToForce(width); - ScaleToForce(force); - } - - private void ScaleToForce(float force) - { - Width = Renderer.LayoutingInfo.CalculateVoiceWidth(force); - var positions = Renderer.LayoutingInfo.BuildOnTimePositions(force); - var beatGlyphs = BeatGlyphs; - for (int i = 0, j = beatGlyphs.Count; i < j; i++) - { - var currentBeatGlyph = beatGlyphs[i]; - var time = currentBeatGlyph.Beat.AbsoluteDisplayStart; - currentBeatGlyph.X = positions[time] - currentBeatGlyph.OnTimeX; - - // size always previousl glyph after we know the position - // of the next glyph - if (i > 0) - { - var beatWidth = currentBeatGlyph.X - beatGlyphs[i - 1].X; - beatGlyphs[i - 1].ScaleToWidth(beatWidth); - } - - // for the last glyph size based on the full width - if (i == j - 1) - { - float beatWidth = Width - beatGlyphs[beatGlyphs.Count - 1].X; - currentBeatGlyph.ScaleToWidth(beatWidth); - } - } - } - - public void RegisterLayoutingInfo(BarLayoutingInfo info) - { - info.UpdateVoiceSize(Width); - var beatGlyphs = BeatGlyphs; - foreach (var b in beatGlyphs) - { - b.RegisterLayoutingInfo(info); - } - } - - public void ApplyLayoutingInfo(BarLayoutingInfo info) - { - var beatGlyphs = BeatGlyphs; - foreach (var b in beatGlyphs) - { - b.ApplyLayoutingInfo(info); - } - ScaleToForce(Math.Max(Renderer.Settings.StretchForce, info.MinStretchForce)); - } - - public override void AddGlyph(Glyph g) - { - g.X = BeatGlyphs.Count == 0 - ? 0 - : BeatGlyphs[BeatGlyphs.Count - 1].X + BeatGlyphs[BeatGlyphs.Count - 1].Width; - g.Renderer = Renderer; - g.DoLayout(); - BeatGlyphs.Add((BeatContainerGlyph)g); - Width = g.X + g.Width; - } - - public override void DoLayout() - { - MinWidth = Width; - } - - //private static Random Random = new Random(); - public override void Paint(float cx, float cy, ICanvas canvas) - { - //canvas.Color = Color.Random(); - //canvas.StrokeRect(cx + X, cy + Y, Width, 100); - - //if (Voice.Index == 0) - //{ - // PaintSprings(cx + X, cy + Y, canvas); - //} - canvas.Color = Voice.Index == 0 - ? Renderer.ScoreRenderer.RenderingResources.MainGlyphColor - : Renderer.ScoreRenderer.RenderingResources.SecondaryGlyphColor; - - for (int i = 0, j = BeatGlyphs.Count; i < j; i++) - { - BeatGlyphs[i].Paint(cx + X, cy + Y, canvas); - } - } - - //private void PaintSprings(float x, float y, ICanvas canvas) - //{ - // y += 40; - - // var force = Renderer.LayoutingInfo.SpaceToForce(Width); - - // var positions = Renderer.LayoutingInfo.BuildOnTimePositions(force); - // canvas.Color = new Color(255, 0, 0); - - // foreach (var time in positions) - // { - // var springX = positions[time]; - // var spring = Renderer.LayoutingInfo.Springs[time]; - - // canvas.BeginPath(); - // canvas.MoveTo(x + springX, y); - // canvas.LineTo(x + springX, y + 10); - // canvas.Stroke(); - - // canvas.BeginPath(); - // canvas.MoveTo(x + springX, y + 5); - // canvas.LineTo(x + springX + Renderer.LayoutingInfo.CalculateWidth(force, spring.SpringConstant), y + 5); - // canvas.Stroke(); - - // canvas.TextAlign = TextAlign.Center; - // canvas.FillText(time.ToString(), x + springX, y + 10); - // } - //} - } - -} diff --git a/Source/AlphaTab/Rendering/IEffectBarRendererInfo.cs b/Source/AlphaTab/Rendering/IEffectBarRendererInfo.cs deleted file mode 100644 index a915ad7ab..000000000 --- a/Source/AlphaTab/Rendering/IEffectBarRendererInfo.cs +++ /dev/null @@ -1,81 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Model; -using AlphaTab.Rendering.Glyphs; - -namespace AlphaTab.Rendering -{ - /// - /// A public class implementing this public interface can provide the - /// data needed by a EffectBarRenderer to create effect glyphs dynamically. - /// - interface IEffectBarRendererInfo - { - /// - /// Gets the unique effect name for this effect. (Used for grouping) - /// - string EffectId { get; } - - /// - /// Gets a value indicating whether this effect can share the space - /// with other effects if required. - /// (Example: tempo and dynamics don't share their space with other effects, a let-ring and palm-mute will share the space if possible) - /// - /// true if this effect bar should only be created once for the first track, otherwise false. - bool CanShareBand { get; } - - /// - /// Gets a value indicating whether this effect glyphs - /// should only be added once on the first track if multiple tracks are rendered. - /// (Example: this allows to render the tempo changes only once) - /// - /// true if this effect bar should only be created once for the first track, otherwise false. - bool HideOnMultiTrack { get; } - - /// - /// Checks whether the given beat has the appropriate effect set and - /// needs a glyph creation - /// - /// - /// the beat storing the data - /// true if the beat has the effect set, otherwise false. - bool ShouldCreateGlyph(Settings settings, Beat beat); - - /// - /// Gets the sizing mode of the glyphs created by this info. - /// - /// the sizing mode to apply to the glyphs during layout - EffectBarGlyphSizing SizingMode { get; } - - /// - /// Creates a new effect glyph for the given beat. - /// - /// the renderer which requests for glyph creation - /// the beat storing the data - /// the glyph which needs to be added to the renderer - EffectGlyph CreateNewGlyph(BarRendererBase renderer, Beat beat); - - /// - /// Checks whether an effect glyph can be expanded to a particular beat. - /// - /// the beat which already has the glyph applied - /// the beat which the glyph should get expanded to - /// true if the glyph can be expanded, false if a new glyph needs to be created. - bool CanExpand(Beat from, Beat to); - } -} diff --git a/Source/AlphaTab/Rendering/IScoreRenderer.cs b/Source/AlphaTab/Rendering/IScoreRenderer.cs deleted file mode 100644 index 9fcfdfc7c..000000000 --- a/Source/AlphaTab/Rendering/IScoreRenderer.cs +++ /dev/null @@ -1,123 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using AlphaTab.Model; -using AlphaTab.Rendering.Utils; - -namespace AlphaTab.Rendering -{ - /// - /// Represents the public interface of the component that can render scores. - /// - public interface IScoreRenderer - { - /// - /// Gets or sets the lookup which allows fast access to beats at a given position. - /// - BoundsLookup BoundsLookup { get; } - - /// - /// Invalidates the drawn music sheet and initiates a redraw. - /// - void Invalidate(); - - /// - /// Triggers a relayout to the given size including redrawing. - /// - /// - void Resize(int width); - - /// - /// Initiates the rendering of the specified tracks of the given score. - /// - /// The score defining the tracks. - /// The indexes of the tracks to draw. - void Render(Score score, int[] trackIndexes); - - /// - /// Occurs before the rendering of the tracks starts. - /// - event Action PreRender; - /// - /// Occurs after the rendering of the tracks finished. - /// - event Action RenderFinished; - /// - /// Occurs whenever a part of the whole music sheet is rendered and can be displayed. - /// - event Action PartialRenderFinished; - /// - /// Occurs when the whole rendering and layout process finished. - /// - event Action PostRenderFinished; - /// - /// Occurs whenever an error happens. - /// - event Action Error; - - /// - /// Updates the settings to the given object. - /// - /// - void UpdateSettings(Settings settings); - - /// - /// Destroys the renderer. - /// - void Destroy(); - } - - /// - /// This eventargs define the details about the rendering and layouting process and are - /// provided whenever a part of of the music sheet is rendered. - /// - public class RenderFinishedEventArgs - { - /// - /// Gets or sets the width of the current rendering result. - /// - public float Width { get; set; } - /// - /// Gets or sets the height of the current rendering result. - /// - public float Height { get; set; } - - /// - /// Gets or sets the currently known total width of the final music sheet. - /// - public float TotalWidth { get; set; } - /// - /// Gets or sets the currently known total height of the final music sheet. - /// - public float TotalHeight { get; set; } - /// - /// Gets or sets the index of the first masterbar that was rendered in this result. - /// - public int FirstMasterBarIndex { get; set; } - /// - /// Gets or sets the index of the last masterbar that was rendered in this result. - /// - public int LastMasterBarIndex { get; set; } - - /// - /// Gets or sets the render engine specific result object which contains the rendered music sheet. - /// - public object RenderResult { get; set; } - } -} diff --git a/Source/AlphaTab/Rendering/Layout/HeaderFooterElements.cs b/Source/AlphaTab/Rendering/Layout/HeaderFooterElements.cs deleted file mode 100644 index 4dc3038f9..000000000 --- a/Source/AlphaTab/Rendering/Layout/HeaderFooterElements.cs +++ /dev/null @@ -1,74 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; - -namespace AlphaTab.Rendering.Layout -{ - /// - /// A list of the elements which can be shown in the header and footer - /// of a rendered song sheet. All values can be combined using bit-operators as they are flags. - /// - [Flags] - enum HeaderFooterElements - { - /// - /// No elements get rendered. - /// - None = 0x0, - /// - /// Enables rendering of the title. - /// - Title = 0x1, - /// - /// Enables rendering of the subtitle. - /// - SubTitle = 0x2, - /// - /// Enables rendering of the artist. - /// - Artist = 0x4, - /// - /// Enables rendering of the album. - /// - Album = 0x8, - /// - /// Enables rendering of the words. - /// - Words = 0x10, - /// - /// Enables rendering of the music. - /// - Music = 0x20, - /// - /// Enables rendering of the words and music. - /// - WordsAndMusic = 0x40, - /// - /// Enables rendering of the copyright. - /// - Copyright = 0x80, - /// - /// Enables rendering of the page number. - /// - PageNumber = 0x100, - /// - /// Enables rendering of all elements. - /// - All = None | Title | SubTitle | Artist | Album | Words | Music | WordsAndMusic | Copyright | PageNumber - } -} diff --git a/Source/AlphaTab/Rendering/Layout/HorizontalScreenLayout.cs b/Source/AlphaTab/Rendering/Layout/HorizontalScreenLayout.cs deleted file mode 100644 index 6e1bf65b1..000000000 --- a/Source/AlphaTab/Rendering/Layout/HorizontalScreenLayout.cs +++ /dev/null @@ -1,200 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.Collections; -using AlphaTab.Model; -using AlphaTab.Platform.Model; -using AlphaTab.Rendering.Staves; -using AlphaTab.Rendering.Utils; -using AlphaTab.Util; - -namespace AlphaTab.Rendering.Layout -{ - class HorizontalScreenLayoutPartialInfo - { - public float Width { get; set; } - public FastList MasterBars { get; set; } - - public HorizontalScreenLayoutPartialInfo() - { - MasterBars = new FastList(); - } - } - - /// - /// This layout arranges the bars all horizontally - /// - class HorizontalScreenLayout : ScoreLayout - { - // left top right bottom - public static readonly float[] PagePadding = { 20, 20, 20, 20 }; - public const float GroupSpacing = 20; - - private StaveGroup _group; - private float[] _pagePadding; - - public override string Name { get { return "HorizontalScreen"; } } - public HorizontalScreenLayout(ScoreRenderer renderer) - : base(renderer) - { - } - - public override bool SupportsResize - { - get { return false; } - } - - public override void Resize() - { - // resizing has no effect on this layout - } - - protected override void DoLayoutAndRender() - { - _pagePadding = Renderer.Settings.Layout.Get("padding", PagePadding); - if (_pagePadding.Length == 1) - { - _pagePadding = new[] - { - _pagePadding[0], - _pagePadding[0], - _pagePadding[0], - _pagePadding[0] - }; - } - else if (_pagePadding.Length == 2) - { - _pagePadding = new[] - { - _pagePadding[0], - _pagePadding[1], - _pagePadding[0], - _pagePadding[1] - }; - } - - var score = Renderer.Score; - var canvas = Renderer.Canvas; - - var startIndex = Renderer.Settings.Layout.Get("start", 1); - startIndex--; // map to array index - startIndex = Math.Min(score.MasterBars.Count - 1, Math.Max(0, startIndex)); - var currentBarIndex = startIndex; - - var endBarIndex = Renderer.Settings.Layout.Get("count", score.MasterBars.Count); - if (endBarIndex < 0) endBarIndex = score.MasterBars.Count; - endBarIndex = startIndex + endBarIndex - 1; // map count to array index - endBarIndex = Math.Min(score.MasterBars.Count - 1, Math.Max(0, endBarIndex)); - - _group = CreateEmptyStaveGroup(); - _group.IsLast = true; - _group.X = _pagePadding[0]; - _group.Y = _pagePadding[1]; - - var countPerPartial = Renderer.Settings.Layout.Get("countPerPartial", 10); - var partials = new FastList(); - - var currentPartial = new HorizontalScreenLayoutPartialInfo(); - while (currentBarIndex <= endBarIndex) - { - var result = _group.AddBars(Renderer.Tracks, currentBarIndex); - - // if we detect that the new renderer is linked to the previous - // renderer, we need to put it into the previous partial - if (currentPartial.MasterBars.Count == 0 && result.IsLinkedToPrevious && partials.Count > 0) - { - var previousPartial = partials[partials.Count - 1]; - previousPartial.MasterBars.Add(score.MasterBars[currentBarIndex]); - previousPartial.Width += result.Width; - } - else - { - currentPartial.MasterBars.Add(score.MasterBars[currentBarIndex]); - currentPartial.Width += result.Width; - // no targetPartial here because previous partials already handled this code - if (currentPartial.MasterBars.Count >= countPerPartial) - { - if (partials.Count == 0) - { - currentPartial.Width += _group.X + _group.AccoladeSpacing; - } - partials.Add(currentPartial); - Logger.Info(Name, - "Finished partial from bar " + currentPartial.MasterBars[0].Index + " to " + - currentPartial.MasterBars[currentPartial.MasterBars.Count - 1].Index); - currentPartial = new HorizontalScreenLayoutPartialInfo(); - } - } - - currentBarIndex++; - } - - // don't miss the last partial if not empty - if (currentPartial.MasterBars.Count > 0) - { - if (partials.Count == 0) - { - currentPartial.Width += _group.X + _group.AccoladeSpacing; - } - partials.Add(currentPartial); - Logger.Info(Name, - "Finished partial from bar " + currentPartial.MasterBars[0].Index + " to " + - currentPartial.MasterBars[currentPartial.MasterBars.Count - 1].Index); - } - - _group.FinalizeGroup(); - - Height = _group.Y + _group.Height + _pagePadding[3]; - Width = _group.X + _group.Width + _pagePadding[2]; - - currentBarIndex = 0; - for (var i = 0; i < partials.Count; i++) - { - var partial = partials[i]; - canvas.BeginRender(partial.Width, Height); - canvas.Color = Renderer.RenderingResources.MainGlyphColor; - canvas.TextAlign = TextAlign.Left; - - var renderX = _group.GetBarX(partial.MasterBars[0].Index) + _group.AccoladeSpacing; - if (i == 0) - { - renderX -= _group.X + _group.AccoladeSpacing; - } - - Logger.Info(Name, - "Rendering partial from bar " + partial.MasterBars[0].Index + " to " + - partial.MasterBars[partial.MasterBars.Count - 1].Index); - - _group.PaintPartial(-renderX, _group.Y, Renderer.Canvas, currentBarIndex, partial.MasterBars.Count); - var result = canvas.EndRender(); - Renderer.OnPartialRenderFinished(new RenderFinishedEventArgs - { - TotalWidth = Width, - TotalHeight = Height, - Width = partial.Width, - Height = Height, - RenderResult = result, - FirstMasterBarIndex = partial.MasterBars[0].Index, - LastMasterBarIndex = partial.MasterBars[partial.MasterBars.Count - 1].Index - }); - currentBarIndex += partial.MasterBars.Count; - } - - } - } -} diff --git a/Source/AlphaTab/Rendering/Layout/PageViewLayout.cs b/Source/AlphaTab/Rendering/Layout/PageViewLayout.cs deleted file mode 100644 index 59c84ff1b..000000000 --- a/Source/AlphaTab/Rendering/Layout/PageViewLayout.cs +++ /dev/null @@ -1,427 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.Collections; -using AlphaTab.Platform; -using AlphaTab.Platform.Model; -using AlphaTab.Rendering.Staves; -using AlphaTab.Rendering.Utils; -using AlphaTab.Rendering.Glyphs; -using AlphaTab.Util; - -namespace AlphaTab.Rendering.Layout -{ - /// - /// This layout arranges the bars into a fixed width and dynamic height region. - /// - class PageViewLayout : ScoreLayout - { - // left top right bottom - public static readonly float[] PagePadding = { 40, 40, 40, 40 }; - public const float GroupSpacing = 20; - - private FastList _groups; - private FastList _allMasterBarRenderers; - private FastList _barsFromPreviousGroup; - private float[] _pagePadding; - - public override string Name { get { return "PageView"; } } - public PageViewLayout(ScoreRenderer renderer) - : base(renderer) - { - _barsFromPreviousGroup = new FastList(); - } - - protected override void DoLayoutAndRender() - { - _pagePadding = Renderer.Settings.Layout.Get("padding", PagePadding); - if (_pagePadding.Length == 1) - { - _pagePadding = new[] - { - _pagePadding[0], - _pagePadding[0], - _pagePadding[0], - _pagePadding[0] - }; - } - else if (_pagePadding.Length == 2) - { - _pagePadding = new[] - { - _pagePadding[0], - _pagePadding[1], - _pagePadding[0], - _pagePadding[1] - }; - } - - var x = _pagePadding[0]; - var y = _pagePadding[1]; - Width = Renderer.Settings.Width; - _allMasterBarRenderers = new FastList(); - - // - // 1. Score Info - y = LayoutAndRenderScoreInfo(x, y); - - // - // 2. One result per StaveGroup - y = LayoutAndRenderScore(x, y); - - Height = y + _pagePadding[3]; - } - - public override bool SupportsResize - { - get { return true; } - } - - public override void Resize() - { - var x = _pagePadding[0]; - var y = _pagePadding[1]; - Width = Renderer.Settings.Width; - var oldHeight = Height; - - // - // 1. Score Info - y = LayoutAndRenderScoreInfo(x, y, oldHeight); - - // - // 2. One result per StaveGroup - y = ResizeAndRenderScore(x, y, oldHeight); - - Height = y + _pagePadding[3]; - } - - private float LayoutAndRenderScoreInfo(float x, float y, float totalHeight = -1) - { - Logger.Debug(Name, "Layouting score info"); - - var scale = Scale; - var res = Renderer.RenderingResources; - - var centeredGlyphs = new[] - { - HeaderFooterElements.Title, HeaderFooterElements.SubTitle, HeaderFooterElements.Artist, - HeaderFooterElements.Album, HeaderFooterElements.WordsAndMusic - }; - - for (int i = 0; i < centeredGlyphs.Length; i++) - { - if (ScoreInfoGlyphs.ContainsKey(centeredGlyphs[i])) - { - var glyph = ScoreInfoGlyphs[centeredGlyphs[i]]; - glyph.X = Width / 2f; - glyph.Y = y; - glyph.TextAlign = TextAlign.Center; - y += glyph.Font.Size; - } - } - - bool musicOrWords = false; - float musicOrWordsHeight = 0; - if (ScoreInfoGlyphs.ContainsKey(HeaderFooterElements.Music)) - { - var glyph = ScoreInfoGlyphs[HeaderFooterElements.Music]; - glyph.X = Width - _pagePadding[2]; - glyph.Y = y; - glyph.TextAlign = TextAlign.Right; - musicOrWords = true; - musicOrWordsHeight = glyph.Font.Size; - } - if (ScoreInfoGlyphs.ContainsKey(HeaderFooterElements.Words)) - { - var glyph = ScoreInfoGlyphs[HeaderFooterElements.Words]; - glyph.X = x; - glyph.Y = y; - glyph.TextAlign = TextAlign.Left; - musicOrWords = true; - musicOrWordsHeight = glyph.Font.Size; - } - - if (musicOrWords) - { - y += musicOrWordsHeight; - } - - if (TuningGlyph != null) - { - y += 20 * scale; - TuningGlyph.X = x; - TuningGlyph.Y = y; - y += TuningGlyph.Height; - } - - y += 20 * scale; - - var canvas = Renderer.Canvas; - canvas.BeginRender(Width, y); - canvas.Color = res.ScoreInfoColor; - canvas.TextAlign = TextAlign.Center; - foreach (var key in ScoreInfoGlyphs) - { - ScoreInfoGlyphs[key].Paint(0, 0, canvas); - } - - if (TuningGlyph != null) - { - TuningGlyph.Paint(0, 0, canvas); - } - - var result = canvas.EndRender(); - Renderer.OnPartialRenderFinished(new RenderFinishedEventArgs - { - Width = Width, - Height = y, - RenderResult = result, - TotalWidth = Width, - TotalHeight = totalHeight < 0 ? y : totalHeight, - FirstMasterBarIndex = -1, - LastMasterBarIndex = -1 - }); - - return y; - } - - private float ResizeAndRenderScore(float x, float y, float oldHeight) - { - var canvas = Renderer.Canvas; - - // if we have a fixed number of bars per row, we only need to refit them. - if (Renderer.Settings.Layout.Get("barsPerRow", -1) != -1) - { - for (int i = 0; i < _groups.Count; i++) - { - var group = _groups[i]; - FitGroup(group); - group.FinalizeGroup(); - - y += PaintGroup(group, oldHeight, canvas); - } - } - // if the bars per row are flexible, we need to recreate the stave groups - // by readding the existing groups - else - { - _groups = new FastList(); - - var currentIndex = 0; - var maxWidth = MaxWidth; - - var group = CreateEmptyStaveGroup(); - group.Index = _groups.Count; - group.X = x; - group.Y = y; - - while (currentIndex < _allMasterBarRenderers.Count) - { - // if the current renderer still has space in the current group add it - // also force adding in case the group is empty - var renderers = _allMasterBarRenderers[currentIndex]; - if (group.Width + renderers.Width <= maxWidth || group.MasterBarsRenderers.Count == 0) - { - group.AddMasterBarRenderers(Renderer.Tracks, renderers); - // move to next group - currentIndex++; - } - else - { - // if we cannot wrap on the current bar, we remove the last bar - // (this might even remove multiple ones until we reach a bar that can wrap); - while (renderers != null && !renderers.CanWrap && group.MasterBarsRenderers.Count > 1) - { - renderers = group.RevertLastBar(); - currentIndex--; - } - - // in case we do not have space, we create a new group - group.IsFull = true; - group.IsLast = LastBarIndex == group.LastBarIndex; - _groups.Add(group); - FitGroup(group); - group.FinalizeGroup(); - y += PaintGroup(group, oldHeight, canvas); - - // note: we do not increase currentIndex here to have it added to the next group - group = CreateEmptyStaveGroup(); - group.Index = _groups.Count; - group.X = x; - group.Y = y; - } - } - group.IsLast = LastBarIndex == group.LastBarIndex; - - // don't forget to finish the last group - FitGroup(group); - group.FinalizeGroup(); - y += PaintGroup(group, oldHeight, canvas); - } - - return y; - } - - private float LayoutAndRenderScore(float x, float y) - { - var canvas = Renderer.Canvas; - - var startIndex = FirstBarIndex; - var currentBarIndex = startIndex; - - var endBarIndex = LastBarIndex; - - _groups = new FastList(); - while (currentBarIndex <= endBarIndex) - { - // create group and align set proper coordinates - var group = CreateStaveGroup(currentBarIndex, endBarIndex); - _groups.Add(group); - group.X = x; - group.Y = y; - - currentBarIndex = group.LastBarIndex + 1; - - // finalize group (sizing etc). - FitGroup(group); - group.FinalizeGroup(); - - Logger.Info(Name, "Rendering partial from bar " + group.FirstBarIndex + " to " + group.LastBarIndex); - y += PaintGroup(group, y, canvas); - } - - return y; - } - - private float PaintGroup(StaveGroup group, float totalHeight, ICanvas canvas) - { - // paint into canvas - var height = group.Height + (GroupSpacing * Scale); - canvas.BeginRender(Width, height); - Renderer.Canvas.Color = Renderer.RenderingResources.MainGlyphColor; - Renderer.Canvas.TextAlign = TextAlign.Left; - // NOTE: we use this negation trick to make the group paint itself to 0/0 coordinates - // since we use partial drawing - group.Paint(0, -group.Y, canvas); - - // calculate coordinates for next group - totalHeight += height; - - var result = canvas.EndRender(); - var args = new RenderFinishedEventArgs(); - args.TotalWidth = Width; - args.TotalHeight = totalHeight; - args.Width = Width; - args.Height = height; - args.RenderResult = result; - args.FirstMasterBarIndex = group.FirstBarIndex; - args.LastMasterBarIndex = group.LastBarIndex; - - Renderer.OnPartialRenderFinished(args); - - return height; - } - - /// - /// Realignes the bars in this line according to the available space - /// - private void FitGroup(StaveGroup group) - { - if (group.IsFull || group.Width > MaxWidth) - { - group.ScaleToWidth(MaxWidth); - } - - Width = Math.Max(Width, group.Width); - } - - private StaveGroup CreateStaveGroup(int currentBarIndex, int endIndex) - { - var group = CreateEmptyStaveGroup(); - group.Index = _groups.Count; - - var barsPerRow = Renderer.Settings.Layout.Get("barsPerRow", -1); - - var maxWidth = MaxWidth; - var end = endIndex + 1; - for (int i = currentBarIndex; i < end; i++) - { - if (_barsFromPreviousGroup.Count > 0) - { - foreach (var renderer in _barsFromPreviousGroup) - { - group.AddMasterBarRenderers(Renderer.Tracks, renderer); - i = renderer.MasterBar.Index; - } - } - else - { - var renderers = group.AddBars(Renderer.Tracks, i); - _allMasterBarRenderers.Add(renderers); - } - _barsFromPreviousGroup = new FastList(); - - var groupIsFull = false; - - // can bar placed in this line? - if (barsPerRow == -1 && ((group.Width) >= maxWidth && group.MasterBarsRenderers.Count != 0)) - { - groupIsFull = true; - } - else if (group.MasterBarsRenderers.Count == barsPerRow + 1) - { - groupIsFull = true; - } - - if (groupIsFull) - { - MasterBarsRenderers reverted = group.RevertLastBar(); - if (reverted != null) - { - _barsFromPreviousGroup.Add(reverted); - - while (reverted != null && !reverted.CanWrap && group.MasterBarsRenderers.Count > 1) - { - reverted = group.RevertLastBar(); - _barsFromPreviousGroup.Add(reverted); - } - } - - group.IsFull = true; - group.IsLast = false; - - _barsFromPreviousGroup.Reverse(); - return group; - } - - group.X = 0; - } - - group.IsLast = endIndex == group.LastBarIndex; - return group; - } - - private float MaxWidth - { - get - { - return Renderer.Settings.Width - _pagePadding[0] - _pagePadding[2]; - } - } - } -} diff --git a/Source/AlphaTab/Rendering/Layout/ScoreLayout.cs b/Source/AlphaTab/Rendering/Layout/ScoreLayout.cs deleted file mode 100644 index 5820ca9db..000000000 --- a/Source/AlphaTab/Rendering/Layout/ScoreLayout.cs +++ /dev/null @@ -1,296 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using AlphaTab.Collections; -using AlphaTab.Model; -using AlphaTab.Platform.Model; -using AlphaTab.Rendering.Glyphs; -using AlphaTab.Rendering.Staves; -using AlphaTab.Util; -using Staff = AlphaTab.Rendering.Staves.Staff; - -namespace AlphaTab.Rendering.Layout -{ - /// - /// This is the base public class for creating new layouting engines for the score renderer. - /// - abstract class ScoreLayout - { - private readonly FastDictionary> _barRendererLookup; - - public ScoreRenderer Renderer { get; set; } - - public abstract string Name { get; } - public float Width { get; set; } - public float Height { get; set; } - - protected FastDictionary ScoreInfoGlyphs; - protected TuningGlyph TuningGlyph; - - - protected ScoreLayout(ScoreRenderer renderer) - { - Renderer = renderer; - _barRendererLookup = new FastDictionary>(); - } - - - public abstract bool SupportsResize { get; } - public abstract void Resize(); - - public void LayoutAndRender() - { - var score = Renderer.Score; - var startIndex = Renderer.Settings.Layout.Get("start", 1); - startIndex--; // map to array index - startIndex = Math.Min(score.MasterBars.Count - 1, Math.Max(0, startIndex)); - FirstBarIndex = startIndex; - - var endBarIndex = Renderer.Settings.Layout.Get("count", score.MasterBars.Count); - if (endBarIndex < 0) endBarIndex = score.MasterBars.Count; - endBarIndex = startIndex + endBarIndex - 1; // map count to array index - endBarIndex = Math.Min(score.MasterBars.Count - 1, Math.Max(0, endBarIndex)); - LastBarIndex = endBarIndex; - - CreateScoreInfoGlyphs(); - DoLayoutAndRender(); - } - - protected abstract void DoLayoutAndRender(); - - private void CreateScoreInfoGlyphs() - { - Logger.Info("ScoreLayout", "Creating score info glyphs"); - - var flags = Renderer.Settings.Layout.Get("hideInfo", false) ? HeaderFooterElements.None : HeaderFooterElements.All; - var score = Renderer.Score; - var res = Renderer.RenderingResources; - - ScoreInfoGlyphs = new FastDictionary(); - if (!string.IsNullOrEmpty(score.Title) && (flags & HeaderFooterElements.Title) != 0) - { - ScoreInfoGlyphs[HeaderFooterElements.Title] = new TextGlyph(0, 0, score.Title, res.TitleFont, TextAlign.Center); - } - if (!string.IsNullOrEmpty(score.SubTitle) && (flags & HeaderFooterElements.SubTitle) != 0) - { - ScoreInfoGlyphs[HeaderFooterElements.SubTitle] = new TextGlyph(0, 0, score.SubTitle, res.SubTitleFont, TextAlign.Center); - } - if (!string.IsNullOrEmpty(score.Artist) && (flags & HeaderFooterElements.Artist) != 0) - { - ScoreInfoGlyphs[HeaderFooterElements.Artist] = new TextGlyph(0, 0, score.Artist, res.SubTitleFont, TextAlign.Center); - } - if (!string.IsNullOrEmpty(score.Album) && (flags & HeaderFooterElements.Album) != 0) - { - ScoreInfoGlyphs[HeaderFooterElements.Album] = new TextGlyph(0, 0, score.Album, res.SubTitleFont, TextAlign.Center); - } - if (!string.IsNullOrEmpty(score.Music) && score.Music == score.Words && (flags & HeaderFooterElements.WordsAndMusic) != 0) - { - ScoreInfoGlyphs[HeaderFooterElements.WordsAndMusic] = new TextGlyph(0, 0, "Music and Words by " + score.Words, res.WordsFont, TextAlign.Center); - } - else - { - if (!string.IsNullOrEmpty(score.Music) && (flags & HeaderFooterElements.Music) != 0) - { - ScoreInfoGlyphs[HeaderFooterElements.Music] = new TextGlyph(0, 0, "Music by " + score.Music, res.WordsFont, TextAlign.Right); - } - if (!string.IsNullOrEmpty(score.Words) && (flags & HeaderFooterElements.Words) != 0) - { - ScoreInfoGlyphs[HeaderFooterElements.Words] = new TextGlyph(0, 0, "Words by " + score.Words, res.WordsFont, TextAlign.Left); - } - } - - if (!Renderer.Settings.Layout.Get("hideTuning", false)) - { - Model.Staff staffWithTuning = null; - foreach (var track in Renderer.Tracks) - { - foreach (var staff in track.Staves) - { - if (staff.StaffKind != StaffKind.Percussion && staff.IsStringed && staff.Tuning.Length > 0) - { - staffWithTuning = staff; - break; - } - } - - if (staffWithTuning != null) - { - break; - } - } - - // tuning info - if (staffWithTuning != null) - { - var tuning = Tuning.FindTuning(staffWithTuning.Tuning); - if (tuning != null) - { - TuningGlyph = new TuningGlyph(0, 0, Scale, Renderer.RenderingResources, tuning); - } - } - } - } - - public float Scale - { - get - { - return Renderer.Settings.Scale; - } - } - - public int FirstBarIndex { get; private set; } - public int LastBarIndex { get; private set; } - - protected StaveGroup CreateEmptyStaveGroup() - { - var group = new StaveGroup(); - group.Layout = this; - - for (var trackIndex = 0; trackIndex < Renderer.Tracks.Length; trackIndex++) - { - var track = Renderer.Tracks[trackIndex]; - var hasScore = false; - //var hasTab = false; - foreach (var staff in track.Staves) - { - switch (staff.StaffKind) - { - case StaffKind.Tablature: - //hasTab = true; - break; - case StaffKind.Score: - hasScore = true; - break; - case StaffKind.Percussion: - break; - case StaffKind.Mixed: - hasScore = true; - //hasTab = true; - break; - } - } - - - for (int staffIndex = 0; staffIndex < track.Staves.Count; staffIndex++) - { - var staff = track.Staves[staffIndex]; - - // use optimal profile for track - string staveProfile; - if (staff.StaffKind == StaffKind.Percussion) - { - staveProfile = Environment.StaveProfileScore; - } - else if (staff.StaffKind == StaffKind.Tablature) - { - if (hasScore) - { - staveProfile = Environment.StaveProfileTabMixed; - } - else - { - staveProfile = Environment.StaveProfileTab; - } - } - else if (staff.IsStringed) - { - staveProfile = Renderer.Settings.Staves.Id; - } - else // if(staff.StaffKind == StaffKind.Score) - { - staveProfile = Environment.StaveProfileScore; - } - - var profile = Environment.StaveProfiles.ContainsKey(staveProfile) - ? Environment.StaveProfiles[staveProfile] - : Environment.StaveProfiles["default"]; - - foreach (var factory in profile) - { - if (factory.CanCreate(track, staff)) - { - group.AddStaff(track, new Staff(trackIndex, staff, factory)); - } - } - } - } - return group; - } - - public void RegisterBarRenderer(string key, BarRendererBase renderer) - { - if (!_barRendererLookup.ContainsKey(key)) - { - _barRendererLookup[key] = new FastDictionary(); - } - _barRendererLookup[key][renderer.Bar.Id] = renderer; - } - - public void UnregisterBarRenderer(string key, BarRendererBase renderer) - { - if (_barRendererLookup.ContainsKey(key)) - { - var lookup = _barRendererLookup[key]; - lookup.Remove(renderer.Bar.Id); - } - } - - public T GetRendererForBar(string key, Bar bar) - where T: BarRendererBase - { - var barRendererId = bar.Id; - if (_barRendererLookup.ContainsKey(key) && _barRendererLookup[key].ContainsKey(barRendererId)) - { - return (T)_barRendererLookup[key][barRendererId]; - } - return null; - } - - public void RenderAnnotation() - { - // attention, you are not allowed to remove change this notice within any version of this library without permission! - var msg = "Rendered using alphaTab (http://www.alphaTab.net)"; - - var canvas = Renderer.Canvas; - var resources = Renderer.RenderingResources; - - var height = (resources.CopyrightFont.Size * 2); - Height += height; - var x = Width / 2; - - canvas.BeginRender(Width, height); - canvas.Color = resources.MainGlyphColor; - canvas.Font = resources.CopyrightFont; - canvas.TextAlign = TextAlign.Center; - canvas.FillText(msg, x, resources.CopyrightFont.Size); - var result = canvas.EndRender(); - Renderer.OnPartialRenderFinished(new RenderFinishedEventArgs - { - Width = Width, - Height = height, - RenderResult = result, - TotalWidth = Width, - TotalHeight = Height, - FirstMasterBarIndex = -1, - LastMasterBarIndex = -1 - }); - } - } -} diff --git a/Source/AlphaTab/Rendering/RenderingResources.cs b/Source/AlphaTab/Rendering/RenderingResources.cs deleted file mode 100644 index 5387326a4..000000000 --- a/Source/AlphaTab/Rendering/RenderingResources.cs +++ /dev/null @@ -1,91 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Platform.Model; - -namespace AlphaTab.Rendering -{ - /// - /// This public class contains central definitions for controlling the visual appearance. - /// - class RenderingResources - { - public Font CopyrightFont { get; set; } - public Font TitleFont { get; set; } - public Font SubTitleFont { get; set; } - public Font WordsFont { get; set; } - public Font EffectFont { get; set; } - - public Font TablatureFont { get; set; } - public Font GraceFont { get; set; } - - public Color StaveLineColor { get; set; } - public Color BarSeperatorColor { get; set; } - - public Font BarNumberFont { get; set; } - public Color BarNumberColor { get; set; } - - public Font FingeringFont { get; set; } - public Font MarkerFont { get; set; } - public Font TabClefFont { get; set; } - - public Color MainGlyphColor { get; set; } - public Color SecondaryGlyphColor { get; set; } - - public float Scale { get; set; } - - public Color ScoreInfoColor { get; set; } - - public RenderingResources(float scale) - { - Init(scale); - } - - public void Init(float scale) - { - Scale = scale; - - const string sansFont = "Arial"; - const string serifFont = "Georgia"; - - EffectFont = new Font(serifFont, 12 * scale, FontStyle.Italic); - CopyrightFont = new Font(sansFont, 12 * scale, FontStyle.Bold); - - TitleFont = new Font(serifFont, 32 * scale); - SubTitleFont = new Font(serifFont, 20 * scale); - WordsFont = new Font(serifFont, 15 * scale); - - TablatureFont = new Font(sansFont, 13 * scale); - GraceFont = new Font(sansFont, 11 * scale); - - StaveLineColor = new Color(165, 165, 165); - BarSeperatorColor = new Color(34, 34, 17); - - BarNumberFont = new Font(sansFont, 11 * scale); - BarNumberColor = new Color(200, 0, 0); - - FingeringFont = new Font(serifFont, 14 * scale); - MarkerFont = new Font(serifFont, 14 * scale, FontStyle.Bold); - TabClefFont = new Font(sansFont, 18 * scale, FontStyle.Bold); - - ScoreInfoColor = new Color(0, 0, 0); - MainGlyphColor = new Color(0, 0, 0); - SecondaryGlyphColor = new Color(0, 0, 0, 100); - } - } -} diff --git a/Source/AlphaTab/Rendering/ScoreBarRenderer.cs b/Source/AlphaTab/Rendering/ScoreBarRenderer.cs deleted file mode 100644 index 8e912f505..000000000 --- a/Source/AlphaTab/Rendering/ScoreBarRenderer.cs +++ /dev/null @@ -1,860 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.Audio; -using AlphaTab.Collections; -using AlphaTab.Model; -using AlphaTab.Platform; -using AlphaTab.Platform.Model; -using AlphaTab.Rendering.Glyphs; -using AlphaTab.Rendering.Utils; - -namespace AlphaTab.Rendering -{ - /// - /// This BarRenderer renders a bar using standard music notation. - /// - class ScoreBarRenderer : BarRendererBase, IBeamYCalculator - { - public const string StaffId = "score"; - - /// - /// The step offsets of sharp symbols for sharp key signatures. - /// - private static readonly int[] SharpKsSteps = { 1, 4, 0, 3, 6, 2, 5 }; - - /// - /// The step offsets of sharp symbols for flat key signatures. - /// - private static readonly int[] FlatKsSteps = { 5, 2, 6, 3, 7, 4, 8 }; - - private const float LineSpacing = 8; - private const float StemWidth = 1.3f; - - public float SimpleWhammyOverflow { get; set; } - - public AccidentalHelper AccidentalHelper { get; set; } - - public ScoreBarRenderer(ScoreRenderer renderer, Bar bar) - : base(renderer, bar) - { - AccidentalHelper = new AccidentalHelper(bar); - } - - public BeamDirection GetBeatDirection(Beat beat) - { - ScoreBeatGlyph g = (ScoreBeatGlyph)GetOnNotesGlyphForBeat(beat); - if (g != null) - { - return g.NoteHeads.Direction; - } - return BeamDirection.Up; - } - - public override float GetNoteX(Note note, bool onEnd = true) - { - ScoreBeatGlyph g = (ScoreBeatGlyph)GetOnNotesGlyphForBeat(note.Beat); - if (g != null) - { - var x = g.Container.VoiceContainer.X + g.Container.X + g.X; - if (onEnd) - { - x += g.Width; - } - return x; - } - return 0; - } - - public override float GetNoteY(Note note, bool aboveNote = false) - { - ScoreBeatGlyph beat = (ScoreBeatGlyph)GetOnNotesGlyphForBeat(note.Beat); - if (beat != null) - { - return beat.NoteHeads.GetNoteY(note, aboveNote); - } - return 0; - } - - public float LineOffset - { - get - { - return ((LineSpacing + 1) * Scale); - } - } - - protected override void UpdateSizes() - { - var res = Resources; - var glyphOverflow = (res.TablatureFont.Size / 2) + (res.TablatureFont.Size * 0.2f); - TopPadding = glyphOverflow; - BottomPadding = glyphOverflow; - Height = (LineOffset * 4) + TopPadding + BottomPadding; - base.UpdateSizes(); - } - - public override void DoLayout() - { - base.DoLayout(); - - if (!Bar.IsEmpty && AccidentalHelper.MaxNoteValueBeat != null) - { - var top = GetScoreY(0); - var bottom = GetScoreY(8); - - var whammyOffset = SimpleWhammyOverflow; - RegisterOverflowTop(whammyOffset); - - var maxNoteY = GetYPositionForNoteValue(AccidentalHelper.MaxNoteValue); - var maxNoteHelper = Helpers.GetBeamingHelperForBeat(AccidentalHelper.MaxNoteValueBeat); - if (maxNoteHelper.Direction == BeamDirection.Up) - { - maxNoteY -= GetStemSize(maxNoteHelper); - maxNoteY -= maxNoteHelper.FingeringCount * Resources.GraceFont.Size; - if (maxNoteHelper.HasTuplet) - { - maxNoteY -= Resources.EffectFont.Size * 2; - } - } - - if (maxNoteHelper.HasTuplet) - { - maxNoteY -= Resources.EffectFont.Size * 1.5f; - } - - if (maxNoteY < top) - { - RegisterOverflowTop(Math.Abs(maxNoteY) + whammyOffset); - } - - var minNoteY = GetYPositionForNoteValue(AccidentalHelper.MinNoteValue); - var minNoteHelper = Helpers.GetBeamingHelperForBeat(AccidentalHelper.MinNoteValueBeat); - if (minNoteHelper.Direction == BeamDirection.Down) - { - minNoteY += GetStemSize(minNoteHelper); - minNoteY += minNoteHelper.FingeringCount * Resources.GraceFont.Size; - } - - if (minNoteY > bottom) - { - RegisterOverflowBottom(Math.Abs(minNoteY) - bottom); - } - } - } - - - public override void Paint(float cx, float cy, ICanvas canvas) - { - base.Paint(cx, cy, canvas); - PaintBeams(cx, cy, canvas); - PaintTuplets(cx, cy, canvas); - } - - private void PaintTuplets(float cx, float cy, ICanvas canvas) - { - for (int i = 0, j = Helpers.TupletHelpers.Count; i < j; i++) - { - var v = Helpers.TupletHelpers[i]; - for (int k = 0, l = v.Count; k < l; k++) - { - var h = v[k]; - PaintTupletHelper(cx + BeatGlyphsStart, cy, canvas, h); - } - } - } - - private void PaintBeams(float cx, float cy, ICanvas canvas) - { - for (int i = 0, j = Helpers.BeamHelpers.Count; i < j; i++) - { - var v = Helpers.BeamHelpers[i]; - for (int k = 0, l = v.Count; k < l; k++) - { - var h = v[k]; - PaintBeamHelper(cx + BeatGlyphsStart, cy, canvas, h); - } - } - } - - private void PaintBeamHelper(float cx, float cy, ICanvas canvas, BeamingHelper h) - { - canvas.Color = h.Voice.Index == 0 - ? Resources.MainGlyphColor - : Resources.SecondaryGlyphColor; - - // TODO: draw stem at least at the center of the score staff. - - // check if we need to paint simple footer - if (h.Beats.Count == 1) - { - PaintFooter(cx, cy, canvas, h); - } - else - { - PaintBar(cx, cy, canvas, h); - } - } - - private void PaintTupletHelper(float cx, float cy, ICanvas canvas, TupletHelper h) - { - var res = Resources; - var oldAlign = canvas.TextAlign; - canvas.TextAlign = TextAlign.Center; - // check if we need to paint simple footer - if (h.Beats.Count == 1 || !h.IsFull) - { - for (int i = 0, j = h.Beats.Count; i < j; i++) - { - var beat = h.Beats[i]; - var beamingHelper = Helpers.BeamHelperLookup[h.VoiceIndex][beat.Index]; - if (beamingHelper == null) continue; - var direction = beamingHelper.Direction; - - var tupletX = beamingHelper.GetBeatLineX(beat) + Scale; - var tupletY = cy + Y + CalculateBeamY(beamingHelper, tupletX); - - var offset = direction == BeamDirection.Up - ? res.EffectFont.Size * 1.8f - : -3 * Scale; - - canvas.Font = res.EffectFont; - canvas.FillText(h.Tuplet.ToString(), cx + X + tupletX, tupletY - offset); - } - } - else - { - var firstBeat = h.Beats[0]; - var lastBeat = h.Beats[h.Beats.Count - 1]; - - var firstBeamingHelper = Helpers.BeamHelperLookup[h.VoiceIndex][firstBeat.Index]; - var lastBeamingHelper = Helpers.BeamHelperLookup[h.VoiceIndex][lastBeat.Index]; - if (firstBeamingHelper != null && lastBeamingHelper != null) - { - var direction = firstBeamingHelper.Direction; - - // - // Calculate the overall area of the tuplet bracket - - var startX = firstBeamingHelper.GetBeatLineX(firstBeat) + Scale; - var endX = lastBeamingHelper.GetBeatLineX(lastBeat) + Scale; - - // - // Calculate how many space the text will need - canvas.Font = res.EffectFont; - var s = h.Tuplet.ToString(); - var sw = canvas.MeasureText(s); - var sp = 3 * Scale; - - // - // Calculate the offsets where to break the bracket - var middleX = (startX + endX) / 2; - var offset1X = middleX - sw / 2 - sp; - var offset2X = middleX + sw / 2 + sp; - - // - // calculate the y positions for our bracket - - var startY = CalculateBeamYWithDirection(firstBeamingHelper, startX, firstBeamingHelper.Direction); - var endY = CalculateBeamYWithDirection(lastBeamingHelper, endX, firstBeamingHelper.Direction); - - var k = (endY - startY) / (endX - startX); - var d = startY - (k * startX); - - var offset1Y = (k * offset1X) + d; - var middleY = (k * middleX) + d; - var offset2Y = (k * offset2X) + d; - - var offset = 10 * Scale; - var size = 5 * Scale; - if (direction == BeamDirection.Down) - { - offset *= -1; - size *= -1; - } - - // - // draw the bracket - canvas.BeginPath(); - canvas.MoveTo(cx + X + startX, (int)(cy + Y + startY - offset)); - canvas.LineTo(cx + X + startX, (int)(cy + Y + startY - offset - size)); - canvas.LineTo(cx + X + offset1X, (int)(cy + Y + offset1Y - offset - size)); - canvas.Stroke(); - - canvas.BeginPath(); - canvas.MoveTo(cx + X + offset2X, (int)(cy + Y + offset2Y - offset - size)); - canvas.LineTo(cx + X + endX, (int)(cy + Y + endY - offset - size)); - canvas.LineTo(cx + X + endX, (int)(cy + Y + endY - offset)); - canvas.Stroke(); - - // - // Draw the string - canvas.FillText(s, cx + X + middleX, cy + Y + middleY - offset - size - res.EffectFont.Size); - } - } - canvas.TextAlign = oldAlign; - } - - private float GetStemSize(BeamingHelper helper) - { - var size = helper.Beats.Count == 1 - ? GetFooterStemSize(helper.ShortestDuration) - : GetBarStemSize(helper.ShortestDuration); - if (helper.IsGrace) - { - size = size * NoteHeadGlyph.GraceScale; - } - - return size; - } - - private float GetBarStemSize(Duration duration) - { - int size; - switch (duration) - { - case Duration.QuadrupleWhole: size = 6; break; - case Duration.Half: size = 6; break; - case Duration.Quarter: size = 6; break; - case Duration.Eighth: size = 6; break; - case Duration.Sixteenth: size = 6; break; - case Duration.ThirtySecond: size = 7; break; - case Duration.SixtyFourth: size = 7; break; - case Duration.OneHundredTwentyEighth: size = 9; break; - case Duration.TwoHundredFiftySixth: size = 10; break; - default: size = 0; break; - } - - return GetScoreY(size); - } - - private float GetFooterStemSize(Duration duration) - { - int size; - switch (duration) - { - case Duration.QuadrupleWhole: size = 6; break; - case Duration.Half: size = 6; break; - case Duration.Quarter: size = 6; break; - case Duration.Eighth: size = 6; break; - case Duration.Sixteenth: size = 6; break; - case Duration.ThirtySecond: size = 6; break; - case Duration.SixtyFourth: size = 6; break; - case Duration.OneHundredTwentyEighth: size = 6; break; - case Duration.TwoHundredFiftySixth: size = 6; break; - default: size = 0; break; - } - - return GetScoreY(size); - } - - public float GetYPositionForNoteValue(int noteValue) - { - return GetScoreY(AccidentalHelper.GetNoteLineForValue(noteValue, true)); - } - - public float CalculateBeamY(BeamingHelper h, float x) - { - var stemSize = GetStemSize(h); - return h.CalculateBeamY(stemSize, Scale, x, Scale, this); - } - - private float CalculateBeamYWithDirection(BeamingHelper h, float x, BeamDirection direction) - { - var stemSize = GetStemSize(h); - return h.CalculateBeamYWithDirection(stemSize, Scale, x, Scale, this, direction); - } - - private void PaintBar(float cx, float cy, ICanvas canvas, BeamingHelper h) - { - for (int i = 0, j = h.Beats.Count; i < j; i++) - { - var beat = h.Beats[i]; - var isGrace = beat.GraceType != GraceType.None; - var scaleMod = isGrace ? NoteHeadGlyph.GraceScale : 1; - - // - // draw line - // - var beatLineX = h.GetBeatLineX(beat) + Scale; - - var direction = h.Direction; - - var y1 = cy + Y; - y1 += direction == BeamDirection.Up - ? GetYPositionForNoteValue(h.GetBeatMinValue(beat)) - : GetYPositionForNoteValue(h.GetBeatMaxValue(beat)); - - var y2 = cy + Y; - y2 += scaleMod * CalculateBeamY(h, beatLineX); - - canvas.LineWidth = StemWidth * Scale; - canvas.BeginPath(); - canvas.MoveTo(cx + X + beatLineX, y1); - canvas.LineTo(cx + X + beatLineX, y2); - canvas.Stroke(); - canvas.LineWidth = Scale; - - float fingeringY = y2; - if (direction == BeamDirection.Down) - { - fingeringY += canvas.Font.Size * 2f; - } - else if (i != 0) - { - fingeringY -= canvas.Font.Size * 1.5f; - } - PaintFingering(canvas, beat, cx + X + beatLineX, direction, fingeringY); - - var brokenBarOffset = 6 * Scale * scaleMod; - var barSpacing = 7 * Scale * scaleMod; - var barSize = LineSpacing / 2 * Scale * scaleMod; - var barCount = beat.Duration.GetIndex() - 2; - var barStart = cy + Y; - if (direction == BeamDirection.Down) - { - barSpacing = -barSpacing; - barSize = -barSize; - } - - for (var barIndex = 0; barIndex < barCount; barIndex++) - { - float barStartX; - float barEndX; - - float barStartY; - float barEndY; - - var barY = barStart + (barIndex * barSpacing); - - // - // Bar to Next? - // - if (i < h.Beats.Count - 1) - { - // full bar? - if (BeamingHelper.IsFullBarJoin(beat, h.Beats[i + 1], barIndex)) - { - barStartX = beatLineX; - barEndX = h.GetBeatLineX(h.Beats[i + 1]) + Scale; - } - // broken bar? - else if (i == 0 || !BeamingHelper.IsFullBarJoin(h.Beats[i - 1], beat, barIndex)) - { - barStartX = beatLineX; - barEndX = barStartX + brokenBarOffset; - } - else - { - continue; - } - barStartY = barY + CalculateBeamY(h, barStartX) * scaleMod; - barEndY = barY + CalculateBeamY(h, barEndX) * scaleMod; - PaintSingleBar(canvas, cx + X + barStartX, barStartY, cx + X + barEndX, barEndY, barSize); - } - // - // Broken Bar to Previous? - // - else if (i > 0 && !BeamingHelper.IsFullBarJoin(beat, h.Beats[i - 1], barIndex)) - { - barStartX = beatLineX - brokenBarOffset; - barEndX = beatLineX; - - barStartY = barY + CalculateBeamY(h, barStartX) * scaleMod; - barEndY = barY + CalculateBeamY(h, barEndX) * scaleMod; - - PaintSingleBar(canvas, cx + X + barStartX, barStartY, cx + X + barEndX, barEndY, barSize); - } - } - } - } - - private static void PaintSingleBar(ICanvas canvas, float x1, float y1, float x2, float y2, float size) - { - canvas.BeginPath(); - canvas.MoveTo(x1, y1); - canvas.LineTo(x2, y2); - canvas.LineTo(x2, y2 + size); - canvas.LineTo(x1, y1 + size); - canvas.ClosePath(); - canvas.Fill(); - } - - private void PaintFooter(float cx, float cy, ICanvas canvas, BeamingHelper h) - { - var beat = h.Beats[0]; - if (beat.GraceType == GraceType.BendGrace || (beat.GraceType != GraceType.None && Settings.DisplayMode == DisplayMode.SongBook)) - { - return; - } - - var isGrace = beat.GraceType != GraceType.None; - var scaleMod = isGrace ? NoteHeadGlyph.GraceScale : 1; - - // - // draw line - // - - var stemSize = GetFooterStemSize(h.ShortestDuration); - - var beatLineX = h.GetBeatLineX(beat) + Scale; - - var direction = h.Direction; - - var topY = GetYPositionForNoteValue(h.MaxNoteValue); - var bottomY = GetYPositionForNoteValue(h.MinNoteValue); - float beamY; - float fingeringY; - if (direction == BeamDirection.Down) - { - bottomY += stemSize * scaleMod; - beamY = bottomY; - fingeringY = cy + Y + bottomY; - } - else - { - topY -= stemSize * scaleMod; - beamY = topY; - fingeringY = cy + Y + topY; - } - - PaintFingering(canvas, beat, cx + X + beatLineX, direction, fingeringY); - - if (beat.Duration == Duration.Whole || beat.Duration == Duration.DoubleWhole || beat.Duration == Duration.QuadrupleWhole) - { - return; - } - - canvas.LineWidth = StemWidth * Scale; - canvas.BeginPath(); - canvas.MoveTo(cx + X + beatLineX, cy + Y + topY); - canvas.LineTo(cx + X + beatLineX, cy + Y + bottomY); - canvas.Stroke(); - canvas.LineWidth = Scale; - - if (beat.GraceType == GraceType.BeforeBeat) - { - var graceSizeY = 15 * Scale; - var graceSizeX = 12 * Scale; - - - canvas.BeginPath(); - if (direction == BeamDirection.Down) - { - canvas.MoveTo(cx + X + beatLineX - (graceSizeX / 2), cy + Y + bottomY - graceSizeY); - canvas.LineTo(cx + X + beatLineX + (graceSizeX / 2), cy + Y + bottomY); - } - else - { - canvas.MoveTo(cx + X + beatLineX - (graceSizeX / 2), cy + Y + topY + graceSizeY); - canvas.LineTo(cx + X + beatLineX + (graceSizeX / 2), cy + Y + topY); - } - canvas.Stroke(); - } - - // - // Draw beam - // - if (beat.Duration > Duration.Quarter || isGrace) - { - var glyph = new BeamGlyph(beatLineX - Scale / 2f, beamY, beat.Duration, direction, isGrace); - glyph.Renderer = this; - glyph.DoLayout(); - glyph.Paint(cx + X, cy + Y, canvas); - } - } - - private void PaintFingering(ICanvas canvas, Beat beat, float beatLineX, BeamDirection direction, float topY) - { - var settings = Settings; - if (settings.FingeringMode != FingeringMode.Score) return; - - if (direction == BeamDirection.Up) - { - beatLineX -= 10 * Scale; - } - else - { - beatLineX += 3 * Scale; - } - - // sort notes ascending in their value to ensure - // we are drawing the numbers according to their order on the stave - var noteList = beat.Notes.Clone(); - noteList.Sort((a, b) => a.RealValue - b.RealValue); - - for (int n = 0; n < noteList.Count; n++) - { - var note = noteList[n]; - string text = null; - if (note.LeftHandFinger != Fingers.Unknown) - { - text = ModelUtils.FingerToString(settings, beat, note.LeftHandFinger, true); - } - else if (note.RightHandFinger != Fingers.Unknown) - { - text = ModelUtils.FingerToString(settings, beat, note.RightHandFinger, false); - } - - if (text == null) - { - continue; - } - - canvas.FillText(text, beatLineX, topY); - topY -= (int)(canvas.Font.Size); - } - } - - - protected override void CreatePreBeatGlyphs() - { - base.CreatePreBeatGlyphs(); - - if (Bar.MasterBar.IsRepeatStart) - { - AddPreBeatGlyph(new RepeatOpenGlyph(0, 0, 1.5f, 3)); - } - - // Clef - if (IsFirstOfLine || Bar.Clef != Bar.PreviousBar.Clef || Bar.ClefOttava != Bar.PreviousBar.ClefOttava) - { - var offset = 0; - var correction = 0; - switch (Bar.Clef) - { - case Clef.Neutral: - offset = 6; - break; - case Clef.F4: - offset = 4; - correction = -1; - break; - case Clef.C3: - offset = 6; - break; - case Clef.C4: - offset = 4; - break; - case Clef.G2: - offset = 8; - break; - } - CreateStartSpacing(); - AddPreBeatGlyph(new ClefGlyph(0, GetScoreY(offset, correction), Bar.Clef, Bar.ClefOttava)); - } - - // Key signature - if ((Index == 0 && Bar.MasterBar.KeySignature != 0) || (Bar.PreviousBar != null && Bar.MasterBar.KeySignature != Bar.PreviousBar.MasterBar.KeySignature)) - { - CreateStartSpacing(); - CreateKeySignatureGlyphs(); - } - - // Time Signature - if ((Bar.PreviousBar == null) || (Bar.PreviousBar != null && Bar.MasterBar.TimeSignatureNumerator != Bar.PreviousBar.MasterBar.TimeSignatureNumerator) || (Bar.PreviousBar != null && Bar.MasterBar.TimeSignatureDenominator != Bar.PreviousBar.MasterBar.TimeSignatureDenominator)) - { - CreateStartSpacing(); - CreateTimeSignatureGlyphs(); - } - - AddPreBeatGlyph(new BarNumberGlyph(0, GetScoreY(-0.5f), Bar.Index + 1)); - - if (Bar.IsEmpty) - { - AddPreBeatGlyph(new SpacingGlyph(0, 0, (30 * Scale))); - } - } - - protected override void CreateBeatGlyphs() - { - for (int v = 0; v < Bar.Voices.Count; v++) - { - var voice = Bar.Voices[v]; - if (HasVoiceContainer(voice)) - { - CreateVoiceGlyphs(voice); - } - } - } - - protected override void CreatePostBeatGlyphs() - { - base.CreatePostBeatGlyphs(); - if (Bar.MasterBar.IsRepeatEnd) - { - AddPostBeatGlyph(new RepeatCloseGlyph(X, 0)); - if (Bar.MasterBar.RepeatCount > 2) - { - AddPostBeatGlyph(new RepeatCountGlyph(0, GetScoreY(-1, -3), Bar.MasterBar.RepeatCount)); - } - } - else - { - AddPostBeatGlyph(new BarSeperatorGlyph(0, 0)); - } - } - - private bool _startSpacing; - - private void CreateStartSpacing() - { - if (_startSpacing) return; - AddPreBeatGlyph(new SpacingGlyph(0, 0, 2 * Scale)); - _startSpacing = true; - } - - private void CreateKeySignatureGlyphs() - { - int offsetClef = 0; - int currentKey = (int)Bar.MasterBar.KeySignature; - int previousKey = Bar.PreviousBar == null ? 0 : (int)Bar.PreviousBar.MasterBar.KeySignature; - - switch (Bar.Clef) - { - case Clef.Neutral: - offsetClef = 0; - break; - case Clef.G2: - offsetClef = 1; - break; - case Clef.F4: - offsetClef = 2; - break; - case Clef.C3: - offsetClef = -1; - break; - case Clef.C4: - offsetClef = 1; - break; - } - - var newLines = new FastDictionary(); - var newGlyphs = new FastList(); - - // how many symbols do we need to get from a C-keysignature - // to the new one - //var offsetSymbols = (currentKey <= 7) ? currentKey : currentKey - 7; - // a sharp keysignature - if (ModelUtils.KeySignatureIsSharp(currentKey)) - { - for (var i = 0; i < Math.Abs(currentKey); i++) - { - var step = SharpKsSteps[i] + offsetClef; - newGlyphs.Add(new AccidentalGlyph(0, GetScoreY(step), AccidentalType.Sharp)); - newLines[step] = true; - } - } - // a flat signature - else - { - for (var i = 0; i < Math.Abs(currentKey); i++) - { - var step = FlatKsSteps[i] + offsetClef; - newGlyphs.Add(new AccidentalGlyph(0, GetScoreY(step), AccidentalType.Flat)); - newLines[step] = true; - } - } - - // naturalize previous key - var naturalizeSymbols = Math.Abs(previousKey); - var previousKeyPositions = ModelUtils.KeySignatureIsSharp(previousKey) ? SharpKsSteps : FlatKsSteps; - - for (var i = 0; i < naturalizeSymbols; i++) - { - var step = previousKeyPositions[i] + offsetClef; - if (!newLines.ContainsKey(step)) - { - AddPreBeatGlyph(new AccidentalGlyph(0, GetScoreY(previousKeyPositions[i] + offsetClef), AccidentalType.Natural)); - } - } - - foreach (var newGlyph in newGlyphs) - { - AddPreBeatGlyph(newGlyph); - } - - } - - private void CreateTimeSignatureGlyphs() - { - AddPreBeatGlyph(new SpacingGlyph(0, 0, 5 * Scale)); - AddPreBeatGlyph(new ScoreTimeSignatureGlyph(0, GetScoreY(2), Bar.MasterBar.TimeSignatureNumerator, Bar.MasterBar.TimeSignatureDenominator, Bar.MasterBar.TimeSignatureCommon)); - } - - private void CreateVoiceGlyphs(Voice v) - { - for (int i = 0, j = v.Beats.Count; i < j; i++) - { - var b = v.Beats[i]; - var container = new ScoreBeatContainerGlyph(b, GetOrCreateVoiceContainer(v)); - container.PreNotes = new ScoreBeatPreNotesGlyph(); - container.OnNotes = new ScoreBeatGlyph(); - AddBeatGlyph(container); - } - } - - // TODO[performance]: Maybe we should cache this (check profiler) - public int GetNoteLine(Note n) - { - return AccidentalHelper.GetNoteLine(n); - } - - /// - /// Gets the relative y position of the given steps relative to first line. - /// - /// the amount of steps while 2 steps are one line - /// - /// - public float GetScoreY(float steps, float correction = 0) - { - return ((LineOffset / 2) * steps) + (correction * Scale); - } - - //private static readonly Random Random = new Random(); - protected override void PaintBackground(float cx, float cy, ICanvas canvas) - { - base.PaintBackground(cx, cy, canvas); - - var res = Resources; - - //var c = new Color((byte)Platform.Random(255), - // (byte)Platform.Random(255), - // (byte)Platform.Random(255), - // 100); - //canvas.Color = c; - //canvas.FillRect(cx + X, cy + Y, Width, Height); - - // - // draw string lines - // - canvas.Color = res.StaveLineColor; - var lineY = cy + Y + TopPadding; - var lineOffset = LineOffset; - for (var i = 0; i < 5; i++) - { - if (i > 0) lineY += lineOffset; - canvas.FillRect(cx + X, (int)lineY, Width, Scale); - } - - canvas.Color = res.MainGlyphColor; - - PaintSimileMark(cx, cy, canvas); - } - } -} diff --git a/Source/AlphaTab/Rendering/ScoreBarRendererFactory.cs b/Source/AlphaTab/Rendering/ScoreBarRendererFactory.cs deleted file mode 100644 index ef03730b7..000000000 --- a/Source/AlphaTab/Rendering/ScoreBarRendererFactory.cs +++ /dev/null @@ -1,36 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Collections; -using AlphaTab.Model; - -namespace AlphaTab.Rendering -{ - /// - /// This Factory procudes ScoreBarRenderer instances - /// - class ScoreBarRendererFactory : BarRendererFactory - { - public override string StaffId { get { return ScoreBarRenderer.StaffId; } } - - public override BarRendererBase Create(ScoreRenderer renderer, Bar bar, StaveSettings staveSettings) - { - return new ScoreBarRenderer(renderer, bar); - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/ScoreRenderer.cs b/Source/AlphaTab/Rendering/ScoreRenderer.cs deleted file mode 100644 index b2fe4951c..000000000 --- a/Source/AlphaTab/Rendering/ScoreRenderer.cs +++ /dev/null @@ -1,270 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.Collections; -using AlphaTab.Model; -using AlphaTab.Platform; -using AlphaTab.Rendering.Layout; -using AlphaTab.Rendering.Utils; -using AlphaTab.Util; - -namespace AlphaTab.Rendering -{ - /// - /// This is the main wrapper of the rendering engine which - /// can render a single track of a score object into a notation sheet. - /// - public class ScoreRenderer : IScoreRenderer - { - private string _currentLayoutMode; - private string _currentRenderEngine; - private Track[] _renderedTracks; - - internal ICanvas Canvas { get; set; } - - internal Score Score { get; private set; } - internal Track[] Tracks { get; private set; } - internal ScoreLayout Layout { get; set; } - - internal RenderingResources RenderingResources { get; set; } - internal Settings Settings { get; set; } - - public BoundsLookup BoundsLookup { get; set; } - - public ScoreRenderer(Settings settings) - { - Settings = settings; - RenderingResources = new RenderingResources(1); - RecreateCanvas(); - RecreateLayout(); - } - - public void Destroy() - { - Score = null; - Canvas = null; - Layout = null; - RenderingResources = null; - Settings = null; - BoundsLookup = null; - Tracks = null; - } - - private bool RecreateCanvas() - { - if (_currentRenderEngine != Settings.Engine) - { - if (Settings.Engine == null || !Environment.RenderEngines.ContainsKey(Settings.Engine)) - { - Canvas = Environment.RenderEngines["default"](); - } - else - { - Canvas = Environment.RenderEngines[Settings.Engine](); - } - _currentRenderEngine = Settings.Engine; - return true; - } - return false; - } - - private bool RecreateLayout() - { - if (_currentLayoutMode != Settings.Layout.Mode) - { - if (Settings.Layout == null || !Environment.LayoutEngines.ContainsKey(Settings.Layout.Mode)) - { - Layout = Environment.LayoutEngines["default"](this); - } - else - { - Layout = Environment.LayoutEngines[Settings.Layout.Mode](this); - } - _currentLayoutMode = Settings.Layout.Mode; - return true; - } - return false; - } - - public void Render(Score score, int[] trackIndexes) - { - try - { - Score = score; - var tracks = new FastList(); - foreach (var track in trackIndexes) - { - if (track >= 0 && track < score.Tracks.Count) - { - tracks.Add(score.Tracks[track]); - } - } - - if (tracks.Count == 0 && score.Tracks.Count > 0) - { - tracks.Add(score.Tracks[0]); - } - Tracks = tracks.ToArray(); - Invalidate(); - } - catch (Exception e) - { - OnError("render", e); - } - } - - public void RenderTracks(Track[] tracks) - { - if (tracks.Length == 0) - { - Score = null; - } - else - { - Score = tracks[0].Score; - } - - Tracks = tracks; - Invalidate(); - } - - - public void UpdateSettings(Settings settings) - { - Settings = settings; - } - - public void Invalidate() - { - if (Settings.Width == 0) - { - Logger.Warning("Rendering", "AlphaTab skipped rendering because of width=0 (element invisible)"); - return; - } - - BoundsLookup = new BoundsLookup(); - if (Tracks.Length == 0) return; - - RecreateCanvas(); - if (RenderingResources.Scale != Settings.Scale) - { - RenderingResources.Init(Settings.Scale); - Canvas.LineWidth = Settings.Scale; - } - Canvas.Resources = RenderingResources; - - Logger.Info("Rendering", "Rendering " + Tracks.Length + " tracks"); - for (int i = 0; i < Tracks.Length; i++) - { - var track = Tracks[i]; - Logger.Info("Rendering", "Track " + i + ": " + track.Name); - } - - OnPreRender(); - RecreateLayout(); - LayoutAndRender(); - _renderedTracks = Tracks; - Logger.Info("Rendering", "Rendering finished"); - } - - public void Resize(int width) - { - if (RecreateLayout() || RecreateCanvas() || _renderedTracks != Tracks || Tracks == null) - { - Logger.Info("Rendering", "Starting full rerendering due to layout or canvas change"); - Invalidate(); - } - else if (Layout.SupportsResize) - { - Logger.Info("Rendering", "Starting optimized rerendering for resize"); - BoundsLookup = new BoundsLookup(); - OnPreRender(); - Settings.Width = width; - Layout.Resize(); - Layout.RenderAnnotation(); - OnRenderFinished(); - OnPostRender(); - } - else - { - Logger.Warning("Rendering", "Current layout does not support dynamic resizing, nothing was done"); - } - Logger.Debug("Rendering", "Resize finished"); - } - - private void LayoutAndRender() - { - Logger.Info("Rendering", "Rendering at scale " + Settings.Scale + " with layout " + Layout.Name); - Layout.LayoutAndRender(); - Layout.RenderAnnotation(); - OnRenderFinished(); - OnPostRender(); - } - - public event Action PreRender; - protected virtual void OnPreRender() - { - var result = Canvas.OnPreRender(); - var handler = PreRender; - var args = new RenderFinishedEventArgs(); - args.TotalWidth = 0; - args.TotalHeight = 0; - args.Width = 0; - args.Height = 0; - args.RenderResult = result; - - if (handler != null) handler(args); - } - - public event Action PartialRenderFinished; - - public virtual void OnPartialRenderFinished(RenderFinishedEventArgs e) - { - Action handler = PartialRenderFinished; - if (handler != null) handler(e); - } - - public event Action RenderFinished; - protected virtual void OnRenderFinished() - { - var result = Canvas.OnRenderFinished(); - Action handler = RenderFinished; - if (handler != null) handler(new RenderFinishedEventArgs - { - RenderResult = result, - TotalHeight = Layout.Height, - TotalWidth = Layout.Width - }); - } - - public event Action Error; - protected virtual void OnError(string type, Exception details) - { - var handler = Error; - if (handler != null) handler(type, details); - } - - public event Action PostRenderFinished; - protected virtual void OnPostRender() - { - Action handler = PostRenderFinished; - if (handler != null) handler(); - } - - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Staves/BarLayoutingInfo.cs b/Source/AlphaTab/Rendering/Staves/BarLayoutingInfo.cs deleted file mode 100644 index beb329e01..000000000 --- a/Source/AlphaTab/Rendering/Staves/BarLayoutingInfo.cs +++ /dev/null @@ -1,346 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using AlphaTab.Audio; -using AlphaTab.Collections; -using AlphaTab.Model; -using AlphaTab.Platform; - -namespace AlphaTab.Rendering.Staves -{ - /// - /// This public class stores size information about a stave. - /// It is used by the layout engine to collect the sizes of score parts - /// to align the parts across multiple staves. - /// - class BarLayoutingInfo - { - private const int MinDuration = 30; - private const int MinDurationWidth = 10; - - private FastList _timeSortedSprings; - private float _xMin; - private int _minTime; - - private float _onTimePositionsForce; - private FastDictionary _onTimePositions; - - /// - /// an internal version number that increments whenever a change was made. - /// - public int Version { get; set; } - public FastDictionary PreBeatSizes { get; set; } - public FastDictionary OnBeatSizes { get; set; } - public FastDictionary OnBeatCenterX { get; set; } - - public float PreBeatSize { get; set; } - public float PostBeatSize { get; set; } - - public float VoiceSize { get; set; } - public float MinStretchForce { get; set; } - public float TotalSpringConstant { get; set; } - - public BarLayoutingInfo() - { - PreBeatSizes = new FastDictionary(); - OnBeatSizes = new FastDictionary(); - OnBeatCenterX = new FastDictionary(); - VoiceSize = 0; - Springs = new FastDictionary(); - Version = 0; - _timeSortedSprings = new FastList(); - _minTime = int.MaxValue; - } - - public void UpdateVoiceSize(float size) - { - if (size > VoiceSize) - { - VoiceSize = size; - Version++; - } - } - - public void SetPreBeatSize(Beat beat, float size) - { - if (!PreBeatSizes.ContainsKey(beat.Index) || PreBeatSizes[beat.Index] < size) - { - PreBeatSizes[beat.Index] = size; - Version++; - } - } - - public float GetPreBeatSize(Beat beat) - { - if (PreBeatSizes.ContainsKey(beat.Index)) - { - return PreBeatSizes[beat.Index]; - } - return 0; - } - - public void SetOnBeatSize(Beat beat, float size) - { - if (!OnBeatSizes.ContainsKey(beat.Index) || OnBeatSizes[beat.Index] < size) - { - OnBeatSizes[beat.Index] = size; - Version++; - } - } - - public float GetOnBeatSize(Beat beat) - { - if (OnBeatSizes.ContainsKey(beat.Index)) - { - return OnBeatSizes[beat.Index]; - } - return 0; - } - - - public float GetBeatCenterX(Beat beat) - { - if (OnBeatCenterX.ContainsKey(beat.Index)) - { - return OnBeatCenterX[beat.Index]; - } - return 0; - } - - public void SetBeatCenterX(Beat beat, float x) - { - if (!OnBeatCenterX.ContainsKey(beat.Index) || OnBeatCenterX[beat.Index] < x) - { - OnBeatCenterX[beat.Index] = x; - Version++; - } - } - - public void UpdateMinStretchForce(float force) - { - if (MinStretchForce < force) - { - MinStretchForce = force; - Version++; - } - } - - public FastDictionary Springs { get; set; } - - public Spring AddSpring(int start, int duration, float preSpringSize, float postSpringSize) - { - Version++; - Spring spring; - if (!Springs.ContainsKey(start)) - { - spring = new Spring(); - spring.TimePosition = start; - spring.AllDurations.Add(duration); - - // check in the previous spring for the shortest duration that overlaps with this spring - // Gourlay defines that we need the smallest note duration that either starts **or continues** on the current spring. - if (_timeSortedSprings.Count > 0) - { - int smallestDuration = duration; - var previousSpring = _timeSortedSprings[_timeSortedSprings.Count - 1]; - foreach (var prevDuration in previousSpring.AllDurations) - { - var end = previousSpring.TimePosition + prevDuration; - if (end >= start && prevDuration < smallestDuration) - { - smallestDuration = prevDuration; - } - } - } - spring.LongestDuration = duration; - spring.PostSpringWidth = postSpringSize; - spring.PreSpringWidth = preSpringSize; - Springs[start] = spring; - - var timeSorted = _timeSortedSprings; - var insertPos = timeSorted.Count - 1; - while (insertPos > 0 && timeSorted[insertPos].TimePosition > start) - { - insertPos--; - } - _timeSortedSprings.InsertAt(insertPos + 1, spring); - } - else - { - spring = Springs[start]; - if (spring.PostSpringWidth < postSpringSize) - { - spring.PostSpringWidth = postSpringSize; - } - if (spring.PreSpringWidth < preSpringSize) - { - spring.PreSpringWidth = preSpringSize; - } - if (duration < spring.SmallestDuration) - { - spring.SmallestDuration = duration; - } - if (duration > spring.LongestDuration) - { - spring.LongestDuration = duration; - } - spring.AllDurations.Add(duration); - } - - if (_minTime > start) - { - _minTime = start; - } - - return spring; - } - - public Spring AddBeatSpring(Beat beat, float preBeatSize, float postBeatSize) - { - var start = beat.AbsoluteDisplayStart; - return AddSpring(start, beat.DisplayDuration, preBeatSize, postBeatSize); - } - - public void Finish() - { - CalculateSpringConstants(); - Version++; - } - - private void CalculateSpringConstants() - { - _xMin = 0f; - var springs = Springs; - foreach (var time in springs) - { - var spring = springs[time]; - if (spring.SpringWidth < _xMin) - { - _xMin = spring.SpringWidth; - } - } - - var totalSpringConstant = 0f; - var sortedSprings = _timeSortedSprings; - for (int i = 0; i < sortedSprings.Count; i++) - { - var currentSpring = sortedSprings[i]; - int duration; - if (i == sortedSprings.Count - 1) - { - duration = currentSpring.LongestDuration; - } - else - { - var nextSpring = sortedSprings[i + 1]; - duration = Math.Abs(nextSpring.TimePosition - currentSpring.TimePosition); - } - currentSpring.SpringConstant = CalculateSpringConstant(currentSpring, duration); - totalSpringConstant += 1 / currentSpring.SpringConstant; - } - TotalSpringConstant = 1 / totalSpringConstant; - - // calculate the force required to have at least the minimum size. - for (int i = 0; i < sortedSprings.Count; i++) - { - var force = sortedSprings[i].SpringWidth * sortedSprings[i].SpringConstant; - UpdateMinStretchForce(force); - } - } - - private float CalculateSpringConstant(Spring spring, int duration) - { - if (duration <= 0) - { - duration = Duration.SixtyFourth.ToTicks(); - } - - if (spring.SmallestDuration == 0) - { - spring.SmallestDuration = duration; - } - float minDuration = spring.SmallestDuration; - var phi = 1 + 0.6f * Platform.Platform.Log2(duration / (float)MinDuration); - return (minDuration / duration) * (1 / (phi * MinDurationWidth)); - } - - public float SpaceToForce(float space) - { - return space * TotalSpringConstant; - } - - public float CalculateVoiceWidth(float force) - { - return CalculateWidth(force, TotalSpringConstant); - } - - public float CalculateWidth(float force, float springConstant) - { - return force / springConstant; - } - - public FastDictionary BuildOnTimePositions(float force) - { - if (Math.Abs(_onTimePositionsForce - force) < 0.00001 && _onTimePositions != null) - { - return _onTimePositions; - } - _onTimePositionsForce = force; - - var positions = _onTimePositions = new FastDictionary(); - - var sortedSprings = _timeSortedSprings; - if (sortedSprings.Count == 0) - { - return positions; - } - - var springX = sortedSprings[0].PreSpringWidth; - for (int i = 0; i < sortedSprings.Count; i++) - { - positions[sortedSprings[i].TimePosition] = springX; - springX += CalculateWidth(force, sortedSprings[i].SpringConstant); - } - - return positions; - } - } - - class Spring - { - public int TimePosition { get; set; } - - public int LongestDuration { get; set; } - public int SmallestDuration { get; set; } - - public float Force { get; set; } - public float SpringConstant { get; set; } - - public float SpringWidth => PreSpringWidth + PostSpringWidth; - public float PreSpringWidth { get; set; } - public float PostSpringWidth { get; set; } - - public FastList AllDurations { get; set; } - - public Spring() - { - AllDurations = new FastList(); - } - } -} diff --git a/Source/AlphaTab/Rendering/Staves/MasterBarsRenderers.cs b/Source/AlphaTab/Rendering/Staves/MasterBarsRenderers.cs deleted file mode 100644 index c957ad137..000000000 --- a/Source/AlphaTab/Rendering/Staves/MasterBarsRenderers.cs +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Collections; -using AlphaTab.Model; - -namespace AlphaTab.Rendering.Staves -{ - /// - /// This container represents a single column of bar renderers independent from any staves. - /// This container can be used to reorganize renderers into a new staves. - /// - class MasterBarsRenderers - { - public float Width { get; set; } - public bool IsLinkedToPrevious { get; set; } - public bool CanWrap { get; set; } - public MasterBar MasterBar { get; set; } - public FastList Renderers { get; set; } - public BarLayoutingInfo LayoutingInfo { get; set; } - - public MasterBarsRenderers() - { - Renderers = new FastList(); - CanWrap = true; - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Staves/Staff.cs b/Source/AlphaTab/Rendering/Staves/Staff.cs deleted file mode 100644 index 058f45a5d..000000000 --- a/Source/AlphaTab/Rendering/Staves/Staff.cs +++ /dev/null @@ -1,242 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.Collections; -using AlphaTab.Model; -using AlphaTab.Platform; -using AlphaTab.Rendering.Layout; - -namespace AlphaTab.Rendering.Staves -{ - /// - /// A Staff represents a single line within a StaveGroup. - /// It stores BarRenderer instances created from a given factory. - /// - class Staff - { - private readonly BarRendererFactory _factory; - - private FastDictionary _sharedLayoutData; - - public StaveTrackGroup StaveTrackGroup { get; set; } - public StaveGroup StaveGroup { get; set; } - - public FastList BarRenderers { get; set; } - - public float X { get; set; } - public float Y { get; set; } - public float Height { get; set; } - public int Index { get; set; } - public int StaffIndex { get; set; } - - /// - /// This is the index of the track being rendered. This is not the index of the track within the model, - /// but the n-th track being rendered. It is the index of the array defining - /// which tracks should be rendered. - /// For single-track rendering this will always be zero. - /// - public int TrackIndex { get; set; } - public Model.Staff ModelStaff { get; set; } - public string StaveId - { - get { return _factory.StaffId; } - } - - /// - /// This is the visual offset from top where the - /// Staff contents actually start. Used for grouping - /// using a accolade - /// - public float StaveTop { get; set; } - public float TopSpacing { get; set; } - public float BottomSpacing { get; set; } - /// - /// This is the visual offset from top where the - /// Staff contents actually ends. Used for grouping - /// using a accolade - /// - public float StaveBottom { get; set; } - - public bool IsFirstInAccolade { get; set; } - public bool IsLastInAccolade { get; set; } - - public Staff(int trackIndex, Model.Staff staff, BarRendererFactory factory) - { - BarRenderers = new FastList(); - TrackIndex = trackIndex; - ModelStaff = staff; - _factory = factory; - TopSpacing = 20; - BottomSpacing = 5; - StaveTop = 0; - StaveBottom = 0; - _sharedLayoutData = new FastDictionary(); - } - - public T GetSharedLayoutData(string key, T def) - { - if (_sharedLayoutData.ContainsKey(key)) - { - return (T)_sharedLayoutData[key]; - } - - return def; - } - - public void SetSharedLayoutData(string key, T def) - { - _sharedLayoutData[key] = def; - } - - public bool IsInAccolade - { - get - { - return _factory.IsInAccolade; - } - } - - public void RegisterStaffTop(float offset) - { - StaveTop = offset; - } - - public void RegisterStaffBottom(float offset) - { - StaveBottom = offset; - } - - public void AddBarRenderer(BarRendererBase renderer) - { - renderer.Staff = this; - renderer.Index = BarRenderers.Count; - renderer.ReLayout(); - BarRenderers.Add(renderer); - StaveGroup.Layout.RegisterBarRenderer(StaveId, renderer); - } - - public void AddBar(Bar bar, BarLayoutingInfo layoutingInfo) - { - BarRendererBase renderer; - if (bar == null) - { - renderer = new BarRendererBase(StaveGroup.Layout.Renderer, bar); - } - else - { - renderer = _factory.Create(StaveGroup.Layout.Renderer, bar, StaveGroup.Layout.Renderer.Settings.Staves); - } - renderer.Staff = this; - renderer.Index = BarRenderers.Count; - renderer.LayoutingInfo = layoutingInfo; - renderer.DoLayout(); - renderer.RegisterLayoutingInfo(); - BarRenderers.Add(renderer); - if (bar != null) - { - StaveGroup.Layout.RegisterBarRenderer(StaveId, renderer); - } - } - - public BarRendererBase RevertLastBar() - { - var lastBar = BarRenderers[BarRenderers.Count - 1]; - BarRenderers.RemoveAt(BarRenderers.Count - 1); - StaveGroup.Layout.UnregisterBarRenderer(StaveId, lastBar); - return lastBar; - } - - public void ScaleToWidth(float width) - { - _sharedLayoutData = new FastDictionary(); - // Note: here we could do some "intelligent" distribution of - // the space over the bar renderers, for now we evenly apply the space to all bars - var difference = width - StaveGroup.Width; - var spacePerBar = difference / BarRenderers.Count; - for (int i = 0, j = BarRenderers.Count; i < j; i++) - { - BarRenderers[i].ScaleToWidth(BarRenderers[i].Width + spacePerBar); - } - } - - public float TopOverflow - { - get - { - var m = 0f; - for (int i = 0, j = BarRenderers.Count; i < j; i++) - { - var r = BarRenderers[i]; - if (r.TopOverflow > m) - { - m = r.TopOverflow; - } - } - return m; - } - } - - public float BottomOverflow - { - get - { - var m = 0f; - for (int i = 0, j = BarRenderers.Count; i < j; i++) - { - var r = BarRenderers[i]; - if (r.BottomOverflow > m) - { - m = r.BottomOverflow; - } - } - return m; - } - } - - public void FinalizeStaff() - { - var x = 0f; - Height = 0; - - var topOverflow = TopOverflow; - var bottomOverflow = BottomOverflow; - for (var i = 0; i < BarRenderers.Count; i++) - { - BarRenderers[i].X = x; - BarRenderers[i].Y = TopSpacing + topOverflow; - Height = Math.Max(Height, BarRenderers[i].Height); - BarRenderers[i].FinalizeRenderer(); - x += BarRenderers[i].Width; - } - - if (Height > 0) - { - Height += TopSpacing + topOverflow + bottomOverflow + BottomSpacing; - } - } - - public void Paint(float cx, float cy, ICanvas canvas, int startIndex, int count) - { - if (Height == 0 || count == 0) return; - for (int i = startIndex, j = Math.Min(startIndex + count, BarRenderers.Count); i < j; i++) - { - BarRenderers[i].Paint(cx + X, cy + Y, canvas); - } - } - } -} diff --git a/Source/AlphaTab/Rendering/Staves/StaveGroup.cs b/Source/AlphaTab/Rendering/Staves/StaveGroup.cs deleted file mode 100644 index 61cd16488..000000000 --- a/Source/AlphaTab/Rendering/Staves/StaveGroup.cs +++ /dev/null @@ -1,498 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; -using AlphaTab.Collections; -using AlphaTab.Model; -using AlphaTab.Platform; -using AlphaTab.Rendering.Layout; -using AlphaTab.Rendering.Utils; - -namespace AlphaTab.Rendering.Staves -{ - /// - /// A Staff consists of a list of different staves and groups - /// them using an accolade. - /// - class StaveGroup - { - private const float AccoladeLabelSpacing = 10; - - private readonly FastList _allStaves; - private Staff _firstStaffInAccolade; - private Staff _lastStaffInAccolade; - - public float X { get; set; } - public float Y { get; set; } - public int Index { get; set; } - - private bool _accoladeSpacingCalculated; - public float AccoladeSpacing { get; set; } - - /// - /// Indicates whether this line is full or not. If the line is full the - /// bars can be aligned to the maximum width. If the line is not full - /// the bars will not get stretched. - /// - public bool IsFull { get; set; } - - /// - /// The width that the content bars actually need - /// - public float Width { get; set; } - - public bool IsLast { get; set; } - - public FastList MasterBarsRenderers { get; set; } - - public FastList Staves { get; set; } - - public ScoreLayout Layout { get; set; } - - public StaveGroup() - { - MasterBarsRenderers = new FastList(); - Staves = new FastList(); - _allStaves = new FastList(); - Width = 0; - Index = 0; - _accoladeSpacingCalculated = false; - AccoladeSpacing = 0; - } - - public int FirstBarIndex - { - get - { - return MasterBarsRenderers[0].MasterBar.Index; - } - } - - public int LastBarIndex - { - get - { - return MasterBarsRenderers[MasterBarsRenderers.Count - 1].MasterBar.Index; - } - } - - public MasterBarsRenderers AddMasterBarRenderers(Track[] tracks, MasterBarsRenderers renderers) - { - if (tracks.Length == 0) return null; - - MasterBarsRenderers.Add(renderers); - CalculateAccoladeSpacing(tracks); - - renderers.LayoutingInfo.PreBeatSize = 0; - - var src = 0; - - for (int i = 0, j = Staves.Count; i < j; i++) - { - var g = Staves[i]; - for (int k = 0, l = g.Staves.Count; k < l; k++) - { - var s = g.Staves[k]; - var renderer = renderers.Renderers[src++]; - - s.AddBarRenderer(renderer); - } - } - - //Width += renderers.Width; - UpdateWidth(); - - return renderers; - } - - public MasterBarsRenderers AddBars(Track[] tracks, int barIndex) - { - if (tracks.Length == 0) return null; - - var result = new MasterBarsRenderers(); - result.LayoutingInfo = new BarLayoutingInfo(); - result.MasterBar = tracks[0].Score.MasterBars[barIndex]; - MasterBarsRenderers.Add(result); - - CalculateAccoladeSpacing(tracks); - - // add renderers - var barLayoutingInfo = result.LayoutingInfo; - foreach (var g in Staves) - { - foreach (var s in g.Staves) - { - var bar = g.Track.Staves[s.ModelStaff.Index].Bars[barIndex]; - s.AddBar(bar, barLayoutingInfo); - var renderer = s.BarRenderers[s.BarRenderers.Count - 1]; - result.Renderers.Add(renderer); - if (renderer.IsLinkedToPrevious) - { - result.IsLinkedToPrevious = true; - } - - if (!renderer.CanWrap) - { - result.CanWrap = false; - } - } - } - barLayoutingInfo.Finish(); - - // ensure same widths of new renderer - result.Width = UpdateWidth(); - - return result; - } - - public MasterBarsRenderers RevertLastBar() - { - if (MasterBarsRenderers.Count > 1) - { - var toRemove = MasterBarsRenderers[MasterBarsRenderers.Count - 1]; - MasterBarsRenderers.RemoveAt(MasterBarsRenderers.Count - 1); - var w = 0f; - for (int i = 0, j = _allStaves.Count; i < j; i++) - { - var s = _allStaves[i]; - var lastBar = s.RevertLastBar(); - w = Math.Max(w, lastBar.Width); - } - Width -= w; - - return toRemove; - } - return null; - } - - public float UpdateWidth() - { - var realWidth = 0f; - for (int i = 0, j = _allStaves.Count; i < j; i++) - { - var s = _allStaves[i]; - s.BarRenderers[s.BarRenderers.Count - 1].ApplyLayoutingInfo(); - if (s.BarRenderers[s.BarRenderers.Count - 1].Width > realWidth) - { - realWidth = s.BarRenderers[s.BarRenderers.Count - 1].Width; - } - } - Width += realWidth; - return realWidth; - } - - private void CalculateAccoladeSpacing(Track[] tracks) - { - if (!_accoladeSpacingCalculated && Index == 0) - { - _accoladeSpacingCalculated = true; - if (Layout.Renderer.Settings.Layout.Get("hideTrackNames", false)) - { - AccoladeSpacing = 0; - } - else - { - var canvas = Layout.Renderer.Canvas; - var res = Layout.Renderer.RenderingResources.EffectFont; - canvas.Font = res; - foreach (var t in tracks) - { - AccoladeSpacing = (float)Math.Ceiling(Math.Max(AccoladeSpacing, canvas.MeasureText(t.ShortName))); - } - AccoladeSpacing += (2 * AccoladeLabelSpacing); - Width += AccoladeSpacing; - } - } - } - - private StaveTrackGroup GetStaveTrackGroup(Track track) - { - for (int i = 0, j = Staves.Count; i < j; i++) - { - var g = Staves[i]; - if (g.Track == track) - { - return g; - } - } - return null; - } - - public void AddStaff(Track track, Staff staff) - { - var group = GetStaveTrackGroup(track); - if (group == null) - { - group = new StaveTrackGroup(this, track); - Staves.Add(group); - } - - staff.StaveTrackGroup = group; - staff.StaveGroup = this; - staff.Index = _allStaves.Count; - _allStaves.Add(staff); - group.Staves.Add(staff); - - if (staff.IsInAccolade) - { - if (_firstStaffInAccolade == null) - { - _firstStaffInAccolade = staff; - staff.IsFirstInAccolade = true; - } - if (group.FirstStaffInAccolade == null) - { - group.FirstStaffInAccolade = staff; - } - if (_lastStaffInAccolade == null) - { - _lastStaffInAccolade = staff; - staff.IsLastInAccolade = true; - } - - if (_lastStaffInAccolade != null) { _lastStaffInAccolade.IsLastInAccolade = false; } - _lastStaffInAccolade = staff; - _lastStaffInAccolade.IsLastInAccolade = true; - group.LastStaffInAccolade = staff; - } - } - - public float Height - { - get - { - return _allStaves[_allStaves.Count - 1].Y + _allStaves[_allStaves.Count - 1].Height; - } - } - - public void ScaleToWidth(float width) - { - for (int i = 0, j = _allStaves.Count; i < j; i++) - { - _allStaves[i].ScaleToWidth(width); - } - Width = width; - } - - public void Paint(float cx, float cy, ICanvas canvas) - { - PaintPartial(cx + X, cy + Y, canvas, 0, MasterBarsRenderers.Count); - } - - public void PaintPartial(float cx, float cy, ICanvas canvas, int startIndex, int count) - { - BuildBoundingsLookup(cx, cy); - - for (int i = 0, j = _allStaves.Count; i < j; i++) - { - _allStaves[i].Paint(cx, cy, canvas, startIndex, count); - } - - var res = Layout.Renderer.RenderingResources; - - if (Staves.Count > 0 && startIndex == 0) - { - // - // Draw start grouping - // - - if (_firstStaffInAccolade != null && _lastStaffInAccolade != null) - { - // - // draw grouping line for all staves - // - - var firstStart = cy + _firstStaffInAccolade.Y + _firstStaffInAccolade.StaveTop + _firstStaffInAccolade.TopSpacing + _firstStaffInAccolade.TopOverflow; - var lastEnd = cy + _lastStaffInAccolade.Y + _lastStaffInAccolade.TopSpacing + _lastStaffInAccolade.TopOverflow - + _lastStaffInAccolade.StaveBottom; - - var acooladeX = cx + _firstStaffInAccolade.X; - - canvas.Color = res.BarSeperatorColor; - - canvas.BeginPath(); - canvas.MoveTo(acooladeX, firstStart); - canvas.LineTo(acooladeX, lastEnd); - canvas.Stroke(); - } - - // - // Draw accolade for each track group - // - canvas.Font = res.EffectFont; - for (int i = 0, j = Staves.Count; i < j; i++) - { - var g = Staves[i]; - if (g.FirstStaffInAccolade != null && g.LastStaffInAccolade != null) - { - var firstStart = cy + g.FirstStaffInAccolade.Y + g.FirstStaffInAccolade.StaveTop + g.FirstStaffInAccolade.TopSpacing + g.FirstStaffInAccolade.TopOverflow; - var lastEnd = cy + g.LastStaffInAccolade.Y + g.LastStaffInAccolade.TopSpacing + g.LastStaffInAccolade.TopOverflow - + g.LastStaffInAccolade.StaveBottom; - - var acooladeX = cx + g.FirstStaffInAccolade.X; - - var barSize = (3 * Layout.Renderer.Settings.Scale); - var barOffset = barSize; - - var accoladeStart = firstStart - (barSize * 4); - var accoladeEnd = lastEnd + (barSize * 4); - - // text - if (Index == 0 && !Layout.Renderer.Settings.Layout.Get("hideTrackNames", false)) - { - canvas.FillText(g.Track.ShortName, cx + (AccoladeLabelSpacing * Layout.Scale), firstStart); - } - - // rect - canvas.FillRect(acooladeX - barOffset - barSize, accoladeStart, barSize, accoladeEnd - accoladeStart); - - var spikeStartX = acooladeX - barOffset - barSize; - var spikeEndX = acooladeX + barSize * 2; - - // top spike - canvas.BeginPath(); - canvas.MoveTo(spikeStartX, accoladeStart); - canvas.BezierCurveTo(spikeStartX, accoladeStart, spikeStartX, accoladeStart, spikeEndX, accoladeStart - barSize); - canvas.BezierCurveTo(acooladeX, accoladeStart + barSize, spikeStartX, accoladeStart + barSize, spikeStartX, accoladeStart + barSize); - canvas.ClosePath(); - canvas.Fill(); - - // bottom spike - canvas.BeginPath(); - canvas.MoveTo(spikeStartX, accoladeEnd); - canvas.BezierCurveTo(spikeStartX, accoladeEnd, acooladeX, accoladeEnd, spikeEndX, accoladeEnd + barSize); - canvas.BezierCurveTo(acooladeX, accoladeEnd - barSize, spikeStartX, accoladeEnd - barSize, spikeStartX, accoladeEnd - barSize); - canvas.ClosePath(); - - canvas.Fill(); - } - } - } - } - - - public void FinalizeGroup() - { - float currentY = 0; - foreach (var staff in _allStaves) - { - staff.X = AccoladeSpacing; - staff.Y = (currentY); - staff.FinalizeStaff(); - currentY += staff.Height; - } - } - - private void BuildBoundingsLookup(float cx, float cy) - { - if (Layout.Renderer.BoundsLookup.IsFinished) return; - if (_firstStaffInAccolade == null || _lastStaffInAccolade == null) return; - - var lastStaff = _allStaves[_allStaves.Count - 1]; - - var visualTop = cy + Y + _firstStaffInAccolade.Y; - var visualBottom = cy + Y + _lastStaffInAccolade.Y + _lastStaffInAccolade.Height; - var realTop = cy + Y + _allStaves[0].Y; - var realBottom = cy + Y + lastStaff.Y + lastStaff.Height; - var lineTop = cy + Y + _firstStaffInAccolade.Y - + _firstStaffInAccolade.TopSpacing - + _firstStaffInAccolade.TopOverflow - + (_firstStaffInAccolade.BarRenderers.Count > 0 ? _firstStaffInAccolade.BarRenderers[0].TopPadding : 0); - var lineBottom = cy + Y + lastStaff.Y + lastStaff.Height - - lastStaff.BottomSpacing - - lastStaff.BottomOverflow - - (lastStaff.BarRenderers.Count > 0 ? lastStaff.BarRenderers[0].BottomPadding : 0); - - var visualHeight = visualBottom - visualTop; - var lineHeight = lineBottom - lineTop; - var realHeight = realBottom - realTop; - - var x = X + _firstStaffInAccolade.X; - - var staveGroupBounds = new StaveGroupBounds(); - staveGroupBounds.VisualBounds = new Bounds - { - X = cx, - Y = cy + Y, - W = Width, - H = Height - }; - staveGroupBounds.RealBounds = new Bounds - { - X = cx, - Y = cy + Y, - W = Width, - H = Height - }; - Layout.Renderer.BoundsLookup.AddStaveGroup(staveGroupBounds); - - var masterBarBoundsLookup = new FastList(); - for (int i = 0; i < Staves.Count; i++) - { - - for (int j = 0, k = Staves[i].FirstStaffInAccolade.BarRenderers.Count; j < k; j++) - { - var renderer = Staves[i].FirstStaffInAccolade.BarRenderers[j]; - - if (i == 0) - { - var masterBarBounds = new MasterBarBounds(); - masterBarBounds.Index = renderer.Bar.MasterBar.Index; - masterBarBounds.IsFirstOfLine = renderer.IsFirstOfLine; - masterBarBounds.RealBounds = new Bounds - { - X = x + renderer.X, - Y = realTop, - W = renderer.Width, - H = realHeight - }; - masterBarBounds.VisualBounds = new Bounds - { - X = x + renderer.X, - Y = visualTop, - W = renderer.Width, - H = visualHeight - }; - masterBarBounds.LineAlignedBounds = new Bounds - { - X = x + renderer.X, - Y = lineTop, - W = renderer.Width, - H = lineHeight - }; - Layout.Renderer.BoundsLookup.AddMasterBar(masterBarBounds); - masterBarBoundsLookup.Add(masterBarBounds); - } - - renderer.BuildBoundingsLookup(masterBarBoundsLookup[j], x, cy + Y + _firstStaffInAccolade.Y); - } - } - - } - - public float GetBarX(int index) - { - if (_firstStaffInAccolade == null || Layout.Renderer.Tracks.Length == 0) - { - return 0; - } - var bar = Layout.Renderer.Tracks[0].Staves[0].Bars[index]; - var renderer = Layout.GetRendererForBar(_firstStaffInAccolade.StaveId, bar); - return renderer.X; - } - } -} diff --git a/Source/AlphaTab/Rendering/Staves/StaveTrackGroup.cs b/Source/AlphaTab/Rendering/Staves/StaveTrackGroup.cs deleted file mode 100644 index 89f502971..000000000 --- a/Source/AlphaTab/Rendering/Staves/StaveTrackGroup.cs +++ /dev/null @@ -1,22 +0,0 @@ -using AlphaTab.Collections; -using AlphaTab.Model; - -namespace AlphaTab.Rendering.Staves -{ - class StaveTrackGroup - { - public Track Track { get; set; } - public StaveGroup StaveGroup { get; set; } - public FastList Staves { get; set; } - - public Staff FirstStaffInAccolade { get; set; } - public Staff LastStaffInAccolade { get; set; } - - public StaveTrackGroup(StaveGroup staveGroup, Track track) - { - StaveGroup = staveGroup; - Track = track; - Staves = new FastList(); - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/TabBarRenderer.cs b/Source/AlphaTab/Rendering/TabBarRenderer.cs deleted file mode 100644 index 77e58fb5a..000000000 --- a/Source/AlphaTab/Rendering/TabBarRenderer.cs +++ /dev/null @@ -1,517 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Collections; -using AlphaTab.Model; -using AlphaTab.Platform; -using AlphaTab.Rendering.Glyphs; -using AlphaTab.Rendering.Utils; - -namespace AlphaTab.Rendering -{ - /// - /// This BarRenderer renders a bar using guitar tablature notation - /// - class TabBarRenderer : BarRendererBase - { - public const string StaffId = "tab"; - - public const float LineSpacing = 10; - - public bool ShowTimeSignature { get; set; } - public bool ShowRests { get; set; } - public bool ShowTiedNotes { get; set; } - - public TabBarRenderer(ScoreRenderer renderer, Bar bar) - : base(renderer, bar) - { - RhythmHeight = 15 * renderer.Layout.Scale; - RhythmBeams = true; - } - - public float LineOffset - { - get - { - return ((LineSpacing + 1) * Scale); - } - } - - public bool RenderRhythm { get; set; } - public float RhythmHeight { get; set; } - public bool RhythmBeams { get; set; } - - public override float GetNoteX(Note note, bool onEnd = true) - { - var beat = (TabBeatGlyph)GetOnNotesGlyphForBeat(note.Beat); - if (beat != null) - { - return beat.Container.X + beat.Container.VoiceContainer.X + beat.X + beat.NoteNumbers.GetNoteX(note, onEnd); - } - return 0; - } - - public override float GetNoteY(Note note, bool aboveNote = false) - { - var beat = (TabBeatGlyph)GetOnNotesGlyphForBeat(note.Beat); - if (beat != null) - { - return beat.NoteNumbers.GetNoteY(note, aboveNote); - } - return 0; - } - - protected override void UpdateSizes() - { - var res = Resources; - var numberOverflow = (res.TablatureFont.Size / 2) + (res.TablatureFont.Size * 0.2f); - TopPadding = numberOverflow; - BottomPadding = numberOverflow; - Height = LineOffset * (Bar.Staff.Tuning.Length - 1) + (numberOverflow * 2); - - if (RenderRhythm) - { - Height += RhythmHeight; - BottomPadding += RhythmHeight; - } - - base.UpdateSizes(); - } - - protected override void CreatePreBeatGlyphs() - { - base.CreatePreBeatGlyphs(); - if (Bar.MasterBar.IsRepeatStart) - { - AddPreBeatGlyph(new RepeatOpenGlyph(0, 0, 1.5f, 3)); - } - - // Clef - if (IsFirstOfLine) - { - var center = (Bar.Staff.Tuning.Length + 1) / 2f; - AddPreBeatGlyph(new TabClefGlyph(5 * Scale, GetTabY(center))); - } - - // Time Signature - if (ShowTimeSignature && - ((Bar.PreviousBar == null) || (Bar.PreviousBar != null && Bar.MasterBar.TimeSignatureNumerator != Bar.PreviousBar.MasterBar.TimeSignatureNumerator) || (Bar.PreviousBar != null && Bar.MasterBar.TimeSignatureDenominator != Bar.PreviousBar.MasterBar.TimeSignatureDenominator))) - { - CreateStartSpacing(); - CreateTimeSignatureGlyphs(); - } - - AddPreBeatGlyph(new BarNumberGlyph(0, GetTabY(-0.5f), Bar.Index + 1)); - - if (Bar.IsEmpty) - { - AddPreBeatGlyph(new SpacingGlyph(0, 0, 30 * Scale)); - } - } - - private bool _startSpacing; - - private void CreateStartSpacing() - { - if (_startSpacing) return; - AddPreBeatGlyph(new SpacingGlyph(0, 0, 2 * Scale)); - _startSpacing = true; - } - - private void CreateTimeSignatureGlyphs() - { - AddPreBeatGlyph(new SpacingGlyph(0, 0, 5 * Scale)); - AddPreBeatGlyph(new TabTimeSignatureGlyph(0, GetTabY(0), Bar.MasterBar.TimeSignatureNumerator, Bar.MasterBar.TimeSignatureDenominator, Bar.MasterBar.TimeSignatureCommon)); - } - - protected override void CreateBeatGlyphs() - { - for (int v = 0; v < Bar.Voices.Count; v++) - { - var voice = Bar.Voices[v]; - if (HasVoiceContainer(voice)) - { - CreateVoiceGlyphs(Bar.Voices[v]); - } - } - } - - private void CreateVoiceGlyphs(Voice v) - { - for (int i = 0, j = v.Beats.Count; i < j; i++) - { - var b = v.Beats[i]; - var container = new TabBeatContainerGlyph(b, GetOrCreateVoiceContainer(v)); - container.PreNotes = new TabBeatPreNotesGlyph(); - container.OnNotes = new TabBeatGlyph(); - AddBeatGlyph(container); - } - } - - protected override void CreatePostBeatGlyphs() - { - base.CreatePostBeatGlyphs(); - if (Bar.MasterBar.IsRepeatEnd) - { - AddPostBeatGlyph(new RepeatCloseGlyph(X, 0)); - if (Bar.MasterBar.RepeatCount > 2) - { - AddPostBeatGlyph(new RepeatCountGlyph(0, GetTabY(-0.5f, -3), Bar.MasterBar.RepeatCount)); - } - } - else - { - AddPostBeatGlyph(new BarSeperatorGlyph(0, 0)); - } - } - - /// - /// Gets the relative y position of the given steps relative to first line. - /// - /// the amount of steps while 2 steps are one line - /// - /// - public float GetTabY(float line, float correction = 0) - { - return (LineOffset * line) + (correction * Scale); - } - - protected override void PaintBackground(float cx, float cy, ICanvas canvas) - { - base.PaintBackground(cx, cy, canvas); - - var res = Resources; - - // - // draw string lines - // - canvas.Color = res.StaveLineColor; - var lineY = cy + Y + TopPadding; - - var padding = Scale; - - // collect tab note position for spaces - var tabNotes = new FastList>(); - for (int i = 0, j = Bar.Staff.Tuning.Length; i < j; i++) - { - tabNotes.Add(new FastList()); - } - - foreach (Voice voice in Bar.Voices) - { - if (HasVoiceContainer(voice)) - { - var vc = GetOrCreateVoiceContainer(voice); - foreach (var bg in vc.BeatGlyphs) - { - var notes = ((TabBeatGlyph) bg.OnNotes); - var noteNumbers = notes.NoteNumbers; - if (noteNumbers != null) - { - foreach (var s in noteNumbers.NotesPerString) - { - var noteNumber = noteNumbers.NotesPerString[s]; - if (!noteNumber.IsEmpty) - { - tabNotes[Bar.Staff.Tuning.Length - s].Add( - new[] - { - vc.X + bg.X + notes.X + noteNumbers.X, - noteNumbers.Width + padding - }); - } - } - } - } - } - } - - // if we have multiple voices we need to sort by X-position, otherwise have a wild mix in the list - // but painting relies on ascending X-position - foreach (var line in tabNotes) - { - line.Sort((a, b) => a[0] > b[0] ? 1 : a[0] < b[0] ? -1 : 0); - } - - var lineOffset = LineOffset; - for (int i = 0, j = Bar.Staff.Tuning.Length; i < j; i++) - { - if (i > 0) lineY += lineOffset; - - var lineX = 0f; - foreach (var line in tabNotes[i]) - { - canvas.FillRect(cx + X + lineX, (int)lineY, line[0] - lineX, Scale); - lineX = line[0] + line[1]; - } - - canvas.FillRect(cx + X + lineX, (int)lineY, Width - lineX, Scale); - } - - canvas.Color = res.MainGlyphColor; - - PaintSimileMark(cx, cy, canvas); - - // Info guides for debugging - - //DrawInfoGuide(canvas, cx, cy, 0, new Color(255, 0, 0)); // top - //DrawInfoGuide(canvas, cx, cy, stave.StaveTop, new Color(0, 255, 0)); // stavetop - //DrawInfoGuide(canvas, cx, cy, stave.StaveBottom, new Color(0,255,0)); // stavebottom - //DrawInfoGuide(canvas, cx, cy, Height, new Color(255, 0, 0)); // bottom - } - - - public override void Paint(float cx, float cy, ICanvas canvas) - { - base.Paint(cx, cy, canvas); - if (RenderRhythm) - { - PaintBeams(cx, cy, canvas); - } - } - - - private void PaintBeams(float cx, float cy, ICanvas canvas) - { - for (int i = 0, j = Helpers.BeamHelpers.Count; i < j; i++) - { - var v = Helpers.BeamHelpers[i]; - for (int k = 0, l = v.Count; k < l; k++) - { - var h = v[k]; - PaintBeamHelper(cx + BeatGlyphsStart, cy, canvas, h); - } - } - } - - private void PaintBeamHelper(float cx, float cy, ICanvas canvas, BeamingHelper h) - { - canvas.Color = h.Voice.Index == 0 - ? Resources.MainGlyphColor - : Resources.SecondaryGlyphColor; - - // check if we need to paint simple footer - if (h.Beats.Count == 1 || !RhythmBeams) - { - PaintFooter(cx, cy, canvas, h); - } - else - { - PaintBar(cx, cy, canvas, h); - } - } - - private void PaintBar(float cx, float cy, ICanvas canvas, BeamingHelper h) - { - for (int i = 0, j = h.Beats.Count; i < j; i++) - { - var beat = h.Beats[i]; - - if (h.HasBeatLineX(beat)) - { - // - // draw line - // - var beatLineX = h.GetBeatLineX(beat); - var y1 = cy + Y; - var y2 = cy + Y + Height; - - var startGlyph = (TabBeatGlyph)GetOnNotesGlyphForBeat(beat); - if (startGlyph.NoteNumbers == null) - { - y1 += Height - RhythmHeight; - } - else - { - y1 += startGlyph.NoteNumbers.GetNoteY(startGlyph.NoteNumbers.MinStringNote) + LineOffset / 2; - } - - if (h.Direction == BeamDirection.Up) - { - beatLineX -= startGlyph.Width / 2f; - } - else - { - beatLineX += startGlyph.Width / 2f; - } - - canvas.BeginPath(); - canvas.MoveTo(cx + X + beatLineX, y1); - canvas.LineTo(cx + X + beatLineX, y2); - canvas.Stroke(); - - var brokenBarOffset = (6 * Scale); - var barSpacing = (6 * Scale); - var barSize = (3 * Scale); - var barCount = beat.Duration.GetIndex() - 2; - var barStart = cy + Y; - barSpacing = -barSpacing; - barStart += Height; - - for (int barIndex = 0; barIndex < barCount; barIndex++) - { - float barStartX; - float barEndX; - - float barStartY; - float barEndY; - - var barY = barStart + (barIndex * barSpacing); - - // - // Broken Bar to Next - // - if (h.Beats.Count == 1) - { - barStartX = beatLineX; - barEndX = beatLineX + brokenBarOffset; - barStartY = barY; - barEndY = barY; - PaintSingleBar(canvas, cx + X + barStartX, barStartY, cx + X + barEndX, barEndY, barSize); - } - // - // Bar to Next? - // - else if (i < h.Beats.Count - 1) - { - // full bar? - if (BeamingHelper.IsFullBarJoin(beat, h.Beats[i + 1], barIndex)) - { - barStartX = beatLineX; - barEndX = h.GetBeatLineX(h.Beats[i + 1]) + Scale; - - var endGlyph = GetOnNotesGlyphForBeat(h.Beats[i + 1]); - if (h.Direction == BeamDirection.Up) - { - barEndX -= endGlyph.Width / 2f; - } - else - { - barEndX += endGlyph.Width / 2f; - } - - } - // broken bar? - else if (i == 0 || !BeamingHelper.IsFullBarJoin(h.Beats[i - 1], beat, barIndex)) - { - barStartX = beatLineX; - barEndX = barStartX + brokenBarOffset; - } - else - { - continue; - } - barStartY = barY; - barEndY = barY; - PaintSingleBar(canvas, cx + X + barStartX, barStartY, cx + X + barEndX, barEndY, barSize); - } - // - // Broken Bar to Previous? - // - else if (i > 0 && !BeamingHelper.IsFullBarJoin(beat, h.Beats[i - 1], barIndex)) - { - barStartX = beatLineX - brokenBarOffset; - barEndX = beatLineX; - - barStartY = barY; - barEndY = barY; - - PaintSingleBar(canvas, cx + X + barStartX, barStartY, cx + X + barEndX, barEndY, barSize); - } - } - } - } - } - - private static void PaintSingleBar(ICanvas canvas, float x1, float y1, float x2, float y2, float size) - { - canvas.BeginPath(); - canvas.MoveTo(x1, y1); - canvas.LineTo(x2, y2); - canvas.LineTo(x2, y2 - size); - canvas.LineTo(x1, y1 - size); - canvas.ClosePath(); - canvas.Fill(); - } - - private void PaintFooter(float cx, float cy, ICanvas canvas, BeamingHelper h) - { - foreach (var beat in h.Beats) - { - if (beat.Duration == Duration.Whole || beat.Duration == Duration.DoubleWhole || beat.Duration == Duration.QuadrupleWhole) - { - return; - } - - - // - // draw line - // - - var beatLineX = h.GetBeatLineX(beat); - var y1 = cy + Y; - var y2 = cy + Y + Height; - - var startGlyph = (TabBeatGlyph) GetOnNotesGlyphForBeat(beat); - if (startGlyph.NoteNumbers == null) - { - y1 += Height - RhythmHeight; - } - else - { - y1 += startGlyph.NoteNumbers.GetNoteY(startGlyph.NoteNumbers.MinStringNote) + LineOffset/2; - } - - if (h.Direction == BeamDirection.Up) - { - beatLineX -= startGlyph.Width/2f; - } - else - { - beatLineX += startGlyph.Width/2f; - } - - canvas.BeginPath(); - canvas.MoveTo(cx + X + beatLineX, y1); - canvas.LineTo(cx + X + beatLineX, y2); - canvas.Stroke(); - - // - // Draw beam - // - if (beat.Duration > Duration.Quarter) - { - var glyph = new BeamGlyph(0, 0, beat.Duration, BeamDirection.Down, false); - glyph.Renderer = this; - glyph.DoLayout(); - glyph.Paint(cx + X + beatLineX, y2, canvas); - } - } - } - - - //private void DrawInfoGuide(ICanvas canvas, int cx, int cy, int y, Color c) - //{ - // canvas.Color = c; - // canvas.BeginPath(); - // canvas.MoveTo(cx + X, cy + Y + y); - // canvas.LineTo(cx + X + Width, cy + Y + y); - // canvas.Stroke(); - //} - } -} diff --git a/Source/AlphaTab/Rendering/TabBarRendererFactory.cs b/Source/AlphaTab/Rendering/TabBarRendererFactory.cs deleted file mode 100644 index 991c03338..000000000 --- a/Source/AlphaTab/Rendering/TabBarRendererFactory.cs +++ /dev/null @@ -1,59 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Model; - -namespace AlphaTab.Rendering -{ - /// - /// This Factory produces TabBarRenderer instances - /// - class TabBarRendererFactory : BarRendererFactory - { - private readonly bool _showTimeSignature; - private readonly bool _showRests; - private readonly bool _showTiedNotes; - public override string StaffId { get { return TabBarRenderer.StaffId; } } - - public TabBarRendererFactory(bool showTimeSignature, bool showRests, bool showTiedNotes) - { - _showTimeSignature = showTimeSignature; - _showRests = showRests; - _showTiedNotes = showTiedNotes; - HideOnPercussionTrack = true; - } - - public override bool CanCreate(Track track, Staff staff) - { - return staff.Tuning.Length > 0 && base.CanCreate(track, staff); - } - - public override BarRendererBase Create(ScoreRenderer renderer, Bar bar, StaveSettings staveSettings) - { - var tabBarRenderer = new TabBarRenderer(renderer, bar); - tabBarRenderer.ShowRests = _showRests; - tabBarRenderer.ShowTimeSignature = _showTimeSignature; - tabBarRenderer.ShowTiedNotes = _showTiedNotes; - tabBarRenderer.RenderRhythm = staveSettings.Get("rhythm", tabBarRenderer.RenderRhythm); - tabBarRenderer.RhythmHeight = staveSettings.Get("rhythmHeight", tabBarRenderer.RhythmHeight); - tabBarRenderer.RhythmBeams = staveSettings.Get("rhythmBeams", tabBarRenderer.RhythmBeams); - - return tabBarRenderer; - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Utils/AccidentalHelper.cs b/Source/AlphaTab/Rendering/Utils/AccidentalHelper.cs deleted file mode 100644 index 2e4bf4c29..000000000 --- a/Source/AlphaTab/Rendering/Utils/AccidentalHelper.cs +++ /dev/null @@ -1,304 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using AlphaTab.Collections; -using AlphaTab.Model; - -namespace AlphaTab.Rendering.Utils -{ - /// - /// This small utilty public class allows the assignment of accidentals within a - /// desired scope. - /// - class AccidentalHelper - { - private readonly Bar _bar; - - /// - /// a lookup list containing an info whether the notes within an octave - /// need an accidental rendered. the accidental symbol is determined based on the type of key signature. - /// - private static readonly bool[][] KeySignatureLookup = - { - // Flats (where the value is true, a flat accidental is required for the notes) - new[] { true, true, true, true, true, true, true, true, true, true, true, true}, - new[] { true, true, true, true, true, false, true, true, true, true, true, true}, - new[] { false, true, true, true, true, false, true, true, true, true, true, true}, - new[] { false, true, true, true, true, false, false, false, true, true, true, true}, - new[] { false, false, false, true, true, false, false, false, true, true, true, true}, - new[] { false, false, false, true, true, false, false, false, false, false, true, true}, - new[] { false, false, false, false, false, false, false, false, false, false, true, true}, - // natural - new[] { false, false, false, false, false, false, false, false, false, false, false, false }, - // sharps (where the value is true, a flat accidental is required for the notes) - new[] {false, false, false, false, false, true, true, false, false, false, false, false}, - new[] {true, true, false, false, false, true, true, false, false, false, false, false}, - new[] {true, true, false, false, false, true, true, true, true, false, false, false}, - new[] {true, true, true, true, false, true, true, true, true, false, false, false}, - new[] {true, true, true, true, false, true, true, true, true, true, true, false}, - new[] {true, true, true, true, true, true, true, true, true, true, true, false}, - new[] {true, true, true, true, true, true, true, true, true, true, true, true } - }; - - /// - /// Contains the list of notes within an octave have accidentals set. - /// - private static readonly bool[] AccidentalNotes = - { - false, true, false, true, false, false, true, false, true, false, true, false - }; - - /// - /// We always have 7 steps per octave. - /// (by a step the offsets inbetween score lines is meant, - /// 0 steps is on the first line (counting from top) - /// 1 steps is on the space inbetween the first and the second line - /// - private const int StepsPerOctave = 7; - - /// - /// Those are the amount of steps for the different clefs in case of a note value 0 - /// [Neutral, C3, C4, F4, G2] - /// - private static readonly int[] OctaveSteps = { 40, 34, 32, 28, 40 }; - - /// - /// The step offsets of the notes within an octave in case of for sharp keysignatures - /// - private static readonly int[] SharpNoteSteps = { 0, 0, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6 }; - - /// - /// The step offsets of the notes within an octave in case of for flat keysignatures - /// - private static readonly int[] FlatNoteSteps = { 0, 1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 6 }; - - private readonly FastDictionary _registeredAccidentals; - - private readonly FastDictionary _appliedScoreLines; - private readonly FastDictionary _appliedScoreLinesByValue; - private readonly FastDictionary _notesByValue; - - public Beat MaxNoteValueBeat { get; set; } - public Beat MinNoteValueBeat { get; set; } - public int MaxNoteValue { get; set; } - public int MinNoteValue { get; set; } - - public AccidentalHelper(Bar bar) - { - _bar = bar; - _registeredAccidentals = new FastDictionary(); - _appliedScoreLines = new FastDictionary(); - _appliedScoreLinesByValue = new FastDictionary(); - _notesByValue = new FastDictionary(); - - MaxNoteValue = -1; - MinNoteValue = -1; - } - - /// - /// Calculates the accidental for the given note and assignes the value to it. - /// The new accidental type is also registered within the current scope - /// - /// - /// - public AccidentalType ApplyAccidental(Note note) - { - var staff = _bar.Staff; - var noteValue = staff.StaffKind == StaffKind.Percussion - ? PercussionMapper.MapNoteForDisplay(note.DisplayValue) - : note.DisplayValue; - bool quarterBend = note.HasQuarterToneOffset; - var line = RegisterNoteLine(note, noteValue); - if (MinNoteValue == -1 || noteValue < MinNoteValue) - { - MinNoteValue = noteValue; - MinNoteValueBeat = note.Beat; - } - if (MaxNoteValue == -1 || noteValue > MaxNoteValue) - { - MaxNoteValue = noteValue; - MaxNoteValueBeat = note.Beat; - } - return GetAccidental(line, noteValue, quarterBend); - } - - /// - /// Calculates the accidental for the given note value and assignes the value to it. - /// The new accidental type is also registered within the current scope - /// - /// - /// - /// - /// - public AccidentalType ApplyAccidentalForValue(Beat relatedBeat, int noteValue, bool quarterBend) - { - var staff = _bar.Staff; - if (staff.StaffKind == StaffKind.Percussion) - { - noteValue = PercussionMapper.MapNoteForDisplay(noteValue); - } - var line = RegisterNoteValueLine(noteValue); - if (MinNoteValue == -1 || noteValue < MinNoteValue) - { - MinNoteValue = noteValue; - MinNoteValueBeat = relatedBeat; - } - if (MaxNoteValue == -1 || noteValue > MaxNoteValue) - { - MaxNoteValue = noteValue; - MaxNoteValueBeat = relatedBeat; - } - return GetAccidental(line, noteValue, quarterBend); - } - - private AccidentalType GetAccidental(int line, int noteValue, bool quarterBend) - { - var accidentalToSet = AccidentalType.None; - if (_bar.Staff.StaffKind != StaffKind.Percussion) - { - var ks = (int)_bar.MasterBar.KeySignature; - var ksi = (ks + 7); - var index = (noteValue % 12); - - // the key signature symbol required according to - var keySignatureAccidental = ksi < 7 ? AccidentalType.Flat : AccidentalType.Sharp; - - // determine whether the current note requires an accidental according to the key signature - var hasNoteAccidentalForKeySignature = KeySignatureLookup[ksi][index]; - var isAccidentalNote = AccidentalNotes[index]; - - if (quarterBend) - { - accidentalToSet = isAccidentalNote ? keySignatureAccidental : AccidentalType.Natural; - } - else - { - var isAccidentalRegistered = _registeredAccidentals.ContainsKey(line); - if (hasNoteAccidentalForKeySignature != isAccidentalNote && !isAccidentalRegistered) - { - _registeredAccidentals[line] = true; - accidentalToSet = isAccidentalNote ? keySignatureAccidental : AccidentalType.Natural; - } - else if (hasNoteAccidentalForKeySignature == isAccidentalNote && isAccidentalRegistered) - { - _registeredAccidentals.Remove(line); - accidentalToSet = isAccidentalNote ? keySignatureAccidental : AccidentalType.Natural; - } - } - } - - // TODO: change accidentalToSet according to note.AccidentalMode - - if (quarterBend) - { - switch (accidentalToSet) - { - case AccidentalType.Natural: - return AccidentalType.NaturalQuarterNoteUp; - case AccidentalType.Sharp: - return AccidentalType.SharpQuarterNoteUp; - case AccidentalType.Flat: - return AccidentalType.FlatQuarterNoteUp; - } - } - - return accidentalToSet; - } - - private int RegisterNoteLine(Note n, int noteValue) - { - var steps = CalculateNoteLine(noteValue, n.AccidentalMode); - _appliedScoreLines[n.Id] = steps; - _notesByValue[noteValue] = n; - return steps; - } - - private int RegisterNoteValueLine(int noteValue) - { - var steps = CalculateNoteLine(noteValue, NoteAccidentalMode.Default); - _appliedScoreLinesByValue[noteValue] = steps; - return steps; - } - - private int CalculateNoteLine(int noteValue, NoteAccidentalMode mode) - { - var value = noteValue; - var ks = (int)_bar.MasterBar.KeySignature; - var clef = _bar.Clef; - - var index = value % 12; - var octave = (value / 12) - 1; - - // Initial Position - var steps = OctaveSteps[(int)clef]; - - // Move to Octave - steps -= (octave * StepsPerOctave); - - // get the step list for the current keySignature - var stepList = ModelUtils.KeySignatureIsSharp(ks) || ModelUtils.KeySignatureIsNatural(ks) - ? SharpNoteSteps - : FlatNoteSteps; - - //Add offset for note itself - int offset = 0; - switch (mode) - { - // TODO: provide line according to accidentalMode - case NoteAccidentalMode.Default: - case NoteAccidentalMode.SwapAccidentals: - case NoteAccidentalMode.ForceNatural: - case NoteAccidentalMode.ForceFlat: - case NoteAccidentalMode.ForceSharp: - default: - // normal behavior: simply use the position where - // the keysignature defines the position - offset = stepList[index]; - break; - } - steps -= stepList[index]; - - return steps; - } - - - public int GetNoteLine(Note n) - { - return _appliedScoreLines[n.Id]; - } - - public int GetNoteLineForValue(int rawValue, bool searchForNote = false) - { - if (_appliedScoreLinesByValue.ContainsKey(rawValue)) - { - return _appliedScoreLinesByValue[rawValue]; - } - - if (searchForNote && _notesByValue.ContainsKey(rawValue)) - { - return GetNoteLine(_notesByValue[rawValue]); - } - else - { - return 0; - } - } - } - -} diff --git a/Source/AlphaTab/Rendering/Utils/BarHelpersGroup.cs b/Source/AlphaTab/Rendering/Utils/BarHelpersGroup.cs deleted file mode 100644 index bd8ac8b03..000000000 --- a/Source/AlphaTab/Rendering/Utils/BarHelpersGroup.cs +++ /dev/null @@ -1,123 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using AlphaTab.Collections; -using AlphaTab.Model; - -namespace AlphaTab.Rendering.Utils -{ - class BarHelpers - { - public FastList> BeamHelpers { get; set; } - public FastList> BeamHelperLookup { get; set; } - public FastList> TupletHelpers { get; set; } - - public BarHelpers(Bar bar) - { - BeamHelpers = new FastList>(); - BeamHelperLookup = new FastList>(); - TupletHelpers = new FastList>(); - - BeamingHelper currentBeamHelper = null; - TupletHelper currentTupletHelper = null; - - if (bar != null) - { - for (int i = 0, j = bar.Voices.Count; i < j; i++) - { - var v = bar.Voices[i]; - BeamHelpers.Add(new FastList()); - BeamHelperLookup.Add(new FastDictionary()); - TupletHelpers.Add(new FastList()); - - for (int k = 0, l = v.Beats.Count; k < l; k++) - { - var b = v.Beats[k]; - var forceNewTupletHelper = false; - - // if a new beaming helper was started, we close our tuplet grouping as well - if (!b.IsRest) - { - // try to fit beam to current beamhelper - if (currentBeamHelper == null || !currentBeamHelper.CheckBeat(b)) - { - if (currentBeamHelper != null) - { - currentBeamHelper.Finish(); - forceNewTupletHelper = currentBeamHelper.Beats.Count > 1; - } - else - { - forceNewTupletHelper = true; - } - // if not possible, create the next beaming helper - currentBeamHelper = new BeamingHelper(bar.Staff); - currentBeamHelper.CheckBeat(b); - BeamHelpers[v.Index].Add(currentBeamHelper); - } - } - - if (b.HasTuplet) - { - // try to fit tuplet to current tuplethelper - // TODO: register tuplet overflow - var previousBeat = b.PreviousBeat; - - // don't group if the previous beat isn't in the same voice - if (previousBeat != null && previousBeat.Voice != b.Voice) previousBeat = null; - - // if a new beaming helper was started, we close our tuplet grouping as well - if (forceNewTupletHelper && currentTupletHelper != null) - { - currentTupletHelper.Finish(); - } - - if (previousBeat == null || currentTupletHelper == null || !currentTupletHelper.Check(b)) - { - currentTupletHelper = new TupletHelper(v.Index); - currentTupletHelper.Check(b); - TupletHelpers[v.Index].Add(currentTupletHelper); - } - } - - BeamHelperLookup[v.Index][b.Index] = currentBeamHelper; - } - - if (currentBeamHelper != null) - { - currentBeamHelper.Finish(); - } - - if (currentTupletHelper != null) - { - currentTupletHelper.Finish(); - } - - currentBeamHelper = null; - currentTupletHelper = null; - } - } - } - - public BeamingHelper GetBeamingHelperForBeat(Beat beat) - { - return BeamHelperLookup[beat.Voice.Index][beat.Index]; - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Utils/BeamingHelper.cs b/Source/AlphaTab/Rendering/Utils/BeamingHelper.cs deleted file mode 100644 index 269da8928..000000000 --- a/Source/AlphaTab/Rendering/Utils/BeamingHelper.cs +++ /dev/null @@ -1,536 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Audio; -using AlphaTab.Collections; -using AlphaTab.Model; - -namespace AlphaTab.Rendering.Utils -{ - enum BeamDirection - { - Up, - Down - } - - /// - /// Lists all types how two voices can be joined with bars. - /// - enum BeamBarType - { - /// - /// Full Bar from current to next - /// - Full, - /// - /// A small Bar from current to previous - /// - PartLeft, - /// - /// A small bar from current to next - /// - PartRight - } - - interface IBeamYCalculator - { - float GetYPositionForNoteValue(int noteValue); - } - - class BeatLinePositions - { - public string StaffId { get; set; } - public float Up { get; set; } - public float Down { get; set; } - - public int MinNoteValue { get; set; } - public int MaxNoteValue { get; set; } - - public BeatLinePositions() - { - } - } - - /// - /// This public class helps drawing beams and bars for notes. - /// - class BeamingHelper - { - private static readonly int[] ScoreMiddleKeys = { 71, 60, 57, 50, 71 }; - - private readonly Staff _staff; - - /// - /// stores the X-positions for beat indices - /// - private readonly FastDictionary _beatLineXPositions; - - public Voice Voice { get; set; } - public FastList Beats { get; set; } - public Duration ShortestDuration { get; set; } - - /// - /// the number of fingering indicators that will be drawn - /// - public int FingeringCount { get; set; } - - /// - /// an indicator whether any beat has a tuplet on it. - /// - public bool HasTuplet { get; set; } - - /// - /// the first min note within this group - /// - public int FirstMinNoteValue { get; set; } - - /// - /// the first max note within this group - /// - public int FirstMaxNoteValue { get; set; } - - /// - /// the last min note within this group - /// - public int LastMinNoteValue { get; set; } - - /// - /// the last max note within this group - /// - public int LastMaxNoteValue { get; set; } - - /// - /// the overall min note value within this group. - /// This includes values caused by bends. - /// - public int MinNoteValue { get; set; } - - public Beat MinNoteBeat { get; set; } - - /// - /// the overall max note value within this group - /// This includes values caused by bends. - /// - public int MaxNoteValue { get; set; } - - public Beat MaxNoteBeat { get; set; } - - public bool InvertBeamDirection { get; set; } - - public bool IsGrace { get; set; } - - public BeamingHelper(Staff staff) - { - _staff = staff; - - Beats = new FastList(); - _beatLineXPositions = new FastDictionary(); - ShortestDuration = Duration.QuadrupleWhole; - MaxNoteValue = int.MinValue; - MinNoteValue = int.MinValue; - FirstMinNoteValue = int.MinValue; - FirstMaxNoteValue = int.MinValue; - LastMinNoteValue = int.MinValue; - LastMaxNoteValue = int.MinValue; - } - - private int GetValue(Note n) - { - if (_staff.StaffKind == StaffKind.Percussion) - { - return PercussionMapper.MapNoteForDisplay(n.DisplayValue); - } - else - { - return n.DisplayValue; - } - } - - private int GetMaxValue(Note n) - { - int value = GetValue(n); - if (n.HarmonicType != HarmonicType.None && n.HarmonicType != HarmonicType.Natural) - { - value = n.RealValue - _staff.DisplayTranspositionPitch; - } - - return value; - } - - private int GetMinValue(Note n) - { - int value = GetValue(n); - return value; - } - - - public float GetBeatLineX(Beat beat) - { - if (HasBeatLineX(beat)) - { - if (Direction == BeamDirection.Up) - { - return (int)_beatLineXPositions[beat.Index].Up; - } - return (int)_beatLineXPositions[beat.Index].Down; - } - return 0; - } - - public bool HasBeatLineX(Beat beat) - { - return _beatLineXPositions.ContainsKey(beat.Index); - } - - public void RegisterBeatLineX(string staffId, Beat beat, float up, float down) - { - var positions = GetOrCreateBeatPositions(beat); - positions.StaffId = staffId; - positions.Up = up; - positions.Down = down; - } - - private BeatLinePositions GetOrCreateBeatPositions(Beat beat) - { - return _beatLineXPositions.ContainsKey(beat.Index) - ? _beatLineXPositions[beat.Index] - : (_beatLineXPositions[beat.Index] = new BeatLinePositions()); - } - - public BeamDirection Direction { get; private set; } - - public void Finish() - { - Direction = CalculateDirection(); - } - - private BeamDirection CalculateDirection() - { - // multivoice handling - if (Voice.Index > 0) - { - return Invert(BeamDirection.Down); - } - if (Voice.Bar.Voices.Count > 1) - { - for (int v = 1; v < Voice.Bar.Voices.Count; v++) - { - if (!Voice.Bar.Voices[v].IsEmpty) - { - return Invert(BeamDirection.Up); - } - } - } - - if (Beats[0].GraceType != GraceType.None) - { - return Invert(BeamDirection.Up); - } - - // the average key is used for determination - // key lowerequal than middle line -> up - // key higher than middle line -> down - var avg = (MaxNoteValue + MinNoteValue) / 2; - return Invert(avg < ScoreMiddleKeys[(int)Beats[Beats.Count - 1].Voice.Bar.Clef] ? BeamDirection.Up : BeamDirection.Down); - } - - private BeamDirection Invert(BeamDirection direction) - { - if (!InvertBeamDirection) return direction; - switch (direction) - { - case BeamDirection.Down: - return BeamDirection.Up; - case BeamDirection.Up: - return BeamDirection.Down; - } - return BeamDirection.Up; - } - - - public bool CheckBeat(Beat beat) - { - if (beat.InvertBeamDirection) - { - InvertBeamDirection = true; - } - - if (Voice == null) - { - Voice = beat.Voice; - } - // allow adding if there are no beats yet - var add = false; - if (Beats.Count == 0) - { - add = true; - } - else if (CanJoin(Beats[Beats.Count - 1], beat)) - { - add = true; - } - - if (add) - { - Beats.Add(beat); - if (beat.GraceType != GraceType.None) - { - IsGrace = true; - } - - var positions = GetOrCreateBeatPositions(beat); - - if (beat.HasTuplet) - { - HasTuplet = true; - } - - int fingeringCount = 0; - for (var n = 0; n < beat.Notes.Count; n++) - { - var note = beat.Notes[n]; - if (note.LeftHandFinger != Fingers.Unknown || note.RightHandFinger != Fingers.Unknown) - { - fingeringCount++; - } - } - - if (fingeringCount > FingeringCount) - { - FingeringCount = fingeringCount; - } - - LastMinNoteValue = int.MinValue; - LastMaxNoteValue = int.MinValue; - - CheckNote(beat.MinNote); - CheckNote(beat.MaxNote); - - positions.MinNoteValue = LastMinNoteValue; - positions.MaxNoteValue = LastMaxNoteValue; - - if (ShortestDuration < beat.Duration) - { - ShortestDuration = beat.Duration; - } - - if (beat.HasTuplet) - { - HasTuplet = true; - } - } - - return add; - } - - private void CheckNote(Note note) - { - var value = GetValue(note); - - if (Beats.Count == 1 && Beats[0] == note.Beat) - { - if (FirstMinNoteValue == int.MinValue || value < FirstMinNoteValue) - { - FirstMinNoteValue = value; - } - if (FirstMaxNoteValue == int.MinValue || value > FirstMaxNoteValue) - { - FirstMaxNoteValue = value; - } - } - - if (LastMinNoteValue == int.MinValue || value < LastMinNoteValue) - { - LastMinNoteValue = value; - } - if (LastMaxNoteValue == int.MinValue || value > LastMaxNoteValue) - { - LastMaxNoteValue = value; - } - - var minValue = GetMinValue(note); - if (MinNoteValue == int.MinValue || MinNoteValue > minValue) - { - MinNoteValue = minValue; - MinNoteBeat = note.Beat; - } - - var maxValue = GetMaxValue(note); - if (MaxNoteValue == int.MinValue || MaxNoteValue < maxValue) - { - MaxNoteValue = maxValue; - MaxNoteBeat= note.Beat; - } - } - - public float CalculateBeamY(float stemSize, float xCorrection, float xPosition, float scale, IBeamYCalculator yPosition) - { - return CalculateBeamYWithDirection(stemSize, xCorrection, xPosition, scale, yPosition, Direction); - } - - public float CalculateBeamYWithDirection(float stemSize, float xCorrection, float xPosition, float scale, IBeamYCalculator yPosition, BeamDirection direction) - { - // create a line between the min and max note of the group - if (Beats.Count == 1) - { - if (direction == BeamDirection.Up) - { - return yPosition.GetYPositionForNoteValue(MaxNoteValue) - stemSize; - } - return yPosition.GetYPositionForNoteValue(MinNoteValue) + stemSize; - } - - // we use the min/max notes to place the beam along their real position - // we only want a maximum of 10 offset for their gradient - var maxDistance = (10 * scale); - - - // if the min note is not first or last, we can align notes directly to the position - // of the min note - if (direction == BeamDirection.Down && MinNoteBeat != Beats[0] && MinNoteBeat != Beats[Beats.Count - 1]) - { - return yPosition.GetYPositionForNoteValue(MinNoteValue) + stemSize; - } - if (direction == BeamDirection.Up && MaxNoteBeat != Beats[0] && MinNoteBeat != Beats[Beats.Count - 1]) - { - return yPosition.GetYPositionForNoteValue(MaxNoteValue) - stemSize; - } - - float startX = GetBeatLineX(Beats[0]) + xCorrection; - float startY = direction == BeamDirection.Up - ? yPosition.GetYPositionForNoteValue(FirstMaxNoteValue) - stemSize - : yPosition.GetYPositionForNoteValue(FirstMinNoteValue) + stemSize; - - float endX = GetBeatLineX(Beats[Beats.Count - 1]) + xCorrection; - float endY = direction == BeamDirection.Up - ? yPosition.GetYPositionForNoteValue(LastMaxNoteValue) - stemSize - : yPosition.GetYPositionForNoteValue(LastMinNoteValue) + stemSize; - - // ensure the maxDistance - if (direction == BeamDirection.Down && startY > endY && (startY - endY) > maxDistance) endY = (startY - maxDistance); - if (direction == BeamDirection.Down && endY > startY && (endY - startY) > maxDistance) startY = (endY - maxDistance); - - if (direction == BeamDirection.Up && startY < endY && (endY - startY) > maxDistance) endY = (startY + maxDistance); - if (direction == BeamDirection.Up && endY < startY && (startY - endY) > maxDistance) startY = (endY + maxDistance); - - // get the y position of the given beat on this curve - - if (startX == endX) return startY; - - // y(x) = ( (y2 - y1) / (x2 - x1) ) * (x - x1) + y1; - return ((endY - startY) / (endX - startX)) * (xPosition - startX) + startY; - } - - // TODO: Check if this beaming is really correct, I'm not sure if we are connecting beats correctly - private static bool CanJoin(Beat b1, Beat b2) - { - // is this a voice we can join with? - if (b1 == null || b2 == null || b1.IsRest || b2.IsRest || b1.GraceType != b2.GraceType || b1.GraceType == GraceType.BendGrace || b2.GraceType == GraceType.BendGrace) - { - return false; - } - - var m1 = b1.Voice.Bar; - var m2 = b1.Voice.Bar; - // only join on same measure - if (m1 != m2) return false; - - // get times of those voices and check if the times - // are in the same division - var start1 = b1.PlaybackStart; - var start2 = b2.PlaybackStart; - - // we can only join 8th, 16th, 32th and 64th voices - if (!CanJoinDuration(b1.Duration) || !CanJoinDuration(b2.Duration)) - { - return start1 == start2; - } - - // TODO: create more rules for automatic beaming - var divisionLength = MidiUtils.QuarterTime; - switch (m1.MasterBar.TimeSignatureDenominator) - { - case 8: - if (m1.MasterBar.TimeSignatureNumerator % 3 == 0) - { - divisionLength += MidiUtils.QuarterTime / 2; - } - break; - } - - // check if they are on the same division - var division1 = ((divisionLength + start1) / divisionLength) | 0; - var division2 = ((divisionLength + start2) / divisionLength) | 0; - - return division1 == division2; - } - - private static bool CanJoinDuration(Duration d) - { - switch (d) - { - case Duration.Whole: - case Duration.Half: - case Duration.Quarter: - return false; - default: - return true; - } - } - - public static bool IsFullBarJoin(Beat a, Beat b, int barIndex) - { - // TODO: this getindex call seems expensive since we call this method very often. - return (a.Duration.GetIndex() - 2 - barIndex > 0) - && (b.Duration.GetIndex() - 2 - barIndex > 0); - } - - /// - /// Returns whether the the position of the given beat, was registered by the staff of the given ID - /// - /// - /// - /// - public bool IsPositionFrom(string staffId, Beat beat) - { - if (!_beatLineXPositions.ContainsKey(beat.Index)) - { - return true; - } - return _beatLineXPositions[beat.Index].StaffId == staffId; - } - - - public int GetBeatMinValue(Beat beat) - { - if (!_beatLineXPositions.ContainsKey(beat.Index)) - { - return beat.MinNote.DisplayValue; - } - return _beatLineXPositions[beat.Index].MinNoteValue; - } - - public int GetBeatMaxValue(Beat beat) - { - if (!_beatLineXPositions.ContainsKey(beat.Index)) - { - return beat.MaxNote.DisplayValue; - } - return _beatLineXPositions[beat.Index].MaxNoteValue; - } - } -} diff --git a/Source/AlphaTab/Rendering/Utils/BoundsLookup.cs b/Source/AlphaTab/Rendering/Utils/BoundsLookup.cs deleted file mode 100644 index de11400c6..000000000 --- a/Source/AlphaTab/Rendering/Utils/BoundsLookup.cs +++ /dev/null @@ -1,375 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Collections; -using AlphaTab.Model; - -namespace AlphaTab.Rendering.Utils -{ - public class Bounds - { - public float X { get; set; } - public float Y { get; set; } - public float W { get; set; } - public float H { get; set; } - } - - public class StaveGroupBounds - { - public int Index { get; set; } - public Bounds VisualBounds { get; set; } - public Bounds RealBounds { get; set; } - public FastList Bars { get; set; } - public BoundsLookup BoundsLookup { get; set; } - - public StaveGroupBounds() - { - Bars = new FastList(); - Index = 0; - } - - public void Finish() - { - for (int i = 0; i < Bars.Count; i++) - { - Bars[i].Finish(); - } - } - - public void AddBar(MasterBarBounds bounds) - { - BoundsLookup.AddMasterBar(bounds); - bounds.StaveGroupBounds = this; - Bars.Add(bounds); - } - - public MasterBarBounds FindBarAtPos(float x) - { - MasterBarBounds b = null; - // move from left to right as long we find bars that start before the clicked position - for (int i = 0; i < Bars.Count; i++) - { - if (b == null || Bars[i].RealBounds.X < x) - { - b = Bars[i]; - } - else if (x > Bars[i].RealBounds.X + Bars[i].RealBounds.W) - { - break; - } - } - return b; - } - - } - - public class MasterBarBounds - { - public int Index { get; set; } - public bool IsFirstOfLine { get; set; } - public Bounds VisualBounds { get; set; } - public Bounds RealBounds { get; set; } - public Bounds LineAlignedBounds { get; set; } - public FastList Bars { get; set; } - public StaveGroupBounds StaveGroupBounds { get; set; } - - public MasterBarBounds() - { - Bars = new FastList(); - } - - public void AddBar(BarBounds bounds) - { - bounds.MasterBarBounds = this; - Bars.Add(bounds); - } - - public Beat FindBeatAtPos(float x, float y) - { - BeatBounds beat = null; - for (int i = 0; i < Bars.Count; i++) - { - var b = Bars[i].FindBeatAtPos(x); - if (b != null && (beat == null || beat.RealBounds.X < b.RealBounds.X)) - { - beat = b; - } - } - return beat == null ? null : beat.Beat; - } - - public void Finish() - { - Bars.Sort((a,b)=> - { - if (a.RealBounds.Y < b.RealBounds.Y) - { - return -1; - } - if (a.RealBounds.Y > b.RealBounds.Y) - { - return 1; - } - if (a.RealBounds.X < b.RealBounds.X) - { - return -1; - } - if (a.RealBounds.X > b.RealBounds.X) - { - return 1; - } - return 0; - }); - } - - public void AddBeat(BeatBounds bounds) - { - StaveGroupBounds.BoundsLookup.AddBeat(bounds); - } - } - - public class BarBounds - { - public MasterBarBounds MasterBarBounds { get; set; } - - public Bounds VisualBounds { get; set; } - public Bounds RealBounds { get; set; } - public Bar Bar { get; set; } - - public FastList Beats { get; set; } - - public BarBounds() - { - Beats = new FastList(); - } - - public void AddBeat(BeatBounds bounds) - { - bounds.BarBounds = this; - Beats.Add(bounds); - MasterBarBounds.AddBeat(bounds); - } - - public BeatBounds FindBeatAtPos(float x) - { - BeatBounds beat = null; - for (int i = 0; i < Beats.Count; i++) - { - if (beat == null || Beats[i].RealBounds.X < x) - { - beat = Beats[i]; - } - else if (Beats[i].RealBounds.X > x) - { - break; - } - } - return beat; - } - } - - public class BeatBounds - { - public BarBounds BarBounds { get; set; } - - public Bounds VisualBounds { get; set; } - public Bounds RealBounds { get; set; } - - public Beat Beat { get; set; } - - public FastList Notes { get; set; } - - public void AddNote(NoteBounds bounds) - { - if(Notes == null) Notes = new FastList(); - Notes.Add(bounds); - } - - public Note FindNoteAtPos(float x, float y) - { - if (Notes == null) return null; - // TODO: can be likely optimized - // a beat is mostly vertically aligned, we could sort the note bounds by Y - // and then do a binary search on the Y-axis. - foreach (var note in Notes) - { - var bottom = note.NoteHeadBounds.Y + note.NoteHeadBounds.H; - var right = note.NoteHeadBounds.X + note.NoteHeadBounds.W; - if (note.NoteHeadBounds.X >= x && note.NoteHeadBounds.Y >= y && x <= right && y <= bottom) - { - return note.Note; - } - } - return null; - } - } - - public class NoteBounds - { - public Bounds NoteHeadBounds { get; set; } - - public Note Note { get; set; } - } - - public partial class BoundsLookup - { - private FastDictionary _beatLookup; - private FastDictionary _masterBarLookup; - private StaveGroupBounds _currentStaveGroup; - public FastList StaveGroups { get; set; } - public bool IsFinished { get; private set; } - - public BoundsLookup() - { - StaveGroups = new FastList(); - _beatLookup = new FastDictionary(); - _masterBarLookup = new FastDictionary(); - } - - public void Finish() - { - for (int i = 0; i < StaveGroups.Count; i++) - { - StaveGroups[i].Finish(); - } - IsFinished = true; - } - - public void AddNote(NoteBounds bounds) - { - var beat = FindBeat(bounds.Note.Beat); - beat.AddNote(bounds); - } - - public void AddStaveGroup(StaveGroupBounds bounds) - { - bounds.Index = StaveGroups.Count; - bounds.BoundsLookup = this; - StaveGroups.Add(bounds); - _currentStaveGroup = bounds; - } - - public void AddMasterBar(MasterBarBounds bounds) - { - if (bounds.StaveGroupBounds == null) - { - bounds.StaveGroupBounds = _currentStaveGroup; - _masterBarLookup[bounds.Index] = bounds; - _currentStaveGroup.AddBar(bounds); - } - else - { - _masterBarLookup[bounds.Index] = bounds; - } - } - - public void AddBeat(BeatBounds bounds) - { - _beatLookup[bounds.Beat.Id] = bounds; - } - - public MasterBarBounds FindMasterBarByIndex(int index) - { - if (_masterBarLookup.ContainsKey(index)) - { - return _masterBarLookup[index]; - } - return null; - } - - public MasterBarBounds FindMasterBar(MasterBar bar) - { - var id = bar.Index; - if (_masterBarLookup.ContainsKey(id)) - { - return _masterBarLookup[id]; - } - return null; - } - - public BeatBounds FindBeat(Beat beat) - { - var id = beat.Id; - if (_beatLookup.ContainsKey(id)) - { - return _beatLookup[id]; - } - return null; - } - - public Beat GetBeatAtPos(float x, float y) - { - // - // find a bar which matches in y-axis - var bottom = 0; - var top = StaveGroups.Count - 1; - - var staveGroupIndex = -1; - while (bottom <= top) - { - var middle = (top + bottom) / 2; - var group = StaveGroups[middle]; - - // found? - if (y >= group.RealBounds.Y && y <= (group.RealBounds.Y + group.RealBounds.H)) - { - staveGroupIndex = middle; - break; - } - // search in lower half - if (y < group.RealBounds.Y) - { - top = middle - 1; - } - // search in upper half - else - { - bottom = middle + 1; - } - } - - // no bar found - if (staveGroupIndex == -1) return null; - - // - // Find the matching bar in the row - var staveGroup = StaveGroups[staveGroupIndex]; - - var bar = staveGroup.FindBarAtPos(x); - - if (bar != null) - { - return bar.FindBeatAtPos(x, y); - } - - return null; - } - - public Note GetNoteAtPos(Beat beat, float x, float y) - { - var beatBounds = FindBeat(beat); - if (beatBounds == null) return null; - - x -= beatBounds.BarBounds.MasterBarBounds.StaveGroupBounds.RealBounds.X; - y -= beatBounds.BarBounds.MasterBarBounds.StaveGroupBounds.RealBounds.Y; - - return beatBounds.FindNoteAtPos(x, y); - } - - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Rendering/Utils/PercussionMapper.cs b/Source/AlphaTab/Rendering/Utils/PercussionMapper.cs deleted file mode 100644 index 9437dcb73..000000000 --- a/Source/AlphaTab/Rendering/Utils/PercussionMapper.cs +++ /dev/null @@ -1,101 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using AlphaTab.Model; - -namespace AlphaTab.Rendering.Utils -{ - class PercussionMapper - { - private static readonly int[][] ElementVariationToMidi = - { - new []{35, 35, 35}, // [ 0] - [0] Kick (hit) - new []{38, 38, 37}, // [ 1] - [0] Snare (hit), [1] Snare (rim shot), [2] Snare (side stick) - new []{56, 56, 56}, // [ 2] - [0] Cowbell low (hit) - new []{56, 56, 56}, // [ 3] - [0] Cowbell medium (hit) - new []{56, 56, 56}, // [ 4] - [0] Cowbell high (hit) - new []{41, 41, 41}, // [ 5] - [0] Tom very low (hit) - new []{43, 43, 43}, // [ 6] - [0] Tom low (hit) - new []{45, 45, 45}, // [ 7] - [0] Tom medium (hit) - new []{47, 47, 47}, // [ 8] - [0] Tom high (hit) - new []{48, 48, 48}, // [ 9] - [0] Tom very high (hit) - new []{42, 46, 46}, // [10] - [0] Hihat (closed), [1] Hihat (half), [2] Hihat (open) - new []{44, 44, 44}, // [11] - [0] Pedal hihat - new []{49, 49, 49}, // [12] - [0] Crash medium (hit) - new []{57, 57, 57}, // [13] - [0] Crash high (hit) - new []{55, 55, 55}, // [14] - [0] Splash (hit) - new []{51, 59, 53}, // [15] - [0] Ride (middle), [1] Ride (edge), [2] Ride (bell) - new []{52, 52, 52} // [16] - [0] China (hit) - }; - - public static int MidiFromElementVariation(Note note) - { - return ElementVariationToMidi[note.Element][note.Variation]; - } - - /// - /// Maps the given note to a normal note value to place the note at the - /// correct line on score notation - /// - /// - /// - public static int MapNoteForDisplay(int value) - { - if (value == 61 || value == 66 || value == 44) - { - return 62; - } - else if (value == 60 || value == 65) - { - return 64; - } - else if ((value >= 35 && value <= 36)) - { - return 65; - } - else if (value == 41 || value == 64) - { - return 67; - } - else if (value == 43 || value == 62) - { - return 69; - } - else if (value == 45 || value == 63) - { - return 71; - } - else if (value == 47 || value == 54) - { - return 74; - } - else if (value == 48 || value == 56) - { - return 76; - } - else if (value == 50) - { - return 77; - } - else if (value == 42 || value == 46 || (value >= 49 && value <= 53) || value == 57 || value == 59) - { - return 79; - } - return 72; - } - } -} diff --git a/Source/AlphaTab/Rendering/Utils/TupletHelper.cs b/Source/AlphaTab/Rendering/Utils/TupletHelper.cs deleted file mode 100644 index 5dca9b269..000000000 --- a/Source/AlphaTab/Rendering/Utils/TupletHelper.cs +++ /dev/null @@ -1,65 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Collections; -using AlphaTab.Model; - -namespace AlphaTab.Rendering.Utils -{ - class TupletHelper - { - private bool _isFinished; - - public FastList Beats { get; set; } - public int VoiceIndex { get; set; } - public int Tuplet { get; set; } - - public TupletHelper(int voice) - { - VoiceIndex = voice; - Beats = new FastList(); - } - - public bool IsFull - { - get - { - return Beats.Count == Tuplet; - } - } - - public void Finish() - { - _isFinished = true; - } - - public bool Check(Beat beat) - { - if (Beats.Count == 0) - { - Tuplet = beat.TupletNumerator; - } - else if (beat.Voice.Index != VoiceIndex || beat.TupletNumerator != Tuplet || IsFull || _isFinished) - { - return false; - } - Beats.Add(beat); - return true; - } - } -} diff --git a/Source/AlphaTab/Settings.cs b/Source/AlphaTab/Settings.cs deleted file mode 100644 index 9f87f257f..000000000 --- a/Source/AlphaTab/Settings.cs +++ /dev/null @@ -1,443 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Collections; -using AlphaTab.Rendering; -using AlphaTab.Rendering.Utils; -using AlphaTab.Util; - -namespace AlphaTab -{ - /// - /// Lists all modes on how alphaTab can handle the display and playback of music notation. - /// - public enum DisplayMode - { - /// - /// Music elements will be displayed and played as in Guitar Pro. - /// - GuitarPro, - /// - /// Music elements will be displayed and played as in traditional songbooks. - /// Changes: - /// 1. Bends - /// For bends additional grace beats are introduced. - /// Bends are categorized into gradual and fast bends. - /// - Gradual bends are indicated by beat text "grad" or "grad.". Bend will sound along the beat duration. - /// - Fast bends are done right before the next note. If the next note is tied even on-beat of the next note. - /// 2. Whammy Bars - /// Dips are shown as simple annotation over the beats - /// Whammy Bars are categorized into gradual and fast. - /// - Gradual whammys are indicated by beat text "grad" or "grad.". Whammys will sound along the beat duration. - /// - Fast whammys are done right the beat. - /// 3. Let Ring - /// Tied notes with let ring are not shown in standard notation - /// Let ring does not cause a longer playback, duration is defined via tied notes. - /// - SongBook - } - - /// - /// Lists all modes on how fingerings should be displayed. - /// - public enum FingeringMode - { - /// - /// Fingerings will be shown in the standard notation staff. - /// - Score, - /// - /// Fingerings will be shown in a effect band above the tabs in case - /// they have only a single note on the beat. - /// - SingleNoteEffectBand - } - - /// - /// This public class contains instance specific settings for alphaTab - /// - public partial class Settings - { - /// - /// Sets the zoom level of the rendered notation - /// - public float Scale { get; set; } - - /// - /// The initial size of the canvas during loading or the width for particular layouts (e.g. page layout). - /// - public int Width { get; set; } - - /// - /// The engine which should be used to render the the tablature. - ///
      - ///
    • default - Platform specific default engine
    • - ///
    • html5 - HTML5 Canvas
    • - ///
    • svg - SVG
    • - ///
    - ///
    - public string Engine { get; set; } - - /// - /// The layout specific settings - /// - public LayoutSettings Layout { get; set; } - - /// - /// Specific settings for importers. Keys are specific for the importers. - /// General - ///
      - ///
    • encoding - The text encoding to use when decoding strings (string, default:utf-8)
    • - ///
    - /// MusicXML - ///
      - ///
    • musicxmlMergePartGroups - If part-groups should be merged into a single track (boolean, default:false)
    • - ///
    - ///
    - public FastDictionary ImporterSettings { get; set; } - - /// - /// The default stretch force to use for layouting. - /// - public float StretchForce { get; set; } - - /// - /// Forces the fingering rendering to use always the piano finger stýle where - /// fingers are rendered as 1-5 instead of p,i,m,a,c and T,1,2,3,4. - /// - public bool ForcePianoFingering { get; set; } - - /// - /// The staves that should be shown in the music sheet. - /// This is one of the profiles registered in the - /// - public StaveSettings Staves { get; set; } - - /// - /// The transposition pitch offsets for the individual tracks. - /// They apply to rendering and playback. - /// - public int[] TranspositionPitches { get; set; } - - /// - /// The transposition pitch offsets for the individual tracks. - /// They apply to rendering only. - /// - public int[] DisplayTranspositionPitches { get; set; } - - /// - /// The log level to use within alphaTab - /// - public LogLevel LogLevel { get; set; } - - /// - /// If set to true the guitar tabs on grace beats are rendered smaller. - /// - public bool SmallGraceTabNotes { get; set; } - - /// - /// If set to true bend arrows expand to the end of the last tied note - /// of the string. Otherwise they end on the next beat. - /// - public bool ExtendBendArrowsOnTiedNotes { get; set; } - - /// - /// If set to true the note heads on tied notes - /// will have parenthesis if they are preceeded by bends. - /// - public bool ShowParenthesisForTiedBends { get; set; } - - /// - /// If set to true a tab number will be shown in case - /// a bend is increased on a tied note. - /// - public bool ShowTabNoteOnTiedBend { get; set; } - - /// - /// Gets or sets the mode to use for display and play music notation elements. - /// - public DisplayMode DisplayMode { get; set; } - - /// - /// Gets or sets the fingering mode to use. - /// - public FingeringMode FingeringMode { get; set; } - - /// - /// If set to true, 0 is shown on dive whammy bars. - /// - public bool ShowZeroOnDiveWhammy { get; set; } - - /// - /// If set to true, line effects (like w/bar, let-ring etc) - /// are drawn until the end of the beat instead of the start. - /// - public bool ExtendLineEffectsToBeatEnd { get; set; } - - /// - /// Gets or sets the settings on how the vibrato audio is generated. - /// - public VibratoPlaybackSettings Vibrato { get; set; } - - /// - /// Gets or sets the height factor for slurs. The factor is multiplied with the distance - /// between slur start and end. - /// - public float SlurHeightFactor { get; set; } - - /// - /// Gets or sets the bend duration in milliseconds for songbook bends. - /// - public int SongBookBendDuration { get; set; } - - /// - /// Gets or sets the duration of whammy dips in milliseconds for songbook whammys. - /// - public int SongBookDipDuration { get; set; } - - /// - /// Gets or sets whether in the also the - /// position and area of each individual note is provided. - /// - public bool IncludeNoteBounds { get; set; } - - /// - /// Gets the default settings for the songbook display mode. - /// - public static Settings SongBook - { - get - { - var settings = Defaults; - settings.DisplayMode = DisplayMode.SongBook; - settings.ApplySongBookDefaults(); - return settings; - } - } - - private void ApplySongBookDefaults() - { - SmallGraceTabNotes = false; - FingeringMode = FingeringMode.SingleNoteEffectBand; - ExtendBendArrowsOnTiedNotes = false; - ShowParenthesisForTiedBends = false; - ShowTabNoteOnTiedBend = false; - ShowZeroOnDiveWhammy = true; - } - - /// - /// Gets the default settings. - /// - public static Settings Defaults - { - get - { - var settings = new Settings(); - - settings.Scale = 1.0f; - settings.StretchForce = 1; - settings.Width = -1; - settings.Engine = "default"; - settings.TranspositionPitches = new int[0]; - settings.DisplayTranspositionPitches = new int[0]; - settings.SmallGraceTabNotes = true; - settings.ExtendBendArrowsOnTiedNotes = true; - settings.ShowParenthesisForTiedBends = true; - settings.ShowTabNoteOnTiedBend = true; - settings.DisplayMode = DisplayMode.GuitarPro; - settings.FingeringMode = FingeringMode.Score; - settings.ShowZeroOnDiveWhammy = false; - settings.ExtendLineEffectsToBeatEnd = false; - settings.SlurHeightFactor = 0.3f; - - settings.ImporterSettings = new FastDictionary(); - - settings.Layout = LayoutSettings.Defaults; - - settings.Staves = new StaveSettings("default"); - settings.LogLevel = LogLevel.Info; - - settings.Vibrato = new VibratoPlaybackSettings(); - settings.Vibrato.NoteSlightAmplitude = 2; - settings.Vibrato.NoteWideAmplitude = 2; - settings.Vibrato.NoteSlightLength = 480; - settings.Vibrato.NoteWideLength = 480; - - settings.Vibrato.BeatSlightAmplitude = 3; - settings.Vibrato.BeatWideAmplitude = 3; - settings.Vibrato.BeatSlightLength = 240; - settings.Vibrato.BeatWideLength = 240; - - settings.SongBookBendDuration = 75; - settings.SongBookDipDuration = 150; - settings.IncludeNoteBounds = false; - - SetDefaults(settings); - - return settings; - } - } - } - - /// - /// This object defines the details on how to generate the vibrato effects. - /// - public class VibratoPlaybackSettings - { - /// - /// Gets or sets the wavelength of the note-wide vibrato in midi ticks. - /// - public int NoteWideLength { get; set; } - /// - /// Gets or sets the amplitude for the note-wide vibrato in semitones. - /// - public int NoteWideAmplitude { get; set; } - - /// - /// Gets or sets the wavelength of the note-slight vibrato in midi ticks. - /// - public int NoteSlightLength { get; set; } - /// - /// Gets or sets the amplitude for the note-slight vibrato in semitones. - /// - public int NoteSlightAmplitude { get; set; } - - /// - /// Gets or sets the wavelength of the beat-wide vibrato in midi ticks. - /// - public int BeatWideLength { get; set; } - /// - /// Gets or sets the amplitude for the beat-wide vibrato in semitones. - /// - public int BeatWideAmplitude { get; set; } - - /// - /// Gets or sets the wavelength of the beat-slight vibrato in midi ticks. - /// - public int BeatSlightLength { get; set; } - /// - /// Gets or sets the amplitude for the beat-slight vibrato in semitones. - /// - public int BeatSlightAmplitude { get; set; } - } - - /// - /// Represents the layout specific settings. - /// - public class LayoutSettings - { - /// - /// The layouting mode used to arrange the the notation. - ///
      - ///
    • page - Bars are aligned in rows using a fixed width
    • - ///
    • horizontal - Bars are aligned horizontally in one row
    • - ///
    - ///
    - public string Mode { get; set; } - - /// - /// Additional layout mode specific settings. - /// mode=page - ///
      - ///
    • barsPerRow - Limit the displayed bars per row, -1 for sized based limit (integer, default:-1)
    • - ///
    • start - The bar start index to start layouting with (integer: default: 0)
    • - ///
    • count - The amount of bars to render overall, -1 for all till the end (integer, default:-1)
    • - ///
    • hideInfo - Render the song information or not (boolean, default:false)
    • - ///
    • hideTuning - Render the tuning information or not (boolean, default:false)
    • - ///
    • hideTrackNames - Render the track names or not (boolean, default:false)
    • - ///
    - /// mode=horizontal - ///
      - ///
    • start - The bar start index to start layouting with (integer: default: 0)
    • - ///
    • count - The amount of bars to render overall, -1 for all till the end (integer, default:-1)
    • - ///
    • hideTrackNames - Render the track names or not (boolean, default:false)
    • - ///
    - ///
    - public FastDictionary AdditionalSettings { get; set; } - - internal T Get(string key, T def) - { - if (AdditionalSettings.ContainsKey(key.ToLower())) - { - return (T)(AdditionalSettings[key.ToLower()]); - } - return def; - } - - /// - /// Initializes a new instance of the class. - /// - public LayoutSettings() - { - AdditionalSettings = new FastDictionary(); - } - - internal static LayoutSettings Defaults - { - get - { - var settings = new LayoutSettings(); - settings.Mode = "page"; - return settings; - } - } - } - - /// - /// Represents the stave specific settings. - /// - public class StaveSettings - { - /// - /// The stave profile name as it is registered in - /// Default Profiles: - ///
      - ///
    • score-tab - Standard music notation and guitar tablature are rendered (default)
    • - ///
    • score - Only standard music notation is rendered
    • - ///
    • tab - Only guitar tablature is rendered
    • - ///
    - ///
    - public string Id { get; set; } - - /// - /// Additional stave sspecific settings - /// id=tab - ///
      - ///
    • rhythm - Renders rhythm beams to tablature notes
    • - ///
    - ///
    - public FastDictionary AdditionalSettings { get; set; } - - public StaveSettings(string id) - { - Id = id; - AdditionalSettings = new FastDictionary(); - } - - public T Get(string key, T def) - { - if (AdditionalSettings.ContainsKey(key.ToLower())) - { - return (T)(AdditionalSettings[key.ToLower()]); - } - return def; - } - - - } -} diff --git a/Source/AlphaTab/Util/Lazy.cs b/Source/AlphaTab/Util/Lazy.cs deleted file mode 100644 index 60b608d9c..000000000 --- a/Source/AlphaTab/Util/Lazy.cs +++ /dev/null @@ -1,46 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -using System; - -namespace AlphaTab.Util -{ - class Lazy - { - private readonly Func _factory; - private bool _created; - private T _value; - - public Lazy(Func factory) - { - _factory = factory; - } - - public T Value - { - get - { - if (!_created) - { - _value = _factory(); - _created = true; - } - return _value; - } - } - } -} diff --git a/Source/AlphaTab/Util/Logger.cs b/Source/AlphaTab/Util/Logger.cs deleted file mode 100644 index 988b91dc2..000000000 --- a/Source/AlphaTab/Util/Logger.cs +++ /dev/null @@ -1,65 +0,0 @@ -namespace AlphaTab.Util -{ - class Logger - { - public static LogLevel LogLevel { get; set; } - - static Logger() - { - LogLevel = LogLevel.Info; - } - - public static void Debug(string category, string msg, object details = null) - { - Log(LogLevel.Debug, category, msg, details); - } - - public static void Warning(string category, string msg, object details = null) - { - Log(LogLevel.Warning, category, msg, details); - } - - public static void Info(string category, string msg, object details = null) - { - Log(LogLevel.Info, category, msg, details); - } - - public static void Error(string category, string msg, object details = null) - { - Log(LogLevel.Error, category, msg, details); - } - - public static void Log(LogLevel logLevel, string category, string msg, object details = null) - { - if (logLevel < LogLevel) return; - Platform.Platform.Log(logLevel, category, msg, details); - } - } - - /// - /// Defines all loglevels. - /// - public enum LogLevel - { - /// - /// No logging - /// - None = 0, - /// - /// Debug level (internal details are displayed). - /// - Debug = 1, - /// - /// Info level (only important details are shown) - /// - Info = 2, - /// - /// Warning level - /// - Warning = 3, - /// - /// Error level. - /// - Error = 4 - } -} diff --git a/Source/AlphaTab/Xml/XmlDocument.cs b/Source/AlphaTab/Xml/XmlDocument.cs deleted file mode 100644 index 5cdb2bcf2..000000000 --- a/Source/AlphaTab/Xml/XmlDocument.cs +++ /dev/null @@ -1,39 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -namespace AlphaTab.Xml -{ - class XmlDocument : XmlNode - { - public XmlNode DocumentElement { get; private set; } - - public XmlDocument(string xml) - { - NodeType = XmlNodeType.Document; - XmlParser.Parse(xml, 0, this); - foreach (var child in ChildNodes) - { - if (child.NodeType == XmlNodeType.Element) - { - DocumentElement = child; - break; - } - } - } - } -} diff --git a/Source/AlphaTab/Xml/XmlException.cs b/Source/AlphaTab/Xml/XmlException.cs deleted file mode 100644 index 36921a805..000000000 --- a/Source/AlphaTab/Xml/XmlException.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; - -namespace AlphaTab.Xml -{ - class XmlException : AlphaTabException - { - public string Xml { get; private set; } - public int Pos { get; private set; } - - public XmlException(string message, string xml, int pos) : base(message) - { - Xml = xml; - Pos = pos; - } - } -} diff --git a/Source/AlphaTab/Xml/XmlNode.cs b/Source/AlphaTab/Xml/XmlNode.cs deleted file mode 100644 index 6ba187454..000000000 --- a/Source/AlphaTab/Xml/XmlNode.cs +++ /dev/null @@ -1,112 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; -using AlphaTab.Collections; - -namespace AlphaTab.Xml -{ - class XmlNode - { - public XmlNodeType NodeType { get; set; } - public string LocalName { get; set; } - public string Value { get; set; } - public FastList ChildNodes { get; private set; } - public FastDictionary Attributes { get; private set; } - public XmlNode FirstChild { get; set; } - public XmlNode FirstElement { get; set; } - - public XmlNode() - { - Attributes = new FastDictionary(); - ChildNodes = new FastList(); - } - - public void AddChild(XmlNode node) - { - ChildNodes.Add(node); - FirstChild = node; - if (node.NodeType == XmlNodeType.Element) - { - FirstElement = node; - } - } - - public string GetAttribute(string name) - { - if (Attributes.ContainsKey(name)) - { - return Attributes[name]; - } - return ""; - } - - public XmlNode[] GetElementsByTagName(string name, bool recursive = false) - { - var tags = new FastList(); - SearchElementsByTagName(ChildNodes, tags, name, recursive); - return tags.ToArray(); - } - - private void SearchElementsByTagName(FastList all, FastList result, string name, bool recursive = false) - { - foreach (var c in all) - { - if (c != null && c.NodeType == XmlNodeType.Element && c.LocalName == name) - { - result.Add(c); - } - - if (recursive) - { - SearchElementsByTagName(c.ChildNodes, result, name, true); - } - } - } - - public XmlNode FindChildElement(string name) - { - foreach (var c in ChildNodes) - { - if (c != null && c.NodeType == XmlNodeType.Element && c.LocalName == name) - { - return c; - } - } - return null; - } - - public string InnerText - { - get - { - if (NodeType == XmlNodeType.Element || NodeType == XmlNodeType.Document) - { - var txt = new StringBuilder(); - foreach (var c in ChildNodes) - { - txt.Append(c.InnerText); - } - string s = txt.ToString(); - return s.Trim(); - } - return Value; - } - } - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Xml/XmlNodeType.cs b/Source/AlphaTab/Xml/XmlNodeType.cs deleted file mode 100644 index ac9e13ade..000000000 --- a/Source/AlphaTab/Xml/XmlNodeType.cs +++ /dev/null @@ -1,41 +0,0 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Xml -{ - enum XmlNodeType - { - None, - Element, - Attribute, - Text, - CDATA, - EntityReference, - Entity, - ProcessingInstruction, - Comment, - Document, - DocumentType, - DocumentFragment, - Notation, - Whitespace, - SignificantWhitespace, - EndElement, - EndEntity, - XmlDeclaration - } -} \ No newline at end of file diff --git a/Source/AlphaTab/Xml/XmlParser.cs b/Source/AlphaTab/Xml/XmlParser.cs deleted file mode 100644 index a9e19becf..000000000 --- a/Source/AlphaTab/Xml/XmlParser.cs +++ /dev/null @@ -1,541 +0,0 @@ -// This XML parser is based on the XML Parser of the Haxe Standard Library (MIT) -/* - * Copyright (C)2005-2017 Haxe Foundation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ -using AlphaTab.Collections; -using AlphaTab.Platform; - -namespace AlphaTab.Xml -{ - class XmlParser - { - public const int CharCodeLF = '\n'; - public const int CharCodeTab = '\t'; - public const int CharCodeCR = '\r'; - public const int CharCodeSpace = ' '; - public const int CharCodeLowerThan = '<'; - public const int CharCodeAmp = '&'; - public const int CharCodeBrackedClose = ']'; - public const int CharCodeBrackedOpen = '['; - public const int CharCodeGreaterThan = '>'; - public const int CharCodeExclamation = '!'; - public const int CharCodeUpperD = 'D'; - public const int CharCodeLowerD = 'd'; - public const int CharCodeMinus = '-'; - public const int CharCodeQuestion = '?'; - public const int CharCodeSlash = '/'; - public const int CharCodeEquals = '='; - public const int CharCodeDoubleQuote = '"'; - public const int CharCodeSingleQuote = '\''; - public const int CharCodeSharp = '#'; - public const int CharCodeLowerX = 'x'; - public const int CharCodeLowerA = 'a'; - public const int CharCodeLowerZ = 'z'; - public const int CharCodeUpperA = 'A'; - public const int CharCodeUpperZ = 'Z'; - public const int CharCode0 = '0'; - public const int CharCode9 = '9'; - public const int CharCodeColon = ':'; - public const int CharCodeDot = '.'; - public const int CharCodeUnderscore = '_'; - - private static readonly FastDictionary Escapes; - - static XmlParser() - { - Escapes = new FastDictionary(); - Escapes["lt"] = "<"; - Escapes["gt"] = ">"; - Escapes["amp"] = "&"; - Escapes["quot"] = "\""; - Escapes["apos"] = "'"; - } - - public static int Parse(string str, int p, XmlNode parent) - { - int c = str[p]; - var state = XmlState.BEGIN; - var next = XmlState.BEGIN; - var start = 0; - var buf = new StringBuilder(); - var escapeNext = XmlState.BEGIN; - XmlNode xml = null; - string aname = null; - - int nbrackets = 0; - - int attrValQuote = 0; - - while (p < str.Length) - { - c = str[p]; - switch (state) - { - case XmlState.IGNORE_SPACES: - // IGNORE_SPACES - switch (c) - { - case CharCodeLF: - case CharCodeCR: - case CharCodeTab: - case CharCodeSpace: - break; - default: - state = next; - continue; - } - break; - - case XmlState.BEGIN: - // BEGIN - switch (c) - { - case CharCodeLowerThan: - // < - state = XmlState.IGNORE_SPACES; - next = XmlState.BEGIN_NODE; - break; - default: - start = p; - state = XmlState.PCDATA; - continue; - } - break; - - case XmlState.PCDATA: - // PCDATA - if (c == CharCodeLowerThan) - { - // < - buf.Append(str.Substring(start, p - start)); - var child = new XmlNode(); - child.NodeType = XmlNodeType.Text; - child.Value = buf.ToString(); - buf = new StringBuilder(); - parent.AddChild(child); - state = XmlState.IGNORE_SPACES; - next = XmlState.BEGIN_NODE; - } - else if (c == CharCodeAmp) - { - // & - buf.Append(str.Substring(start, p - start)); - state = XmlState.ESCAPE; - escapeNext = XmlState.PCDATA; - start = p + 1; - } - break; - - case XmlState.CDATA: - // CDATA - if (c == CharCodeBrackedClose && str[p + 1] == CharCodeBrackedClose && str[p + 2] == CharCodeGreaterThan) - { - // ]]> - var child = new XmlNode(); - child.NodeType = XmlNodeType.CDATA; - child.Value = str.Substring(start, p - start); - parent.AddChild(child); - p += 2; - state = XmlState.BEGIN; - } - break; - - case XmlState.BEGIN_NODE: - // BEGIN_NODE - switch (c) - { - case CharCodeExclamation: - // ! - if (str[p + 1] == CharCodeBrackedOpen) - { - // ] - p += 2; - if (str.Substring(p, 6).ToUpper() != "CDATA[") - { - throw new XmlException("Expected - state = XmlState.CHILDS; - break; - default: - state = XmlState.ATTRIB_NAME; - start = p; - continue; - } - break; - - case XmlState.ATTRIB_NAME: - // ATTRIB_NAME - if (!IsValidChar(c)) - { - if (start == p) - { - throw new XmlException("Expected attribute name", str, p); - } - var tmp = str.Substring(start, p - start); - aname = tmp; - if (xml.Attributes.ContainsKey(aname)) - { - throw new XmlException("Duplicate attribute [" + aname + "]", str, p); - } - state = XmlState.IGNORE_SPACES; - next = XmlState.EQUALS; - continue; - } - break; - - case XmlState.EQUALS: - // EQUALS - switch (c) - { - case CharCodeEquals: - // = - state = XmlState.IGNORE_SPACES; - next = XmlState.ATTVAL_BEGIN; - break; - default: - throw new XmlException("Expected =", str, p); - } - break; - - case XmlState.ATTVAL_BEGIN: - // ATTVAL_BEGIN - switch (c) - { - case CharCodeDoubleQuote: - case CharCodeSingleQuote: - // " ' - buf = new StringBuilder(); - state = XmlState.ATTRIB_VAL; - start = p + 1; - attrValQuote = c; - break; - } - break; - - case XmlState.ATTRIB_VAL: - // ATTRIB_VAL - switch (c) - { - case CharCodeAmp: - // & - buf.Append(str.Substring(start, p - start)); - state = XmlState.ESCAPE; - escapeNext = XmlState.ATTRIB_VAL; - start = p + 1; - break; - default: - if (c == attrValQuote) - { - buf.Append(str.Substring(start, p - start)); - var val = buf.ToString(); - buf = new StringBuilder(); - xml.Attributes[aname] = val; - state = XmlState.IGNORE_SPACES; - next = XmlState.BODY; - } - break; - } - break; - - case XmlState.CHILDS: - // CHILDS - p = Parse(str, p, xml); - start = p; - state = XmlState.BEGIN; - break; - - case XmlState.WAIT_END: - // WAIT_END - switch (c) - { - case CharCodeGreaterThan: - // > - state = XmlState.BEGIN; - break; - default: - throw new XmlException("Expected >", str, p); - } - break; - case XmlState.WAIT_END_RET: - // WAIT_END_RET - switch (c) - { - case CharCodeGreaterThan: - // > - return p; - default: - throw new XmlException("Expected >", str, p); - } - case XmlState.CLOSE: - // CLOSE - if (!IsValidChar(c)) - { - if (start == p) - { - throw new XmlException("Expected node name", str, p); - } - - var v = str.Substring(start, p - start); - if (v != parent.LocalName) - { - throw new XmlException("Expected ", str, p); - } - - state = XmlState.IGNORE_SPACES; - next = XmlState.WAIT_END_RET; - continue; - } - break; - - case XmlState.COMMENT: - // COMMENT - if (c == CharCodeMinus && str[p + 1] == CharCodeMinus && str[p + 2] == CharCodeGreaterThan) - { - // --> - //var comment = new XmlNode(); - //comment.NodeType = XmlNodeType.Comment; - //comment.Value = str.Substring(start, p - start); - //parent.AddChild(comment); - p += 2; - state = XmlState.BEGIN; - } - break; - - case XmlState.DOCTYPE: - // DOCTYPE - if (c == CharCodeBrackedOpen) - { - // [ - nbrackets++; - } - else if (c == CharCodeBrackedClose) - { - // ] - nbrackets--; - } - else if (c == CharCodeGreaterThan && nbrackets == 0) - { - // > - var node = new XmlNode(); - node.NodeType = XmlNodeType.DocumentType; - node.Value = str.Substring(start, p - start); - parent.AddChild(node); - state = XmlState.BEGIN; - } - break; - - case XmlState.HEADER: - // HEADER - if (c == CharCodeQuestion && str[p + 1] == CharCodeGreaterThan) - { - // ?> - p++; - // skip - state = XmlState.BEGIN; - } - break; - - case XmlState.ESCAPE: - // ESCAPE - if (c == (int)';') - { - // ; - var s = str.Substring(start, p - start); - if (s[0] == CharCodeSharp) - { - // # - var code = s[1] == CharCodeLowerX - ? Platform.Platform.ParseInt("0" + s.Substring(1, s.Length - 1)) - : Platform.Platform.ParseInt(s.Substring(1, s.Length - 1)); - buf.AppendChar(code); - } - else if (Escapes.ContainsKey(s)) - { - buf.Append(Escapes[s]); - } - else - { - buf.Append("&" + s + ";"); - } - - start = p + 1; - state = escapeNext; - } - else if (!IsValidChar(c) && c != CharCodeSharp) - { - // # - buf.Append("&"); - buf.Append(str.Substring(start, p - start)); - p--; - start = p + 1; - state = escapeNext; - } - break; - } - - p++; - } - - if (state == XmlState.BEGIN) - { - start = p; - state = XmlState.PCDATA; - } - - if (state == XmlState.PCDATA) - { - if (p != start) - { - buf.Append(str.Substring(start, p - start)); - var node = new XmlNode(); - node.NodeType = XmlNodeType.Text; - node.Value = buf.ToString(); - parent.AddChild(node); - } - return p; - } - - if (state == XmlState.ESCAPE && escapeNext == XmlState.PCDATA) - { - buf.Append("&"); - buf.Append(str.Substring(start, p - start)); - var node = new XmlNode(); - node.NodeType = XmlNodeType.Text; - node.Value = buf.ToString(); - parent.AddChild(node); - return p; - } - - throw new XmlException("Unexpected end", str, p); - } - - - private static bool IsValidChar(int c) - { - return (c >= CharCodeLowerA && c <= CharCodeLowerZ) || (c >= CharCodeUpperA && c <= CharCodeUpperZ) || - (c >= CharCode0 && c <= CharCode9) || c == CharCodeColon || c == CharCodeDot || c == CharCodeUnderscore || - c == CharCodeMinus; - } - - /// - /// faster than enum - /// - class XmlState - { - public const int IGNORE_SPACES = 0; - public const int BEGIN = 1; - public const int BEGIN_NODE = 2; - public const int TAG_NAME = 3; - public const int BODY = 4; - public const int ATTRIB_NAME = 5; - public const int EQUALS = 6; - public const int ATTVAL_BEGIN = 7; - public const int ATTRIB_VAL = 8; - public const int CHILDS = 9; - public const int CLOSE = 10; - public const int WAIT_END = 11; - public const int WAIT_END_RET = 12; - public const int PCDATA = 13; - public const int HEADER = 14; - public const int COMMENT = 15; - public const int DOCTYPE = 16; - public const int CDATA = 17; - public const int ESCAPE = 18; - } - } -} diff --git a/Tools/FontMeasurement.html b/Tools/FontMeasurement.html deleted file mode 100644 index 1d9422f6b..000000000 --- a/Tools/FontMeasurement.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - - Font Size Generation - - - - -
    - -
    - - \ No newline at end of file diff --git a/Tools/addSourceHeader.js b/Tools/addSourceHeader.js deleted file mode 100644 index 9eec83be8..000000000 --- a/Tools/addSourceHeader.js +++ /dev/null @@ -1,25 +0,0 @@ -var templateFile = process.argv[2]; -var fs = require('fs'); -var execSync = require('child_process').execSync; - -console.log("Loading Template from " + templateFile); - -var buildProps = fs.readFileSync('Directory.Build.props', 'utf8'); -var version = (/([^<]+)<\/FileVersion>/i).exec(buildProps)[1]; -var branch = execSync('git rev-parse --abbrev-ref HEAD').toString().trim(); - -var template = fs.readFileSync(templateFile, 'utf8'); -template = template.replace("{{year}}", new Date().getFullYear()); -template = template.replace("{{version}}", version); -template = template.replace("{{branch}}", branch); - -console.log(template); - -for(var i = 3; i < process.argv.length; i++) -{ - var fileName = process.argv[i]; - console.log("Adding header to " + fileName); - var source = fs.readFileSync(fileName, 'utf8'); - source = template.replace("{{source}}", source); - fs.writeFileSync(fileName, source, 'utf8'); -} \ No newline at end of file diff --git a/Tools/header.js b/Tools/header.js deleted file mode 100644 index 264a8be24..000000000 --- a/Tools/header.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * alphaTab v{{version}} ({{branch}}) - * - * This file is part of alphaTab. - * Copyright © {{year}}, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -{{source}} \ No newline at end of file diff --git a/Tools/miniweb.exe b/Tools/miniweb.exe deleted file mode 100644 index bae19c1f6..000000000 Binary files a/Tools/miniweb.exe and /dev/null differ diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 7e3bbc94e..000000000 --- a/appveyor.yml +++ /dev/null @@ -1,108 +0,0 @@ -version: 0.9.1.{build} -# branches to build -branches: - only: - - master - - develop - -image: Visual Studio 2017 -configuration: Release -platform: Any CPU -environment: - nodejs_version: '8' - global: - HAXELIB_ROOT: C:\projects\haxelib - access_token: - secure: ig+MHQrCziKIZzGkDBsOTuusdSATGEXh79QCG8b4guSHAcM0+uYy0zGoVTKG446Q - deployment_token: - secure: F3tqGIN2guX5kU5B6q89HGPDAgI0g//DwtqRQL0oVeaJU6oBkgbQeJfiproA3yFi - -skip_commits: - author: AlphaTab Build Agent - -install: - # - # Haxe installation - - ps: Set-Service wuauserv -StartupType Manual - # Install the neko chocolatey package (https://chocolatey.org/packages/neko) - - cinst neko --version 2.2.0 -y - # Install the haxe chocolatey package (https://chocolatey.org/packages/haxe) - - cinst haxe --version 3.4.4 -y - - RefreshEnv - # Setup haxelib - - mkdir "%HAXELIB_ROOT%" - - haxelib setup "%HAXELIB_ROOT%" - # Install project dependencies - # `> log.txt || type log.txt` is for muting the output unless there is an error - - haxelib install munit > log.txt || type log.txt && cmd /C exit 1 - - haxelib install hamcrest > log.txt || type log.txt && cmd /C exit 1 - - haxelib list - # - # Npm/Uglifyjs installation - - npm install -g uglify-js - - RefreshEnv - -dotnet_csproj: - patch: true - file: 'Directory.Build.props' - version: '{version}' - package_version: '{version}' - assembly_version: '{version}' - file_version: '{version}' - -build: - parallel: true - publish_nuget: false - publish_nuget_symbols: false - verbosity: minimal - -before_build: -- nuget restore - -test: - assemblies: - only: - - Source\AlphaTab.Test.CSharp\bin\Release\net471\AlphaTab.Test.CSharp.dll - -after_test: - - Documentation\generate.bat - - 7z a -r Documentation\documentation.zip Documentation\output\* - -artifacts: - # JavaScript Output - - path: Build\JavaScript\*.* - name: JavaScript - # NuGet package output - - path: Source\AlphaTab.CSharp\Bin\$(configuration)\*.nupkg - name: NuGet - # Documentation - - path: Documentation\documentation.zip - name: Documentation - -deploy: - # My Get (Every Build) - - provider: NuGet - server: https://www.myget.org/F/coderline/api/v2 - api_key: - secure: sQI+q8oHDFwWAWOy/pvP4zHehqNASufniwX7aOpN03YlcKt44ZX/55guh2kGoJ2n - artifact: NuGet - # NuGet.org (Only Tagged on Master) - - provider: NuGet - api_key: - secure: 89W5Zsv2maiKx7/Bmn5zFFlKjPC2J7NnDRhPrpeH9hEnVvtDlWt04xR+kYi52dPt - artifact: NuGet - on: - branch: master - appveyor_repo_tag: true - # Documentation - - provider: Environment - name: AlphaTabDocumentation - -on_success: - - git config --global credential.helper store - - ps: Add-Content "$env:USERPROFILE\.git-credentials" "https://$($env:access_token):x-oauth-basic@github.com`n" - - git config --global user.email "appveyor@alphatab.net" - - git config --global user.name "AlphaTab Build Agent" - - git add Directory.Build.props - - git commit -m "%APPVEYOR_REPO_COMMIT_MESSAGE%, build %APPVEYOR_BUILD_VERSION%" - - git push origin "%APPVEYOR_REPO_BRANCH%" diff --git a/bower.json b/bower.json deleted file mode 100644 index 57955fa29..000000000 --- a/bower.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "name": "AlphaTab", - "homepage": "http://www.alphatab.net", - "authors": [ - "Danielku15 " - ], - "description": "alphaTab is a cross platform music notation and guitar tablature rendering library.", - "main": [ - "Build/JavaScript/AlphaTab.js", - "Build/JavaScript/AlphaTab.worker.js", - "Build/JavaScript/jquery.alphaTab.js" - ], - "license": "LGPL-3.0+", - "repository": { - "type": "git", - "url": "git://github.com/CoderLine/alphaTab.git" - }, - "moduleType": [ - "globals" - ], - "keywords": [ - "music", - "notation", - "html5", - "svg", - "guitar", - "tablature" - ], - "ignore": [ - "Build/CSharp", - "Font", - "Samples", - "SharpKit", - "Source", - "packages", - "Tools", - ".gitattributes", - ".gitignore", - ".gitmodules", - "AlphaTab.sln", - "AlphaTab.targets", - "LICENSE", - "README.md", - "TODO", - "logo.png" - ] -} diff --git a/code-of-conduct.md b/code-of-conduct.md new file mode 100644 index 000000000..4a9568ba8 --- /dev/null +++ b/code-of-conduct.md @@ -0,0 +1,74 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at alexjovermorales@gmail.com. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/Build/JavaScript/Font/Bravura.eot b/font/bravura/Bravura.eot similarity index 100% rename from Build/JavaScript/Font/Bravura.eot rename to font/bravura/Bravura.eot diff --git a/Build/JavaScript/Font/Bravura.otf b/font/bravura/Bravura.otf similarity index 100% rename from Build/JavaScript/Font/Bravura.otf rename to font/bravura/Bravura.otf diff --git a/Build/JavaScript/Font/Bravura.svg b/font/bravura/Bravura.svg similarity index 100% rename from Build/JavaScript/Font/Bravura.svg rename to font/bravura/Bravura.svg diff --git a/Font/bravura/ttf/Bravura.ttf b/font/bravura/Bravura.ttf similarity index 100% rename from Font/bravura/ttf/Bravura.ttf rename to font/bravura/Bravura.ttf diff --git a/Build/JavaScript/Font/Bravura.woff b/font/bravura/Bravura.woff similarity index 100% rename from Build/JavaScript/Font/Bravura.woff rename to font/bravura/Bravura.woff diff --git a/Font/bravura/woff/Bravura.woff2 b/font/bravura/Bravura.woff2 similarity index 100% rename from Font/bravura/woff/Bravura.woff2 rename to font/bravura/Bravura.woff2 diff --git a/Font/bravura/eot/BravuraText.eot b/font/bravura/BravuraText.eot similarity index 100% rename from Font/bravura/eot/BravuraText.eot rename to font/bravura/BravuraText.eot diff --git a/Font/bravura/otf/BravuraText.otf b/font/bravura/BravuraText.otf similarity index 100% rename from Font/bravura/otf/BravuraText.otf rename to font/bravura/BravuraText.otf diff --git a/Font/bravura/svg/BravuraText.svg b/font/bravura/BravuraText.svg similarity index 100% rename from Font/bravura/svg/BravuraText.svg rename to font/bravura/BravuraText.svg diff --git a/Font/bravura/woff/BravuraText.woff b/font/bravura/BravuraText.woff similarity index 100% rename from Font/bravura/woff/BravuraText.woff rename to font/bravura/BravuraText.woff diff --git a/Font/bravura/FONTLOG.txt b/font/bravura/FONTLOG.txt similarity index 100% rename from Font/bravura/FONTLOG.txt rename to font/bravura/FONTLOG.txt diff --git a/Font/bravura/OFL-FAQ.txt b/font/bravura/OFL-FAQ.txt similarity index 100% rename from Font/bravura/OFL-FAQ.txt rename to font/bravura/OFL-FAQ.txt diff --git a/Font/bravura/OFL.txt b/font/bravura/OFL.txt similarity index 100% rename from Font/bravura/OFL.txt rename to font/bravura/OFL.txt diff --git a/Font/bravura/bravura-text.md b/font/bravura/bravura-text.md similarity index 100% rename from Font/bravura/bravura-text.md rename to font/bravura/bravura-text.md diff --git a/Font/bravura/bravura_metadata.json b/font/bravura/bravura_metadata.json similarity index 100% rename from Font/bravura/bravura_metadata.json rename to font/bravura/bravura_metadata.json diff --git a/font/sonivox/LICENSE b/font/sonivox/LICENSE new file mode 100644 index 000000000..e7bbb6b3b --- /dev/null +++ b/font/sonivox/LICENSE @@ -0,0 +1,10 @@ +Copyright (c) 2004-2006 Sonic Network Inc. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/font/sonivox/README.md b/font/sonivox/README.md new file mode 100644 index 000000000..a6ff5c1dd --- /dev/null +++ b/font/sonivox/README.md @@ -0,0 +1,13 @@ +SONiVOX EAS Version 2.00 Editing Software: Synthfont Viena +Ported from Samsung GT-E1272 and Android Soundfont of SONiVOX EAS Full Presets and Full Keys Range (no reverb). Frequency 11khz - 32khz +Using a Creative Sound Blaster GM bank. Using a Software Creative Vienna Soundfont Studio. Copy a Soundfont wt22khz.sf2 from in Floppy Disk 1.44MB Assembled of Indonesia. +Sonivox Corporation Tokyo Japan. +Copyright 1993 Sonivox Corporation + +https://musical-artifacts.com/artifacts/824 + + +This soundfont is based on the Sonivox EAS synthesizer, Copyright Sonic Network Inc. 2006. +Sonivox EAS belongs to the Android Open Source Project. +https://android.googlesource.com/platform/external/sonivox/+/refs/heads/master + diff --git a/font/sonivox/sonivox.sf2 b/font/sonivox/sonivox.sf2 new file mode 100644 index 000000000..bd4cc714c Binary files /dev/null and b/font/sonivox/sonivox.sf2 differ diff --git a/global.json b/global.json deleted file mode 100644 index f30586045..000000000 --- a/global.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "msbuild-sdks": - { - "MSBuild.Sdk.Extras": "1.5.4" - } -} \ No newline at end of file diff --git a/Images/BrowserStack.png b/img/BrowserStack.png similarity index 100% rename from Images/BrowserStack.png rename to img/BrowserStack.png diff --git a/Images/banner.png b/img/banner.png similarity index 100% rename from Images/banner.png rename to img/banner.png diff --git a/Images/logo.png b/img/logo.png similarity index 100% rename from Images/logo.png rename to img/logo.png diff --git a/karma.conf.js b/karma.conf.js new file mode 100644 index 000000000..f7b7d5e0e --- /dev/null +++ b/karma.conf.js @@ -0,0 +1,98 @@ +const resolve = require('./rollup.resolve'); +const commonjs = require('@rollup/plugin-commonjs'); +const multer = require('multer'); +const storage = multer.diskStorage({ + destination: 'test-results/', + filename: function (req, file, cb) { + try { + cb(null, file.originalname); + } catch (e) { + const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1e9); + cb(null, file.fieldname + '-' + uniqueSuffix); + } + } +}); +const upload = multer({ storage: storage }); +const cors = require('cors'); +const fs = require('fs'); + +module.exports = function (config) { + config.set({ + frameworks: ['jasmine', 'express-http-server'], + files: [ + { pattern: 'dist/lib.test/test/index.js', watched: false }, + { + pattern: 'test-data/**/*', + type: 'html', + watched: false, + included: false, + served: true, + nocache: true + }, + { + pattern: 'font/**/*', + type: 'html', + watched: false, + included: false, + served: true, + nocache: true + } + ], + preprocessors: { + 'dist/lib.test/test/index.js': ['rollup'] + }, + + client: { + clearContext: false, + jasmine: { + random: false, + stopSpecOnExpectationFailure: false + } + }, + + expressHttpServer: { + port: 8090, + appVisitor: function (app, log) { + app.use(cors()); + app.post( + '/save-visual-error', + upload.fields([ + { + name: 'expected', + maxCount: 1 + }, + { + name: 'actual', + maxCount: 1 + }, + { + name: 'diff', + maxCount: 1 + } + ]), + function (req, res) { + log.info(`save visual error ${req.file}`); + res.send(JSON.stringify('OK')); + } + ); + } + }, + + rollupPreprocessor: { + plugins: [ + resolve({ + mappings: { + '@src': 'dist/lib.test/src', + '@test': 'dist/lib.test/test' + } + }), + commonjs() + ], + output: { + format: 'iife', + name: 'alphaTab', + sourcemap: false + } + } + }); +}; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..7bf3b6734 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,4347 @@ +{ + "name": "@coderline/alphatab", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.0.tgz", + "integrity": "sha512-6G8bQKjOh+of4PV/ThDm/rRqlU7+IGoJuofpagU5GlEl29Vv0RGqqt86ZGRV8ZuSOY3o+8yXl5y782SMcG7SHw==", + "dev": true + }, + "@babel/highlight": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz", + "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.9.0", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", + "integrity": "sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.3", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz", + "integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz", + "integrity": "sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.3", + "fastq": "^1.6.0" + } + }, + "@rollup/plugin-commonjs": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-14.0.0.tgz", + "integrity": "sha512-+PSmD9ePwTAeU106i9FRdc+Zb3XUWyW26mo5Atr2mk82hor8+nPwkztEjFo8/B1fJKfaQDg9aM2bzQkjhi7zOw==", + "dev": true, + "requires": { + "@rollup/pluginutils": "^3.0.8", + "commondir": "^1.0.1", + "estree-walker": "^1.0.1", + "glob": "^7.1.2", + "is-reference": "^1.1.2", + "magic-string": "^0.25.2", + "resolve": "^1.11.0" + } + }, + "@rollup/pluginutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", + "dev": true, + "requires": { + "@types/estree": "0.0.39", + "estree-walker": "^1.0.1", + "picomatch": "^2.2.2" + } + }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "dev": true + }, + "@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", + "dev": true + }, + "@types/events": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", + "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==", + "dev": true + }, + "@types/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-UoOfVEzAUpeSPmjm7h1uk5MH6KZma2z2O7a75onTGjnNvAvMVrPzPL/vBbT65iIGHWj6rokwfmYcmxmlSf2uwg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/glob": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", + "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", + "dev": true, + "requires": { + "@types/events": "*", + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "@types/jasmine": { + "version": "3.5.11", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.5.11.tgz", + "integrity": "sha512-fg1rOd/DehQTIJTifGqGVY6q92lDgnLfs7C6t1ccSwQrMyoTGSoH6wWzhJDZb6ezhsdwAX4EIBLe8w5fXWmEng==", + "dev": true + }, + "@types/minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", + "dev": true + }, + "@types/node": { + "version": "13.11.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.11.1.tgz", + "integrity": "sha512-eWQGP3qtxwL8FGneRrC5DwrJLGN4/dH1clNTuLfN81HCrxVtxRjygDTUoZJ5ASlDEeo0ppYFQjQIlXhtXpOn6g==", + "dev": true + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "dev": true, + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "after": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", + "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=", + "dev": true + }, + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY=", + "dev": true + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "arraybuffer.slice": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", + "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==", + "dev": true + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + } + }, + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", + "dev": true + }, + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, + "backo2": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", + "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, + "base64-arraybuffer": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", + "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=", + "dev": true + }, + "base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "dev": true + }, + "basic-auth": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-1.1.0.tgz", + "integrity": "sha1-RSIe5Cn37h5QNb4/UVM/HN/SmIQ=", + "dev": true + }, + "better-assert": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", + "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", + "dev": true, + "requires": { + "callsite": "1.0.0" + } + }, + "binary-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", + "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==", + "dev": true + }, + "blob": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", + "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==", + "dev": true + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "dev": true, + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, + "busboy": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz", + "integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=", + "dev": true, + "requires": { + "dicer": "0.2.5", + "readable-stream": "1.1.x" + } + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "dev": true + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, + "callsite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", + "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chokidar": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.1.tgz", + "integrity": "sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg==", + "dev": true, + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.1.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.3.0" + } + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "colorette": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.1.0.tgz", + "integrity": "sha512-6S062WDQUXi6hOfkO/sBPVwE5ASXY4G2+b4atvhJfSsuUUhIaUKlkjLe9692Ipyt5/a+IPF5aVTu3V5gvXq5cg==", + "dev": true + }, + "colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "commenting": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/commenting/-/commenting-1.1.0.tgz", + "integrity": "sha512-YeNK4tavZwtH7jEgK1ZINXzLKm6DZdEMfsaaieOsCAN0S8vsY7UeuO3Q7d/M018EFgE+IeUAuBOKkFccBZsUZA==", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "component-bind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", + "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=", + "dev": true + }, + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, + "component-inherit": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", + "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "concurrently": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-5.2.0.tgz", + "integrity": "sha512-XxcDbQ4/43d6CxR7+iV8IZXhur4KbmEJk1CetVMUqCy34z9l0DkszbY+/9wvmSnToTej0SYomc2WSRH+L0zVJw==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "date-fns": "^2.0.1", + "lodash": "^4.17.15", + "read-pkg": "^4.0.1", + "rxjs": "^6.5.2", + "spawn-command": "^0.0.2-1", + "supports-color": "^6.1.0", + "tree-kill": "^1.2.2", + "yargs": "^13.3.0" + }, + "dependencies": { + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "connect": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "dev": true, + "requires": { + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" + } + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", + "dev": true + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, + "corser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", + "integrity": "sha1-jtolLsqrWEDc2XXOuQ2TcMgZ/4c=", + "dev": true + }, + "custom-event": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", + "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", + "dev": true + }, + "date-fns": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.14.0.tgz", + "integrity": "sha512-1zD+68jhFgDIM0rF05rcwYO8cExdNqxjq4xP1QKM60Q45mnO6zaMWB4tOzrIr4M4GSLntsKeE4c9Bdl2jhL/yw==", + "dev": true + }, + "date-format": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-3.0.0.tgz", + "integrity": "sha512-eyTcpKOcamdhWJXj56DpQMo1ylSQpcGtGKXcU0Tb97+K56/CF5amAqqqNj0+KvA0iw2ynxtHWFsPDSClCxe48w==", + "dev": true + }, + "debounce": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.0.tgz", + "integrity": "sha512-mYtLl1xfZLi1m4RtQYlZgJUNQjl4ZxVnHzIR8nLLgi4q1YT8o/WM+MK/f8yfcc9s5Ir5zRaPZyZU6xs1Syoocg==", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "dev": true + }, + "detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", + "dev": true + }, + "di": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", + "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=", + "dev": true + }, + "dicer": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz", + "integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=", + "dev": true, + "requires": { + "readable-stream": "1.1.x", + "streamsearch": "0.1.2" + } + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "doctrine": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-0.7.2.tgz", + "integrity": "sha1-fLhgNZujvpDgQLJrcpzkv6ZUxSM=", + "dev": true, + "requires": { + "esutils": "^1.1.6", + "isarray": "0.0.1" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + } + } + }, + "dom-serialize": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", + "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", + "dev": true, + "requires": { + "custom-event": "~1.0.0", + "ent": "~2.2.0", + "extend": "^3.0.0", + "void-elements": "^2.0.0" + } + }, + "ecstatic": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/ecstatic/-/ecstatic-3.3.2.tgz", + "integrity": "sha512-fLf9l1hnwrHI2xn9mEDT7KIi22UDqA2jaCwyCbSUJh9a1V+LEUSL/JO/6TIz/QyuBURWUHrFL5Kg2TtO1bkkog==", + "dev": true, + "requires": { + "he": "^1.1.1", + "mime": "^1.6.0", + "minimist": "^1.1.0", + "url-join": "^2.0.5" + }, + "dependencies": { + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + } + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "dev": true + }, + "engine.io": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.4.2.tgz", + "integrity": "sha512-b4Q85dFkGw+TqgytGPrGgACRUhsdKc9S9ErRAXpPGy/CXKs4tYoHDkvIRdsseAF7NjfVwjRFIn6KTnbw7LwJZg==", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "0.3.1", + "debug": "~4.1.0", + "engine.io-parser": "~2.2.0", + "ws": "^7.1.2" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "engine.io-client": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.3.tgz", + "integrity": "sha512-0NGY+9hioejTEJCaSJZfWZLk4FPI9dN+1H1C4+wj2iuFba47UgZbJzfWs4aNFajnX/qAaYKbe2lLTfEEWzCmcw==", + "dev": true, + "requires": { + "component-emitter": "~1.3.0", + "component-inherit": "0.0.3", + "debug": "~4.1.0", + "engine.io-parser": "~2.2.0", + "has-cors": "1.1.0", + "indexof": "0.0.1", + "parseqs": "0.0.5", + "parseuri": "0.0.5", + "ws": "~6.1.0", + "xmlhttprequest-ssl": "~1.5.4", + "yeast": "0.1.2" + }, + "dependencies": { + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "ws": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz", + "integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0" + } + } + } + }, + "engine.io-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.0.tgz", + "integrity": "sha512-6I3qD9iUxotsC5HEMuuGsKA0cXerGz+4uGcXQEkfBidgKf0amsjrrtwcbwK/nzpZBxclXlV7gGl9dgWvu4LF6w==", + "dev": true, + "requires": { + "after": "0.8.2", + "arraybuffer.slice": "~0.0.7", + "base64-arraybuffer": "0.1.5", + "blob": "0.0.5", + "has-binary2": "~1.0.2" + } + }, + "ent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", + "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", + "dev": true + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "estree-walker": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", + "dev": true + }, + "esutils": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-1.1.6.tgz", + "integrity": "sha1-wBzKqa5LiXxtDD4hCuUvPHqEQ3U=", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "dev": true + }, + "eventemitter3": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz", + "integrity": "sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==", + "dev": true + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "dev": true, + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "dev": true, + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "dev": true + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "fast-glob": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.2.tgz", + "integrity": "sha512-UDV82o4uQyljznxwMxyVRJgZZt3O5wENYojjzbaGEGZgeOxkLFf+V4cnUD+krzb2F72E18RhamkMZ7AdeggF7A==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.0", + "merge2": "^1.3.0", + "micromatch": "^4.0.2", + "picomatch": "^2.2.1" + } + }, + "fastq": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.7.0.tgz", + "integrity": "sha512-YOadQRnHd5q6PogvAR/x62BGituF2ufiEA6s8aavQANw5YKHERI4AREboX6KotzP8oX2klxYF2wcV/7bn1clfQ==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "findup-sync": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", + "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", + "dev": true, + "requires": { + "detect-file": "^1.0.0", + "is-glob": "^3.1.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + }, + "dependencies": { + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } + } + }, + "flatted": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", + "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", + "dev": true + }, + "follow-redirects": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.11.0.tgz", + "integrity": "sha512-KZm0V+ll8PfBrKwMzdo5D13b1bur9Iq9Zd/RMmAoQQcl2PxxFml8cxXPaaPYVbV0RjNjq1CU7zIzAOqtUPudmA==", + "dev": true, + "requires": { + "debug": "^3.0.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "dev": true + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "dev": true + }, + "fs-extra": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", + "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==", + "dev": true, + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^1.0.0" + }, + "dependencies": { + "jsonfile": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", + "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^1.0.0" + } + }, + "universalify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", + "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==", + "dev": true + } + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", + "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", + "dev": true, + "optional": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "git-branch": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/git-branch/-/git-branch-2.0.1.tgz", + "integrity": "sha512-jMCT1kjXvsUdZKQd2p8E1uZhKsIuR1pnHgcDYQpQiXBtzE9cmYGvOcCSGqqi58x0B9CPS0lUSu/yti866est8g==", + "dev": true, + "requires": { + "findup-sync": "^2.0.0" + } + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "dev": true, + "requires": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + } + }, + "global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "dev": true, + "requires": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + } + }, + "globby": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.1.tgz", + "integrity": "sha512-sSs4inE1FB2YQiymcmTv6NWENryABjUNPeWhOvmn4SjtKybglsyPZxFB3U1/+L1bYi0rNZDqCLlHyLYDl1Pq5A==", + "dev": true, + "requires": { + "@types/glob": "^7.1.1", + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.0.3", + "glob": "^7.1.3", + "ignore": "^5.1.1", + "merge2": "^1.2.3", + "slash": "^3.0.0" + } + }, + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "dev": true + }, + "has-binary2": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", + "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", + "dev": true, + "requires": { + "isarray": "2.0.1" + } + }, + "has-cors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", + "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "dev": true, + "requires": { + "parse-passwd": "^1.0.0" + } + }, + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "dev": true + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + } + } + }, + "http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "requires": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-server": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/http-server/-/http-server-0.12.3.tgz", + "integrity": "sha512-be0dKG6pni92bRjq0kvExtj/NrrAd28/8fCXkaI/4piTwQMSDSLMhWyW0NI1V+DBI3aa1HMlQu46/HjVLfmugA==", + "dev": true, + "requires": { + "basic-auth": "^1.0.3", + "colors": "^1.4.0", + "corser": "^2.0.1", + "ecstatic": "^3.3.2", + "http-proxy": "^1.18.0", + "minimist": "^1.2.5", + "opener": "^1.5.1", + "portfinder": "^1.0.25", + "secure-compare": "3.0.1", + "union": "~0.5.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz", + "integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==", + "dev": true + }, + "indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-plain-object": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.0.tgz", + "integrity": "sha512-tZIpofR+P05k8Aocp7UI/2UTa9lTJSebCXpFFoR9aibpokDj/uXBsJ8luUu0tTVYKkMU6URDUuOfJZ7koewXvg==", + "dev": true, + "requires": { + "isobject": "^4.0.0" + } + }, + "is-reference": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", + "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", + "dev": true, + "requires": { + "@types/estree": "*" + } + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", + "dev": true + }, + "isbinaryfile": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.6.tgz", + "integrity": "sha512-ORrEy+SNVqUhrCaal4hA4fBzhggQQ+BaLntyPOdoEiwlKZW9BZiJXjg3RMiruE4tPEI3pyVPpySHQF/dKWperg==", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz", + "integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==", + "dev": true + }, + "jasmine-core": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.5.0.tgz", + "integrity": "sha512-nCeAiw37MIMA9w9IXso7bRaLl+c/ef3wnxsoSAlYrzS+Ot0zTG6nU8G/cIfGkqpkjX2wNaIW9RFG0TwIFnG6bA==", + "dev": true + }, + "jest-worker": { + "version": "26.1.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.1.0.tgz", + "integrity": "sha512-Z9P5pZ6UC+kakMbNJn+tA2RdVdNX5WH1x+5UCBZ9MxIK24pjYtFt96fK+UwBTrjLYm232g1xz0L3eTh51OW+yQ==", + "dev": true, + "requires": { + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", + "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "karma": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/karma/-/karma-5.1.0.tgz", + "integrity": "sha512-I3aPbkuIbwuBo6wSog97P5WnnhCgUTsWTu/bEw1vZVQFbXmKO3PK+cfFhZioOgVtJAuQxoyauGNjnwXNHMCxbw==", + "dev": true, + "requires": { + "body-parser": "^1.19.0", + "braces": "^3.0.2", + "chokidar": "^3.0.0", + "colors": "^1.4.0", + "connect": "^3.7.0", + "di": "^0.0.1", + "dom-serialize": "^2.2.1", + "flatted": "^2.0.2", + "glob": "^7.1.6", + "graceful-fs": "^4.2.4", + "http-proxy": "^1.18.1", + "isbinaryfile": "^4.0.6", + "lodash": "^4.17.15", + "log4js": "^6.2.1", + "mime": "^2.4.5", + "minimatch": "^3.0.4", + "qjobs": "^1.2.0", + "range-parser": "^1.2.1", + "rimraf": "^3.0.2", + "socket.io": "^2.3.0", + "source-map": "^0.6.1", + "tmp": "0.2.1", + "ua-parser-js": "0.7.21", + "yargs": "^15.3.1" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "requires": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "requires": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + } + }, + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "karma-chrome-launcher": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.1.0.tgz", + "integrity": "sha512-3dPs/n7vgz1rxxtynpzZTvb9y/GIaW8xjAwcIGttLbycqoFtI7yo1NGnQi6oFTherRE+GIhCAHZC4vEqWGhNvg==", + "dev": true, + "requires": { + "which": "^1.2.1" + } + }, + "karma-express-http-server": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/karma-express-http-server/-/karma-express-http-server-0.0.1.tgz", + "integrity": "sha1-zLbLSRJoEMS28RvP9aOappuqWBc=", + "dev": true, + "requires": { + "express": ">=4.4.2" + } + }, + "karma-jasmine": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-3.3.1.tgz", + "integrity": "sha512-Nxh7eX9mOQMyK0VSsMxdod+bcqrR/ikrmEiWj5M6fwuQ7oI+YEF1FckaDsWfs6TIpULm9f0fTKMjF7XcrvWyqQ==", + "dev": true, + "requires": { + "jasmine-core": "^3.5.0" + } + }, + "karma-jasmine-html-reporter": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.5.4.tgz", + "integrity": "sha512-PtilRLno5O6wH3lDihRnz0Ba8oSn0YUJqKjjux1peoYGwo0AQqrWRbdWk/RLzcGlb+onTyXAnHl6M+Hu3UxG/Q==", + "dev": true + }, + "karma-rollup-preprocessor": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/karma-rollup-preprocessor/-/karma-rollup-preprocessor-7.0.5.tgz", + "integrity": "sha512-VhZI81l8LZBvBrSf4xaojsbur7bcycsSlxXkYaTOjV6DQwx1gtAM0CQVdue7LuIbXB1AohYIg0S5at+dqDtMxQ==", + "dev": true, + "requires": { + "chokidar": "^3.3.1", + "debounce": "^1.2.0" + } + }, + "karma-spec-reporter": { + "version": "0.0.32", + "resolved": "https://registry.npmjs.org/karma-spec-reporter/-/karma-spec-reporter-0.0.32.tgz", + "integrity": "sha1-LpxyB+pyZ3EmAln4K+y1QyCeRAo=", + "dev": true, + "requires": { + "colors": "^1.1.2" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", + "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", + "dev": true + }, + "log4js": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.3.0.tgz", + "integrity": "sha512-Mc8jNuSFImQUIateBFwdOQcmC6Q5maU0VVvdC2R6XMb66/VnT+7WS4D/0EeNMZu1YODmJe5NIn2XftCzEocUgw==", + "dev": true, + "requires": { + "date-format": "^3.0.0", + "debug": "^4.1.1", + "flatted": "^2.0.1", + "rfdc": "^1.1.4", + "streamroller": "^2.2.4" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "magic-string": { + "version": "0.25.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", + "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", + "dev": true, + "requires": { + "sourcemap-codec": "^1.4.4" + } + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "merge2": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.3.0.tgz", + "integrity": "sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw==", + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "mime": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz", + "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==", + "dev": true + }, + "mime-db": { + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz", + "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==", + "dev": true + }, + "mime-types": { + "version": "2.1.26", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz", + "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==", + "dev": true, + "requires": { + "mime-db": "1.43.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "moment": { + "version": "2.25.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.25.1.tgz", + "integrity": "sha512-nRKMf9wDS4Fkyd0C9LXh2FFXinD+iwbJ5p/lh3CHitW9kZbRbJ8hCruiadiIXZVbeAqKZzqcTvHnK3mRhFjb6w==", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "multer": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.2.tgz", + "integrity": "sha512-xY8pX7V+ybyUpbYMxtjM9KAiD9ixtg5/JkeKUTD6xilfDv0vzzOFcCp4Ljb1UU3tSOM3VTZtKo63OmzOrGi3Cg==", + "dev": true, + "requires": { + "append-field": "^1.0.0", + "busboy": "^0.2.11", + "concat-stream": "^1.5.2", + "mkdirp": "^0.5.1", + "object-assign": "^4.1.1", + "on-finished": "^2.3.0", + "type-is": "^1.6.4", + "xtend": "^4.0.0" + } + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-component": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", + "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=", + "dev": true + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "^3.0.0" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "opener": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.1.tgz", + "integrity": "sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA==", + "dev": true + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "package-name-regex": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/package-name-regex/-/package-name-regex-1.0.8.tgz", + "integrity": "sha512-g3vB2J62dLqf4m50VM4tJUC4sixw3JB+Igd0cF3P/gJhAvmvsmFEV2eWZTeLbwfkKEWTf3+gwQ2C6JFFRxWHEQ==", + "dev": true + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", + "dev": true + }, + "parseqs": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", + "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", + "dev": true, + "requires": { + "better-assert": "~1.0.0" + } + }, + "parseuri": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", + "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", + "dev": true, + "requires": { + "better-assert": "~1.0.0" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "dev": true + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "portfinder": { + "version": "1.0.26", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.26.tgz", + "integrity": "sha512-Xi7mKxJHHMI3rIUrnm/jjUgwhbYMkp/XKEcZX3aG4BrumLpq3nmoQMX+ClYnDZnZ/New7IatC1no5RX0zo1vXQ==", + "dev": true, + "requires": { + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.1" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "dev": true, + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + } + }, + "qjobs": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", + "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", + "dev": true + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "dev": true, + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "read-pkg": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-4.0.1.tgz", + "integrity": "sha1-ljYlN48+HE1IyFhytabsfV0JMjc=", + "dev": true, + "requires": { + "normalize-package-data": "^2.3.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0" + } + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + } + } + }, + "readdirp": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.3.0.tgz", + "integrity": "sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ==", + "dev": true, + "requires": { + "picomatch": "^2.0.7" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true + }, + "resolve": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", + "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "dev": true, + "requires": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + } + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rfdc": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.1.4.tgz", + "integrity": "sha512-5C9HXdzK8EAqN7JDif30jqsBzavB7wLpaubisuQIGHWf2gUXSpzy6ArX/+Da8RjFpagWsCn+pIgxTMAmKw9Zug==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "rollup": { + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.21.0.tgz", + "integrity": "sha512-BEGgy+wSzux7Ycq58pRiWEOBZaXRXTuvzl1gsm7gqmsAHxkWf9nyA5V2LN9fGSHhhDQd0/C13iRzSh4bbIpWZQ==", + "dev": true, + "requires": { + "fsevents": "~2.1.2" + } + }, + "rollup-plugin-copy": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-copy/-/rollup-plugin-copy-3.3.0.tgz", + "integrity": "sha512-euDjCUSBXZa06nqnwCNADbkAcYDfzwowfZQkto9K/TFhiH+QG7I4PUsEMwM9tDgomGWJc//z7KLW8t+tZwxADA==", + "dev": true, + "requires": { + "@types/fs-extra": "^8.0.1", + "colorette": "^1.1.0", + "fs-extra": "^8.1.0", + "globby": "10.0.1", + "is-plain-object": "^3.0.0" + }, + "dependencies": { + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + } + } + }, + "rollup-plugin-dts": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/rollup-plugin-dts/-/rollup-plugin-dts-1.4.8.tgz", + "integrity": "sha512-2qHB4B3oaTyi1mDmqDzsRGKlev32v3EhMUAmc45Cn9eB22R7FsYDiigvT9XdM/oQzfd0qv5MkeZju8DP0nauiA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "dev": true, + "optional": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", + "dev": true, + "optional": true + }, + "@babel/highlight": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "dev": true, + "optional": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + } + } + }, + "rollup-plugin-license": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-license/-/rollup-plugin-license-2.1.0.tgz", + "integrity": "sha512-HvwwqCbG+//hAwGxOce+XadsEooq79SqDu3z2GTwU2y1rFQ/uqT5JbCzcmauFII+nKZ0/N2BzG76ZuP7OdfU7A==", + "dev": true, + "requires": { + "commenting": "1.1.0", + "glob": "7.1.6", + "lodash": "4.17.15", + "magic-string": "0.25.7", + "mkdirp": "1.0.4", + "moment": "2.25.1", + "package-name-regex": "1.0.8", + "spdx-expression-validate": "2.0.0", + "spdx-satisfies": "5.0.0" + }, + "dependencies": { + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + } + } + }, + "rollup-plugin-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-6.1.0.tgz", + "integrity": "sha512-4fB3M9nuoWxrwm39habpd4hvrbrde2W2GG4zEGPQg1YITNkM3Tqur5jSuXlWNzbv/2aMLJ+dZJaySc3GCD8oDw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "jest-worker": "^26.0.0", + "serialize-javascript": "^3.0.0", + "terser": "^4.7.0" + } + }, + "run-parallel": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", + "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==", + "dev": true + }, + "rxjs": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.0.tgz", + "integrity": "sha512-3HMA8z/Oz61DUHe+SdOiQyzIf4tOx5oQHmMir7IZEu6TMqCLHT4LRcmNaUS0NwOz8VLvmmBduMsoaUvMaIiqzg==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "secure-compare": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", + "integrity": "sha1-8aAymzCLIh+uN7mXTz1XjQypmeM=", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "serialize-javascript": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-3.1.0.tgz", + "integrity": "sha512-JIJT1DGiWmIKhzRsG91aS6Ze4sFUrYbltlkg2onR5OrnNM02Kl/hnY/T4FN2omvyeBbQmMJv+K4cPOpGzOTFBg==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "dev": true, + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "socket.io": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.3.0.tgz", + "integrity": "sha512-2A892lrj0GcgR/9Qk81EaY2gYhCBxurV0PfmmESO6p27QPrUK1J3zdns+5QPqvUYK2q657nSj0guoIil9+7eFg==", + "dev": true, + "requires": { + "debug": "~4.1.0", + "engine.io": "~3.4.0", + "has-binary2": "~1.0.2", + "socket.io-adapter": "~1.1.0", + "socket.io-client": "2.3.0", + "socket.io-parser": "~3.4.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "socket.io-adapter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz", + "integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g==", + "dev": true + }, + "socket.io-client": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.3.0.tgz", + "integrity": "sha512-cEQQf24gET3rfhxZ2jJ5xzAOo/xhZwK+mOqtGRg5IowZsMgwvHwnf/mCRapAAkadhM26y+iydgwsXGObBB5ZdA==", + "dev": true, + "requires": { + "backo2": "1.0.2", + "base64-arraybuffer": "0.1.5", + "component-bind": "1.0.0", + "component-emitter": "1.2.1", + "debug": "~4.1.0", + "engine.io-client": "~3.4.0", + "has-binary2": "~1.0.2", + "has-cors": "1.1.0", + "indexof": "0.0.1", + "object-component": "0.0.3", + "parseqs": "0.0.5", + "parseuri": "0.0.5", + "socket.io-parser": "~3.3.0", + "to-array": "0.1.4" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "socket.io-parser": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.0.tgz", + "integrity": "sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng==", + "dev": true, + "requires": { + "component-emitter": "1.2.1", + "debug": "~3.1.0", + "isarray": "2.0.1" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + } + } + }, + "socket.io-parser": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.4.1.tgz", + "integrity": "sha512-11hMgzL+WCLWf1uFtHSNvliI++tcRUWdoeYuwIl+Axvwy9z2gQM+7nJyN3STj1tLj5JyIUH8/gpDGxzAlDdi0A==", + "dev": true, + "requires": { + "component-emitter": "1.2.1", + "debug": "~4.1.0", + "isarray": "2.0.1" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "dev": true, + "requires": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "dev": true + }, + "spawn-command": { + "version": "0.0.2-1", + "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz", + "integrity": "sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A=", + "dev": true + }, + "spdx-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/spdx-compare/-/spdx-compare-1.0.0.tgz", + "integrity": "sha512-C1mDZOX0hnu0ep9dfmuoi03+eOdDoz2yvK79RxbcrVEG1NO1Ph35yW102DHWKN4pk80nwCgeMmSY5L25VE4D9A==", + "dev": true, + "requires": { + "array-find-index": "^1.0.2", + "spdx-expression-parse": "^3.0.0", + "spdx-ranges": "^2.0.0" + } + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-expression-validate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-validate/-/spdx-expression-validate-2.0.0.tgz", + "integrity": "sha512-b3wydZLM+Tc6CFvaRDBOF9d76oGIHNCLYFeHbftFXUWjnfZWganmDmvtM5sm1cRwJc/VDBMLyGGrsLFd1vOxbg==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "dev": true + }, + "spdx-ranges": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/spdx-ranges/-/spdx-ranges-2.1.1.tgz", + "integrity": "sha512-mcdpQFV7UDAgLpXEE/jOMqvK4LBoO0uTQg0uvXUewmEFhpiZx5yJSZITHB8w1ZahKdhfZqP5GPEOKLyEq5p8XA==", + "dev": true + }, + "spdx-satisfies": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/spdx-satisfies/-/spdx-satisfies-5.0.0.tgz", + "integrity": "sha512-/hGhwh20BeGmkA+P/lm06RvXD94JduwNxtx/oX3B5ClPt1/u/m5MCaDNo1tV3Y9laLkQr/NRde63b9lLMhlNfw==", + "dev": true, + "requires": { + "spdx-compare": "^1.0.0", + "spdx-expression-parse": "^3.0.0", + "spdx-ranges": "^2.0.0" + } + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true + }, + "streamroller": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-2.2.4.tgz", + "integrity": "sha512-OG79qm3AujAM9ImoqgWEY1xG4HX+Lw+yY6qZj9R1K2mhF5bEmQ849wvrb+4vt4jLMLzwXttJlQbOdPOQVRv7DQ==", + "dev": true, + "requires": { + "date-format": "^2.1.0", + "debug": "^4.1.1", + "fs-extra": "^8.1.0" + }, + "dependencies": { + "date-format": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-2.1.0.tgz", + "integrity": "sha512-bYQuGLeFxhkxNOF3rcMtiZxvCBAquGzZm6oWA1oZ0g2THUzivaRhv8uOhdr19LmoobSOLoIAxeUK2RdbM8IFTA==", + "dev": true + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "streamsearch": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", + "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "terser": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz", + "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==", + "dev": true, + "requires": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + } + }, + "tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "requires": { + "rimraf": "^3.0.0" + } + }, + "to-array": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", + "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=", + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "dev": true + }, + "tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true + }, + "ts-node": { + "version": "8.10.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.10.2.tgz", + "integrity": "sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA==", + "dev": true, + "requires": { + "arg": "^4.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.17", + "yn": "3.1.1" + } + }, + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "dev": true + }, + "tslint": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.2.tgz", + "integrity": "sha512-UyNrLdK3E0fQG/xWNqAFAC5ugtFyPO4JJR1KyyfQAyzR8W0fTRrC91A8Wej4BntFzcvETdCSDa/4PnNYJQLYiA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "builtin-modules": "^1.1.1", + "chalk": "^2.3.0", + "commander": "^2.12.1", + "diff": "^4.0.1", + "glob": "^7.1.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.3", + "resolve": "^1.3.2", + "semver": "^5.3.0", + "tslib": "^1.10.0", + "tsutils": "^2.29.0" + } + }, + "tslint-config-prettier": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/tslint-config-prettier/-/tslint-config-prettier-1.18.0.tgz", + "integrity": "sha512-xPw9PgNPLG3iKRxmK7DWr+Ea/SzrvfHtjFt5LBl61gk2UBG/DB9kCXRjv+xyIU1rUtnayLeMUVJBcMX8Z17nDg==", + "dev": true + }, + "tslint-config-standard": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/tslint-config-standard/-/tslint-config-standard-9.0.0.tgz", + "integrity": "sha512-CAw9J743RnPMemQV/XQ4YyNreC+A1NItACfkm+cBedrOkz6CQfwlnbKn8anUXBfoa4Zo4tjAhblRbsMNcSLfSw==", + "dev": true, + "requires": { + "tslint-eslint-rules": "^5.3.1" + } + }, + "tslint-eslint-rules": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/tslint-eslint-rules/-/tslint-eslint-rules-5.4.0.tgz", + "integrity": "sha512-WlSXE+J2vY/VPgIcqQuijMQiel+UtmXS+4nvK4ZzlDiqBfXse8FAvkNnTcYhnQyOTW5KFM+uRRGXxYhFpuBc6w==", + "dev": true, + "requires": { + "doctrine": "0.7.2", + "tslib": "1.9.0", + "tsutils": "^3.0.0" + }, + "dependencies": { + "tslib": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.0.tgz", + "integrity": "sha512-f/qGG2tUkrISBlQZEjEqoZ3B2+npJjIf04H1wuAv9iA8i04Icp+61KRXxFdha22670NJopsZCIjhC3SnjPRKrQ==", + "dev": true + }, + "tsutils": { + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", + "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + } + } + }, + "tsutils": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", + "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "ttypescript": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/ttypescript/-/ttypescript-1.5.10.tgz", + "integrity": "sha512-Hk7TRej1hM+p+Fo+Pyb/XK9pe9CAt3Sh5n5YRutxFS8hUgkh2u1Vd2K40kMcNP3WYhiVFBMqXwM/2E8O95Ep6g==", + "dev": true, + "requires": { + "resolve": "^1.9.0" + } + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "typescript": { + "version": "3.9.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.6.tgz", + "integrity": "sha512-Pspx3oKAPJtjNwE92YS05HQoY7z2SFyOpHo9MqJor3BXAGNaPUs83CuVp9VISFkSjyRfiTpmKuAYGJB7S7hOxw==", + "dev": true + }, + "ua-parser-js": { + "version": "0.7.21", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.21.tgz", + "integrity": "sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ==", + "dev": true + }, + "union": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", + "integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==", + "dev": true, + "requires": { + "qs": "^6.4.0" + } + }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "url-join": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-2.0.5.tgz", + "integrity": "sha1-WvIvGMBSoACkjXuCxenC4v7tpyg=", + "dev": true + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true + }, + "void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", + "dev": true + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "ws": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz", + "integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==", + "dev": true + }, + "xmlhttprequest-ssl": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", + "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=", + "dev": true + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "yeast": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", + "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=", + "dev": true + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 000000000..25eeb9366 --- /dev/null +++ b/package.json @@ -0,0 +1,82 @@ +{ + "name": "@coderline/alphatab", + "version": "1.0.0", + "description": "alphaTab is a music notation and guitar tablature rendering library", + "keywords": [ + "guitar", + "music-notation", + "music-sheet", + "html5", + "svg", + "guitar-tablature" + ], + "homepage": "https://alphatab.net", + "bugs": { + "url": "https://github.com/coderline/alphaTab/issues" + }, + "author": "Daniel Kuschny", + "license": "MPL-2.0", + "repository": { + "type": "git", + "url": "https://github.com/coderline/alphaTab" + }, + "main": "dist/alphaTab.js", + "typings": "dist/alphaTab.d.ts", + "engines": { + "node": ">=6.0.0" + }, + "scripts": { + "clean": "rimraf dist", + "lint": "tslint --project tsconfig.build.json -t codeFrame 'src/**/*.ts' 'test/**/*.ts'", + "build": "ttsc --project tsconfig.build.json && rollup -c rollup.config.js", + "build-ci": "npm run clean && npm run build && npm pack", + "start": "node scripts/setup-playground.js && npm run build && concurrently --kill-others \"ttsc --project tsconfig.build.json --watch\" \"rollup -c rollup.config.js -w\" \"http-server -o playground/control.html \"", + "test": "ttsc --project tsconfig.json && concurrently --kill-others \"ttsc --project tsconfig.json -w\" \"karma start karma.conf.js --browsers Chrome --no-single-run --reporters spec,kjhtml\"", + "test-ci": "ttsc --project tsconfig.json && karma start karma.conf.js --browsers ChromeHeadless --single-run --reporters spec", + "generate-csharp": "ts-node --project tsconfig.build-csharp.json src.compiler/csharp/CSharpTranspiler.ts --project tsconfig.build-csharp.json", + "build-csharp": "npm run generate-csharp && cd src.csharp && dotnet build -c Release", + "build-csharp-ci": "npm run clean && npm run generate-csharp && cd src.csharp && dotnet build -c Release", + "test-csharp": "cd src.csharp && dotnet test", + "test-csharp-ci": "cd src.csharp && dotnet test" + }, + "devDependencies": { + "@rollup/plugin-commonjs": "^14.0.0", + "@types/jasmine": "^3.5.11", + "concurrently": "^5.2.0", + "cors": "^2.8.5", + "fs-extra": "^9.0.1", + "git-branch": "^2.0.1", + "http-server": "^0.12.3", + "karma": "^5.1.0", + "karma-chrome-launcher": "^3.1.0", + "karma-express-http-server": "0.0.1", + "karma-jasmine": "^3.3.1", + "karma-jasmine-html-reporter": "^1.5.4", + "karma-rollup-preprocessor": "^7.0.5", + "karma-spec-reporter": "0.0.32", + "lodash": "^4.17.19", + "multer": "^1.4.2", + "rimraf": "^3.0.2", + "rollup": "^2.21.0", + "rollup-plugin-copy": "^3.3.0", + "rollup-plugin-dts": "^1.4.8", + "rollup-plugin-license": "^2.1.0", + "rollup-plugin-terser": "^6.1.0", + "terser": "^4.8.0", + "ts-node": "^8.10.2", + "tslint": "^6.1.2", + "tslint-config-prettier": "^1.15.0", + "tslint-config-standard": "^9.0.0", + "ttypescript": "^1.5.10", + "typescript": "^3.9.6" + }, + "files": [ + "/dist/alphaTab.js", + "/dist/alphaTab.min.js", + "/dist/alphaTab.d.ts", + "/dist/font/Bravura.*", + "/dist/font/*.txt", + "/dist/soundfont/*", + "LICENSE.header" + ] +} diff --git a/playground-template/alphatex-editor.css b/playground-template/alphatex-editor.css new file mode 100644 index 000000000..b4eb55c73 --- /dev/null +++ b/playground-template/alphatex-editor.css @@ -0,0 +1,18 @@ +body { + padding: 30px; +} +.at-wrap { + height: 80vh; + margin: 0; +} +.editor-wrap { + height: 20vh; + position: relative; +} +.editor-wrap > * { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; +} \ No newline at end of file diff --git a/playground-template/alphatex-editor.html b/playground-template/alphatex-editor.html new file mode 100644 index 000000000..92b056cbe --- /dev/null +++ b/playground-template/alphatex-editor.html @@ -0,0 +1,98 @@ + + + + + AlphaTab alphaTex Editor Demo + + + + + + + + + + + + + + + + + + + +
    +
    +
    + \title "Canon Rock" + \subtitle "JerryC" + \tempo 90 + . + :2 19.2{v f} 17.2{v f} | + 15.2{v f} 14.2{v f}| + 12.2{v f} 10.2{v f}| + 12.2{v f} 14.2{v f}.4 :8 15.2 17.2 | + 14.1.2 :8 17.2 15.1 14.1{h} 17.2 | + 15.2{v d}.4 :16 17.2{h} 15.2 :8 14.2 14.1 17.1{b(0 4 4 0)}.4 | + 15.1.8 :16 14.1{tu 3} 15.1{tu 3} 14.1{tu 3} :8 17.2 15.1 14.1 :16 12.1{tu 3} 14.1{tu 3} 12.1{tu 3} :8 15.2 14.2 | + 12.2 14.3 12.3 15.2 :32 14.2{h} 15.2{h} 14.2{h} 15.2{h}14.2{h} 15.2{h}14.2{h} 15.2{h}14.2{h} 15.2{h}14.2{h} 15.2{h}14.2{h} 15.2{h}14.2{h} 15.2{h} +
    +
    + + + + + diff --git a/playground-template/alphatex-editor.js b/playground-template/alphatex-editor.js new file mode 100644 index 000000000..2db61de4e --- /dev/null +++ b/playground-template/alphatex-editor.js @@ -0,0 +1,18 @@ +function trimCode(code) { + return code + .trim() + .split(/\r?\n/) + .map(l => l.trimLeft()) + .join('\r\n'); +} + +function setupEditor(api, selector) { + const element = document.querySelector(selector); + element.innerHTML = trimCode(element.innerHTML); + const editor = ace.edit(element, { + mode: 'ace/mode/tex' + }); + editor.session.on('change', () => { + api.tex(editor.getSession().getDocument().getAllLines().join('\n')); + }); +} diff --git a/playground-template/control-template.html b/playground-template/control-template.html new file mode 100644 index 000000000..8b73db8a0 --- /dev/null +++ b/playground-template/control-template.html @@ -0,0 +1,165 @@ +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    + + \ No newline at end of file diff --git a/playground-template/control.css b/playground-template/control.css new file mode 100644 index 000000000..fcadcf10c --- /dev/null +++ b/playground-template/control.css @@ -0,0 +1,416 @@ +.at-cursor-bar { + /* Defines the color of the bar background when a bar is played */ + background: rgba(255, 242, 0, 0.25); +} + +.at-selection div { + /* Defines the color of the selection background */ + background: rgba(64, 64, 255, 0.2); +} + +.at-cursor-beat { + /* Defines the beat cursor */ + background: rgba(64, 64, 255, 0.75); + width: 3px; +} + +.at-highlight * { + /* Defines the color of the music symbols when they are being played (svg) */ + fill: #0078ff; + stroke: #0078ff; +} + +html, +body { + font-family: 'Roboto', sans-serif; + font-size: 14px; + height: 100vh; +} + +body { + display: flex; + align-content: stretch; + flex-direction: column; +} + +body > * { + overflow: hidden; +} + +.at-wrap { + position: relative; + width: 80%; + height: 80vh; + margin: 0 auto; + border: 1px solid rgba(0, 0, 0, 0.12); + background: #fff; + display: flex; + flex-direction: column; + overflow: hidden; +} + +.at-content { + flex: 1 1 auto; + overflow: hidden; + position: relative; +} + +.at-footer { + flex: 0 0 auto; + background: #436d9d; + color: #fff; +} + +.at-sidebar { + max-width: 70px; + width: auto; + display: flex; + align-content: stretch; + z-index: 1001; + position: absolute; + top: 0; + left: 0; + bottom: 0; + overflow: hidden; + border-right: 1px solid rgba(0, 0, 0, 0.12); + background: #f7f7f7; +} + +.at-sidebar:hover { + max-width: 400px; + transition: max-width 0.2s; + overflow-y: auto; +} + +.at-viewport { + overflow-y: auto; + position: absolute; + top: 0; + left: 70px; + right: 0; + bottom: 0; + padding-right: 20px; +} + +.at-song-title { + font-weight: 500; +} + +.at-track { + display: grid; + grid-template-columns: auto 1fr; + grid-template-rows: auto auto; + grid-template-areas: 'icon title' 'icon controls'; + padding: 5px; + transition: background 0.2s; + grid-gap: 5px; + cursor: pointer; +} + +.at-track:hover { + background: rgba(0, 0, 0, 0.1); +} + +.at-track > .at-track-icon { + grid-area: icon; + font-size: 32px; + display: flex; + justify-content: center; + align-items: center; + opacity: 0.5; + transition: opacity 0.2s; + width: 64px; + height: 64px; +} + +.at-track:hover > .at-track-icon { + opacity: 0.8; +} + +.at-track.active { + background: rgba(0, 0, 0, 0.03); +} + +.at-track.active > .at-track-icon { + color: #4972a1; + opacity: 1; +} + +.at-track > .at-track-name { + grid-area: title; + font-weight: 500; +} + +.at-track > .at-track-controls { + grid-area: controls; + display: flex; + align-items: center; +} + +.at-track > .at-track-controls > * { + margin: 0 2px; +} + +.at-track > .at-track-controls > button.active:hover { + background: transparent; +} + +input[type='range'] { + -webkit-appearance: none; + background: #d3d3d3; + outline: none; + opacity: 0.7; + transition: opacity 0.2s; + height: 5px; +} + +input[type='range']::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 16px; + height: 16px; + border-radius: 50%; + background: #4972a1; + cursor: pointer; +} + +input[type='range']::-moz-range-thumb { + width: 16px; + height: 16px; + border-radius: 50%; + background: #4972a1; + cursor: pointer; +} + +.at-overlay { + display: flex; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 1002; + backdrop-filter: blur(3px); + background: rgba(0, 0, 0, 0.5); + justify-content: center; + align-items: flex-start; + opacity: 0; + visibility: hidden; + transition: all 0.2s ease-in-out; + transition-delay: 0; +} + +.at-wrap.loading .at-overlay, +.at-wrap.dragging .at-overlay { + visibility: visible; + opacity: 1; + transition-delay: 0.2s; +} + +.at-overlay-content { + margin-top: 20px; + background: #fff; + box-shadow: 0px 5px 10px 0px rgba(0, 0, 0, 0.3); + padding: 10px; +} + +.at-overlay-content > .spinner-border { + color: #4972a1; +} + +.at-player { + display: flex; + justify-content: space-between; +} +.at-player > div { + padding: 3px; + display: flex; + justify-content: flex-start; + align-content: center; + align-items: center; +} +.at-player-left > *, +.at-player-right > * { + margin-right: 4px; +} + +.at-player-left > a.disabled, +.at-player-left > a.disabled:hover, +.at-player-left > a.disabled:active { + color: rgba(0, 0, 0, 0.3); +} + +.at-footer a { + color: #fff; + display: flex; + width: 40px; + height: 40px; + text-align: center; + box-sizing: content-box; + align-items: center; + justify-content: center; + transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, + box-shadow 0.15s ease-in-out; +} + +.at-footer a > i { + vertical-align: top; + font-size: 16px; +} + +.at-footer .btn { + padding: 4px; + color: #fff; + border-radius: 0; + height: 40px; +} + +.at-footer .btn:hover { + background: #5588c7; +} + +.at-footer a.active, +.at-footer a:hover { + background: #5588c7; + text-decoration: none; +} + +.at-footer .dropdown-menu { + box-shadow: 0 2px 4px -1px rgba(0, 0, 0, 0.2), 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12); + border: 0; + border-radius: 4px; + color: #fff; +} + +.at-footer .dropdown-item { + color: initial; + width: auto; + height: auto; + text-align: left; + justify-content: start; +} + +.at-footer .dropdown-item > i { + margin-right: 4px; +} + +.at-footer .dropdown-item:hover { + background: rgba(0, 0, 0, 0.04); +} + +.at-time-position { + font-weight: bold; +} + +.at-time-slider { + height: 4px; + background: #d9d9d9; +} + +.at-time-slider-value { + height: 4px; + background: #4972a1; + width: 0; +} + +.at-speed-value { + font-size: 0.8rem; + margin: 0 0.5em; +} + +/** Circular Progress Bar https://jsfiddle.net/bootstrapious/3xnomecr **/ +.progress { + width: 28px; + height: 28px; + background: none; + position: relative; +} + +.progress::after { + content: ''; + width: 100%; + height: 100%; + border-radius: 50%; + border: 3px solid #eee; + position: absolute; + top: 0; + left: 0; +} + +.progress > span { + width: 50%; + height: 100%; + overflow: hidden; + position: absolute; + top: 0; + z-index: 1; +} + +.progress .progress-left { + left: 0; +} + +.progress .progress-bar { + width: 100%; + height: 100%; + background: none; + position: absolute; + top: 0; + border: 3px solid #4972a1; +} + +.progress .progress-left .progress-bar { + left: 100%; + border-top-right-radius: 16px; + border-bottom-right-radius: 16px; + border-left: 0; + -webkit-transform-origin: center left; + transform-origin: center left; +} + +.progress .progress-right { + right: 0; +} + +.progress .progress-right .progress-bar { + left: -100%; + border-top-left-radius: 16px; + border-bottom-left-radius: 16px; + border-right: 0; + -webkit-transform-origin: center right; + transform-origin: center right; +} + +.progress .progress-value { + position: absolute; + top: 0; + left: 0; + font-size: 8px; +} + +@media screen and (max-width: 920px) { + .at-player-right > *:not(.at-logo) { + display: none !important; + } + + .at-wrap { + height: 60vh; + } +} + +@media screen and (max-width: 1100px) { + .at-footer * { + font-size: 12px !important; + } + .at-logo img { + height: 18px; + } + + .at-sidebar { + display: none; + } + + .at-viewport { + left: 0 !important; + } +} diff --git a/playground-template/control.html b/playground-template/control.html new file mode 100644 index 000000000..4ba418291 --- /dev/null +++ b/playground-template/control.html @@ -0,0 +1,56 @@ + + + + + AlphaTab Control Demo + + + + + + + + + + + + + + +
    + + + + diff --git a/playground-template/control.js b/playground-template/control.js new file mode 100644 index 000000000..45b320d7a --- /dev/null +++ b/playground-template/control.js @@ -0,0 +1,283 @@ +const toDomElement = (function () { + const parser = document.createElement('div'); + return function (html) { + parser.innerHTML = html; + return parser.firstElementChild; + }; +})(); + +function createTrackItem(track) { + const trackTemplate = Handlebars.compile(document.querySelector('#at-track-template').innerHTML); + const trackItem = toDomElement(trackTemplate(track)); + + // init track controls + const muteButton = trackItem.querySelector('.at-track-mute'); + const soloButton = trackItem.querySelector('.at-track-solo'); + const volumeSlider = trackItem.querySelector('.at-track-volume'); + + muteButton.onclick = function (e) { + e.stopPropagation(); + muteButton.classList.toggle('active'); + at.changeTrackMute([track], muteButton.classList.contains('active')); + }; + + soloButton.onclick = function (e) { + e.stopPropagation(); + soloButton.classList.toggle('active'); + at.changeTrackSolo([track], soloButton.classList.contains('active')); + }; + + volumeSlider.oninput = function (e) { + e.preventDefault(); + // Here we need to do some math to map the 1-16 slider to the + // volume in alphaTab. In alphaTab it is 1.0 for 100% which is + // equal to the volume in the track information + at.changeTrackVolume([track], volumeSlider.value / track.playbackInfo.volume); + }; + + volumeSlider.onclick = function (e) { + e.stopPropagation(); + }; + + trackItem.onclick = function (e) { + e.stopPropagation(); + at.renderTracks([track]); + }; + + muteButton.value = track.playbackInfo.isMute; + soloButton.value = track.playbackInfo.isSolo; + volumeSlider.value = track.playbackInfo.volume; + + trackItem.track = track; + return trackItem; +} + +function setupControl(selector) { + const el = document.querySelector(selector); + const control = el.closest('.at-wrap'); + + const viewPort = + 'playerScrollelement' in el.dataset ? el.dataset.playerScrollelement : control.querySelector('.at-viewport'); + const at = new alphaTab.AlphaTabApi(el, { + player: { + scrollElement: viewPort + } + }); + at.error.on(function(e) { + console.error('alphaTab error', e); + }); + + el.ondragover = function (e) { + e.stopPropagation(); + e.preventDefault(); + e.dataTransfer.dropEffect = 'link'; + }; + + el.ondrop = function (e) { + e.stopPropagation(); + e.preventDefault(); + const files = e.dataTransfer.files; + if (files.length === 1) { + const reader = new FileReader(); + reader.onload = function (data) { + at.load(data.target.result, [0]); + }; + reader.readAsArrayBuffer(files[0]); + } + console.log('drop', files); + }; + + const trackItems = []; + at.renderStarted.on(function (isResize) { + if (!isResize) { + control.classList.add('loading'); + } + const tracks = new Map(); + at.tracks.forEach(function (t) { + tracks.set(t.index, t); + }); + + trackItems.forEach(function (trackItem) { + if (tracks.has(trackItem.track.index)) { + trackItem.classList.add('active'); + } else { + trackItem.classList.remove('active'); + } + }); + }); + + const playerLoadingIndicator = control.querySelector('.at-player-loading'); + at.soundFontLoad.on(function (args) { + updateProgress(playerLoadingIndicator, args.loaded / args.total); + }); + at.soundFontLoaded.on(function () { + playerLoadingIndicator.classList.add('d-none'); + }); + at.renderFinished.on(function () { + control.classList.remove('loading'); + }); + + at.scoreLoaded.on(function (score) { + control.querySelector('.at-song-title').innerText = score.title; + control.querySelector('.at-song-artist').innerText = score.artist; + + // fill track selector + const trackList = control.querySelector('.at-track-list'); + trackList.innerHTML = ''; + + score.tracks.forEach(function (track) { + const trackItem = createTrackItem(track); + trackItems.push(trackItem); + trackList.appendChild(trackItem); + }); + + currentTempo = score.tempo; + }); + + let currentTempo = 0; + const timePositionLabel = control.querySelector('.at-time-position'); + const timeSliderValue = control.querySelector('.at-time-slider-value'); + + function formatDuration(milliseconds) { + let seconds = milliseconds / 1000; + const minutes = (seconds / 60) | 0; + seconds = (seconds - minutes * 60) | 0; + return String(minutes).padStart(2, '0') + ':' + String(seconds).padStart(2, '0'); + } + + let previousTime = -1; + at.playerPositionChanged.on(function (args) { + // reduce number of UI updates to second changes. + const currentSeconds = (args.currentTime / 1000) | 0; + if (currentSeconds == previousTime) { + return; + } + previousTime = currentSeconds; + + timePositionLabel.innerText = formatDuration(args.currentTime) + ' / ' + formatDuration(args.endTime); + timeSliderValue.style.width = ((args.currentTime / args.endTime) * 100).toFixed(2) + '%'; + }); + + const playPauseButton = control.querySelector('.at-play-pause'); + at.playerReady.on(function () { + control.querySelectorAll('.at-player .disabled').forEach(function (c) { + c.classList.remove('disabled'); + }); + }); + + at.playerStateChanged.on(function (args) { + const icon = playPauseButton.querySelector('i'); + if (args.state == 0) { + icon.classList.remove('fa-pause'); + icon.classList.add('fa-play'); + } else { + icon.classList.remove('fa-play'); + icon.classList.add('fa-pause'); + } + }); + + playPauseButton.onclick = function (e) { + e.stopPropagation(); + if (!e.target.classList.contains('disabled')) { + at.playPause(); + } + }; + + control.querySelector('.at-stop').onclick = function (e) { + e.stopPropagation(); + if (!e.target.classList.contains('disabled')) { + at.stop(); + } + }; + + control.querySelector('.at-metronome').onclick = function (e) { + e.stopPropagation(); + const link = e.target.closest('a'); + link.classList.toggle('active'); + if (link.classList.contains('active')) { + at.metronomeVolume = 1; + } else { + at.metronomeVolume = 0; + } + }; + + control.querySelectorAll('.at-speed-options a').forEach(function (a) { + a.onclick = function (e) { + e.preventDefault(); + at.playbackSpeed = parseFloat(e.target.innerText); + control.querySelector('.at-speed-label').innerText = e.target.innerText; + }; + }); + + control.querySelector('.at-loop').onclick = function (e) { + e.stopPropagation(); + const link = e.target.closest('a'); + link.classList.toggle('active'); + if (link.classList.contains('active')) { + at.isLooping = true; + } else { + at.isLooping = false; + } + }; + + control.querySelector('.at-print').onclick = function (e) { + at.print(); + }; + + control.querySelectorAll('.at-zoom-options a').forEach(function (a) { + a.onclick = function (e) { + e.preventDefault(); + at.settings.display.scale = parseInt(e.target.innerText) / 100.0; + control.querySelector('.at-zoom-label').innerText = e.target.innerText; + at.updateSettings(); + at.render(); + }; + }); + + control.querySelectorAll('.at-layout-options a').forEach(function (a) { + a.onclick = function (e) { + e.preventDefault(); + const settings = at.settings; + switch (e.target.dataset.layout) { + case 'page': + settings.display.layoutMode = alphaTab.LayoutMode.Page; + settings.player.scrollMode = alphaTab.ScrollMode.Continuous; + break; + case 'horizontal-bar': + settings.display.layoutMode = alphaTab.LayoutMode.Horizontal; + settings.player.scrollMode = alphaTab.ScrollMode.Continuous; + break; + case 'horizontal-screen': + settings.display.layoutMode = alphaTab.LayoutMode.Horizontal; + settings.player.scrollMode = alphaTab.ScrollMode.OffScreen; + break; + } + + at.updateSettings(); + at.render(); + }; + }); + + $(control).find('[data-toggle="tooltip"]').tooltip(); + + return at; +} + +function updateProgress(el, value) { + value = value * 100; + const left = el.querySelector('.progress-left .progress-bar'); + const right = el.querySelector('.progress-right .progress-bar'); + function percentageToDegrees(percentage) { + return (percentage / 100) * 360; + } + + if (value > 0) { + if (value <= 50) { + right.style.transform = 'rotate(' + percentageToDegrees(value) + 'deg)'; + } else { + right.style.transform = 'rotate(180deg)'; + left.style.transform = 'rotate(' + percentageToDegrees(value - 50) + 'deg)'; + } + } + el.querySelector('.progress-value-number').innerText = value | 0; +} diff --git a/playground-template/full-page.css b/playground-template/full-page.css new file mode 100644 index 000000000..8757d2a66 --- /dev/null +++ b/playground-template/full-page.css @@ -0,0 +1,32 @@ +body { + display: initial; +} + +body > * { + overflow: initial; +} + +.at-wrap { + height: auto; + overflow:visible; +} + +.at-content { + overflow:visible; +} + +.at-footer { + position: sticky; + width: 100%; + bottom: 30px; + left: 0; + z-index: 1002; +} + +.at-viewport { + position:initial; + top:initial; + margin-left:70px; + right:initial; + bottom:initial; +} \ No newline at end of file diff --git a/playground-template/full-page.html b/playground-template/full-page.html new file mode 100644 index 000000000..2a7df1531 --- /dev/null +++ b/playground-template/full-page.html @@ -0,0 +1,57 @@ + + + + + AlphaTab Full Page Demo + + + + + + + + + + + + + + + +
    + + + + diff --git a/playground-template/simple.html b/playground-template/simple.html new file mode 100644 index 000000000..448abd8e6 --- /dev/null +++ b/playground-template/simple.html @@ -0,0 +1,57 @@ + + + + + AlphaTab Simple Demo + + + + + + + + + + + + + +
    + + + diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 000000000..2da6199c8 --- /dev/null +++ b/rollup.config.js @@ -0,0 +1,83 @@ +const resolve = require('./rollup.resolve'); +const terser = require('rollup-plugin-terser').terser; +const dts = require('rollup-plugin-dts').default; +const copy = require('rollup-plugin-copy'); +const branch = require('git-branch'); +const license = require('rollup-plugin-license'); + +const commonOutput = { + name: 'alphaTab', + format: 'umd', + globals: { + jQuery: 'jQuery' + } +}; + +module.exports = [ + { + input: `dist/lib/alphatab.js`, + output: [ + { + file: 'dist/alphaTab.js', + name: 'alphaTab' + }, + { + file: 'dist/alphaTab.min.js', + name: 'alphaTab', + plugins: [terser()] + } + ].map(o => ({ ...o, ...commonOutput })), + external: [], + watch: { + include: 'dist/lib/**', + exclude: 'node_modules/**' + }, + plugins: [ + license({ + banner: { + content: { + file: 'LICENSE.header' + }, + data() { + let buildNumber = process.env.GITHUB_RUN_NUMBER || 0; + let gitBranch = branch.sync(); + return { + branch: gitBranch, + build: buildNumber + }; + } + } + }), + + copy({ + targets: [ + { src: 'font/bravura/*', dest: 'dist/font' }, + { src: 'font/sonivox/*', dest: 'dist/soundfont' } + ] + }), + resolve({ + mappings: { + '@src': 'dist/lib' + } + }) + ] + }, + { + input: 'dist/types/alphatab.d.ts', + output: [ + { + file: 'dist/alphaTab.d.ts', + format: 'es' + } + ], + plugins: [ + resolve({ + mappings: { + '@src': 'dist/types' + }, + types: true + }), + dts() + ] + } +]; diff --git a/rollup.resolve.js b/rollup.resolve.js new file mode 100644 index 000000000..c470c9d64 --- /dev/null +++ b/rollup.resolve.js @@ -0,0 +1,53 @@ +const join = require('path').join; +const glob = require('glob').sync; +const fs = require('fs'); + +module.exports = function (options) { + const mappings = options.mappings; + const types = options.types; + return { + name: 'resolve-typescript-paths', + resolveId: function (importee, importer) { + if (typeof importer === 'undefined' || importee.startsWith('\0')) { + return null; + } + + const extension = types ? '.d.ts' : '.js'; + + if (importee.startsWith('**')) { + return importee; + } else { + const match = Object.entries(mappings).filter(m => importee.startsWith(m[0])); + if (!match || match.length === 0) { + return null; + } + + if (match[0][1].endsWith(extension)) { + return join(process.cwd(), match[0][1]); + } + + let resolved = join(process.cwd(), match[0][1], importee.substring(match[0][0].length)); + if (fs.existsSync(join(resolved, 'index' + extension))) { + return join(resolved, 'index' + extension); + } + return resolved + extension; + } + }, + load(id) { + if (id.startsWith('**')) { + const files = glob(id, { + cwd: process.cwd() + }); + const source = files + .map( + (file, i) => + `import _${i} from ${JSON.stringify(join(process.cwd(), file))}; + export { _${i} };` + ) + .join('\r\n'); + return source; + } + return null; + } + }; +}; diff --git a/scripts/setup-playground.js b/scripts/setup-playground.js new file mode 100644 index 000000000..7351ef1f6 --- /dev/null +++ b/scripts/setup-playground.js @@ -0,0 +1,5 @@ +const fs = require('fs-extra'); + +const src = `${__dirname}/../playground-template` +const dest = `${__dirname}/../playground` +fs.copySync(src, dest); \ No newline at end of file diff --git a/scripts/update-csharp-version.js b/scripts/update-csharp-version.js new file mode 100644 index 000000000..33dc4adb6 --- /dev/null +++ b/scripts/update-csharp-version.js @@ -0,0 +1,32 @@ +const packageJsonFilePath = `${__dirname}/../package.json`; +const pkg = require(packageJsonFilePath); +const fs = require('fs'); + +let version = pkg.version.match(/([0-9]+\.[0-9]+\.[0-9]+)/)[0]; +let assemblyVersion = version; + +if(process.argv.length === 4) { + // update-csharp-version.js alpha 37 + // -> 1.0.0-alpha37 + // -> 1.0.0.37 + // -> 1.0.0.37 + version = `${version}-${process.argv[2]}.${process.argv[3]}`; + assemblyVersion = `${assemblyVersion}.${process.argv[3]}`; +} else if(process.argv.length === 3) { + // update-csharp-version.js 37 + // -> 1.0.0 + // -> 1.0.0.37 + // -> 1.0.0.37 + assemblyVersion = `${assemblyVersion}.${process.argv[2]}`; +} + +console.log(`Updating Version to ${version}`); +console.log(`Updating AssemblyVersion to ${assemblyVersion}`); + +const propsPath = `${__dirname}/../src.csharp/Directory.Build.props`; + +let propsSource = fs.readFileSync(propsPath, 'utf-8'); +propsSource = propsSource.replace(/[^<]+<\//g, `${version}[^<]+<\//g, `${assemblyVersion} "version": "1.0.0-alpha37", + version = `${version}-${process.argv[2]}.${process.argv[3]}`; + console.log(`Updating version to ${version}`); +} else { + console.log(`Keeping version on ${version}`); +} + +exec(`npm --no-git-tag-version version ${version}`) \ No newline at end of file diff --git a/src.compiler/JsonSerializationBuilder.ts b/src.compiler/JsonSerializationBuilder.ts new file mode 100644 index 000000000..e3436fdbf --- /dev/null +++ b/src.compiler/JsonSerializationBuilder.ts @@ -0,0 +1,940 @@ +import * as ts from 'typescript'; +interface JsonProperty { + property: ts.PropertyDeclaration; + jsonNames: string[]; +} + +function wrapToNonNull(isNullableType: boolean, expr: ts.Expression) { + return isNullableType ? expr : ts.createNonNullExpression(expr); +} + +function getTypeWithNullableInfo(checker: ts.TypeChecker, node: ts.TypeNode) { + let isNullable = false; + let type: ts.Type | null = null; + if (ts.isUnionTypeNode(node)) { + for (const t of node.types) { + if (t.kind === ts.SyntaxKind.NullKeyword) { + isNullable = true; + } else if (type !== null) { + throw new Error('Multi union types on JSON settings not supported!'); + } else { + type = checker.getTypeAtLocation(t); + } + } + } else { + type = checker.getTypeAtLocation(node); + } + + return { + isNullable, + type + }; +} + +function isPrimitiveType(type: ts.Type) { + if (hasFlag(type, ts.TypeFlags.Number)) { + return true; + } + if (hasFlag(type, ts.TypeFlags.String)) { + return true; + } + if (hasFlag(type, ts.TypeFlags.Boolean)) { + return true; + } + if (hasFlag(type, ts.TypeFlags.BigInt)) { + return true; + } + if (hasFlag(type, ts.TypeFlags.Unknown)) { + return true; + } + + return isEnumType(type); +} + +function isEnumType(type: ts.Type) { + // if for some reason this returns true... + if (hasFlag(type, ts.TypeFlags.Enum)) return true; + // it's not an enum type if it's an enum literal type + if (hasFlag(type, ts.TypeFlags.EnumLiteral) && !type.isUnion()) return false; + // get the symbol and check if its value declaration is an enum declaration + const symbol = type.getSymbol(); + if (!symbol) return false; + const { valueDeclaration } = symbol; + + return valueDeclaration && valueDeclaration.kind === ts.SyntaxKind.EnumDeclaration; +} + +function isTypedArray(type: ts.Type) { + return type.symbol.members.has(ts.escapeLeadingUnderscores('slice')); +} + +function hasFlag(type: ts.Type, flag: ts.TypeFlags): boolean { + return (type.flags & flag) === flag; +} + +function isImmutable(type: ts.Type): boolean { + const declaration = type.symbol.valueDeclaration; + if (declaration) { + return !!ts.getJSDocTags(declaration).find(t => t.tagName.text === 'json_immutable'); + } + + return false; +} + +function isMap(type: ts.Type): boolean { + return type.symbol.name === 'Map'; +} + +function generateToJsonBodyForClass( + classDeclaration: ts.ClassDeclaration, + propertiesToSerialize: JsonProperty[] +): ts.Block { + return ts.createBlock([ + // const json:any = {}; + ts.createVariableStatement( + [ts.createModifier(ts.SyntaxKind.ConstKeyword)], + [ + ts.createVariableDeclaration( + 'json', + ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword), + ts.createObjectLiteral() + ) + ] + ), + + // obj.fillToJson(json) + ts.createExpressionStatement( + ts.createCall( + ts.createPropertyAccess(ts.createIdentifier('obj'), 'fillToJson'), + [], + [ts.createIdentifier('json')] + ) + ), + + // return json; + ts.createReturn(ts.createIdentifier('json')) + ]); +} +function generateFillToJsonBodyForClass( + program: ts.Program, + classDeclaration: ts.ClassDeclaration, + propertiesToSerialize: JsonProperty[] +): ts.Block { + const statements: ts.Statement[] = []; + + for (let prop of propertiesToSerialize) { + const fieldName = (prop.property.name as ts.Identifier).text; + const jsonName = prop.jsonNames.filter(n => n !== '')[0]; + + const accessJsonName = function (): ts.Expression { + return ts.createPropertyAccess(ts.createIdentifier('json'), jsonName); + }; + const accessField = function (): ts.Expression { + return ts.createPropertyAccess(ts.createThis(), ts.createIdentifier(fieldName)); + }; + + const assignToJsonName = function (value: ts.Expression): ts.Statement { + return ts.createExpressionStatement(ts.createAssignment(accessJsonName(), value)); + }; + + if (jsonName) { + const type = getTypeWithNullableInfo(program.getTypeChecker(), prop.property.type); + if (isPrimitiveType(type.type)) { + // json.jsonName = this.fieldName + statements.push(assignToJsonName(accessField())); + } else if (isTypedArray(type.type)) { + // json.jsonName = this.fieldName ? this.fieldName.slice() : null + if (type.isNullable) { + statements.push( + assignToJsonName( + ts.createConditional( + accessField(), + ts.createToken(ts.SyntaxKind.QuestionToken), + ts.createCall(ts.createPropertyAccess(accessField(), 'slice'), [], []), + ts.createToken(ts.SyntaxKind.ColonToken), + ts.createNull() + ) + ) + ); + } else { + statements.push( + assignToJsonName(ts.createCall(ts.createPropertyAccess(accessField(), 'slice'), [], [])) + ); + } + } else if (isMap(type.type)) { + const mapType = type.type as ts.TypeReference; + if (!isEnumType(mapType.typeArguments[0]) || !isPrimitiveType(mapType.typeArguments[1])) { + throw new Error('only Map maps are supported extend if needed!'); + } + // json.jsonName = { } as any; + // this.fieldName.forEach((val, key) => (json.jsonName as any)[key] = val)) + statements.push( + assignToJsonName( + ts.createAsExpression( + ts.createObjectLiteral(), + ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword) + ) + ) + ); + statements.push( + ts.createExpressionStatement( + ts.createCall(ts.createPropertyAccess(accessField(), 'forEach'), undefined, [ + ts.createArrowFunction( + undefined, + undefined, + [ + ts.createParameter(undefined, undefined, undefined, '$mv'), + ts.createParameter(undefined, undefined, undefined, '$mk') + ], + undefined, + ts.createToken(ts.SyntaxKind.EqualsGreaterThanToken), + ts.createBlock([ + ts.createExpressionStatement( + ts.createAssignment( + ts.createElementAccess( + ts.createAsExpression( + accessJsonName(), + ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword) + ), + ts.createIdentifier('$mk') + ), + ts.createIdentifier('$mv') + ) + ) + ]) + ) + ]) + ) + ); + } else if (isImmutable(type.type)) { + // json.jsonName = TypeName.toJson(this.fieldName); + statements.push( + assignToJsonName( + wrapToNonNull( + type.isNullable, + ts.createCall( + ts.createPropertyAccess(ts.createIdentifier(type.type.symbol.name), 'toJson'), + [], + [accessField()] + ) + ) + ) + ); + } else { + // not nullable: + // if(json.jsonName) { + // this.fieldName.fillToJson(json.jsonName) + // } else { + // json.jsonName = TypeName.toJson(this.fieldName)!; + // } + + // nullable: + // if(json.jsonName) { + // if(this.fieldName) this.fieldName.fillToJson(json.jsonName) + // } else { + // json.jsonName = TypeName.toJson(this.fieldName); + // } + + // this.field.fillToJson(json.jsonName) + let fillToJsonStatent: ts.Statement = ts.createExpressionStatement( + ts.createCall( + // this.field.fillToJson + ts.createPropertyAccess(accessField(), 'fillToJson'), + [], + [accessJsonName()] + ) + ); + if (type.isNullable) { + fillToJsonStatent = ts.createIf(accessField(), fillToJsonStatent); + } + + statements.push( + ts.createIf( + // if(json.jsonName) + accessJsonName(), + // this.field.fillToJson(json.jsonName) + fillToJsonStatent, + // else json.jsonName = ... + assignToJsonName( + wrapToNonNull( + type.isNullable, + ts.createCall( + // TypeName.toJson + ts.createPropertyAccess(ts.createIdentifier(type.type.symbol.name), 'toJson'), + [], + [accessField()] + ) + ) + ) + ) + ); + } + } + } + + return ts.createBlock(statements); +} +function generateFromJsonBodyForClass( + classDeclaration: ts.ClassDeclaration, + propertiesToSerialize: JsonProperty[] +): ts.Block { + const statements: ts.Statement[] = []; + // if(!json) return null; + statements.push( + ts.createIf( + ts.createPrefix(ts.SyntaxKind.ExclamationToken, ts.createIdentifier('json')), + ts.createReturn(ts.createNull()) + ) + ); + + // const obj = new Type(); + statements.push( + ts.createVariableStatement( + [ts.createModifier(ts.SyntaxKind.ConstKeyword)], + [ + ts.createVariableDeclaration( + 'obj', + undefined, + ts.createNew(ts.createIdentifier(classDeclaration.name.text), [], []) + ) + ] + ) + ); + + // obj.fillFromJson(json); + statements.push( + ts.createExpressionStatement( + ts.createCall( + ts.createPropertyAccess(ts.createIdentifier('obj'), 'fillFromJson'), + [], + [ts.createIdentifier('json')] + ) + ) + ); + + // return obj; + statements.push( + // return json; + ts.createReturn(ts.createIdentifier('obj')) + ); + + return ts.createBlock(statements); +} +function generateFillFromJsonBodyForClass( + classDeclaration: ts.ClassDeclaration, + propertiesToSerialize: JsonProperty[] +): ts.Block { + return ts.createBlock([ + ts.createIf( + // if(json) for($k in json) { this.setProperty($k.toLowerCase(), json[$k]) } + ts.createIdentifier('json'), + ts.createForIn( + ts.createVariableDeclarationList([ts.createVariableDeclaration('$k')], ts.NodeFlags.Const), + ts.createIdentifier('json'), + ts.createExpressionStatement( + ts.createCall( + ts.createPropertyAccess(ts.createThis(), 'setProperty'), + [], + [ + // $k.toLowerCase(), + ts.createCall(ts.createPropertyAccess(ts.createIdentifier('$k'), 'toLowerCase'), [], []), + // json[$k] + ts.createElementAccess(ts.createIdentifier('json'), ts.createIdentifier('$k')) + ] + ) + ) + ) + ) + ]); +} + +function createEnumMapping(value: string, type: ts.Type): ts.Expression { + // isNan(parseInt(value)) ? Enum[Object.keys(Enum).find($k => $k.toLowerCase() === value.toLowerCase()] : parseInt(value) + return ts.createConditional( + ts.createCall(ts.createIdentifier('isNaN'), undefined, [ + ts.createCall(ts.createIdentifier('parseInt'), undefined, [ts.createIdentifier(value)]) + ]), + ts.createToken(ts.SyntaxKind.QuestionToken), + ts.createElementAccess( + ts.createIdentifier(type.symbol.name), + ts.createCall( + // Object.keys(EnumName).find + ts.createPropertyAccess( + // Object.keys(EnumName) + ts.createCall( + ts.createPropertyAccess(ts.createIdentifier('Object'), 'keys'), + [], + [ts.createIdentifier(type.symbol.name)] + ), + 'find' + ), + [], + [ + ts.createArrowFunction( + [], + [], + [ts.createParameter(undefined, undefined, undefined, '$k')], + undefined, + ts.createToken(ts.SyntaxKind.EqualsGreaterThanToken), + ts.createBinary( + ts.createCall( + // $.toLowerCase() + ts.createPropertyAccess(ts.createIdentifier('$k'), 'toLowerCase'), + [], + [] + ), + // === + ts.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken), + // value.toLowerCase() + ts.createCall( + // $.toLowerCase() + ts.createPropertyAccess(ts.createIdentifier(value), 'toLowerCase'), + [], + [] + ) + ) + ) + ] + ) + ), + ts.createToken(ts.SyntaxKind.ColonToken), + ts.createCall(ts.createIdentifier('parseInt'), undefined, [ts.createIdentifier(value)]) + ); +} + +function generateSetPropertyMethodBodyForClass( + program: ts.Program, + classDeclaration: ts.ClassDeclaration, + propertiesToSerialize: JsonProperty[] +): ts.Block { + const statements: ts.Statement[] = []; + const cases: ts.CaseOrDefaultClause[] = []; + + const typeChecker = program.getTypeChecker(); + for (const prop of propertiesToSerialize) { + const jsonNames = prop.jsonNames.map(j => j.toLowerCase()); + const caseValues: string[] = jsonNames.filter(j => j !== ''); + const fieldName = (prop.property.name as ts.Identifier).text; + + const caseStatements: ts.Statement[] = []; + + const type = getTypeWithNullableInfo(typeChecker, prop.property.type); + + const assignField = function (expr: ts.Expression): ts.Statement { + return ts.createExpressionStatement( + ts.createAssignment(ts.createPropertyAccess(ts.createThis(), fieldName), expr) + ); + }; + + if (isEnumType(type.type)) { + // this.fieldName = enummapping + // return true; + caseStatements.push(assignField(createEnumMapping('value', type.type))); + caseStatements.push(ts.createReturn(ts.createTrue())); + } else if (isPrimitiveType(type.type)) { + // this.fieldName = value + // return true; + caseStatements.push(assignField(ts.createIdentifier('value'))); + caseStatements.push(ts.createReturn(ts.createTrue())); + } else if (isTypedArray(type.type)) { + // nullable: + // this.fieldName = value ? value.slice() : null + // return true; + + // not nullable: + // this.fieldName = value.slice() + // return true; + + if (type.isNullable) { + caseStatements.push( + assignField( + ts.createConditional( + ts.createIdentifier('value'), + ts.createToken(ts.SyntaxKind.QuestionToken), + ts.createCall(ts.createPropertyAccess(ts.createIdentifier('value'), 'slice'), [], []), + ts.createToken(ts.SyntaxKind.ColonToken), + ts.createNull() + ) + ) + ); + } else { + caseStatements.push( + assignField(ts.createCall(ts.createPropertyAccess(ts.createIdentifier('value'), 'slice'), [], [])) + ); + } + + caseStatements.push(ts.createReturn(ts.createTrue())); + } else if (isMap(type.type)) { + // this.fieldName = new Map(); + // for(let key in value) { + // if(value.hasOwnProperty(key) this.fieldName.set(, value[key]); + // } + // return true; + + const mapType = type.type as ts.TypeReference; + if (!isEnumType(mapType.typeArguments[0]) || !isPrimitiveType(mapType.typeArguments[1])) { + throw new Error('only Map maps are supported extend if needed!'); + } + + caseStatements.push(assignField(ts.createNew(ts.createIdentifier('Map'), undefined, []))); + caseStatements.push( + ts.createForIn( + ts.createVariableDeclarationList( + [ts.createVariableDeclaration(ts.createIdentifier('$mk'), undefined, undefined)], + ts.NodeFlags.Let + ), + ts.createIdentifier('value'), + ts.createIf( + ts.createCall( + ts.createPropertyAccess(ts.createIdentifier('value'), 'hasOwnProperty'), + undefined, + [ts.createIdentifier('$mk')] + ), + ts.createExpressionStatement( + ts.createCall( + ts.createPropertyAccess( + ts.createPropertyAccess(ts.createThis(), ts.createIdentifier(fieldName)), + ts.createIdentifier('set') + ), + undefined, + [ + createEnumMapping('$mk', mapType.typeArguments![0]), + ts.createElementAccess(ts.createIdentifier('value'), ts.createIdentifier('$mk')) + ] + ) + ) + ) + ) + ); + caseStatements.push(ts.createReturn(ts.createTrue())); + } else if (isImmutable(type.type)) { + // this.fieldName = TypeName.fromJson(value)! + // return true; + caseStatements.push( + assignField( + wrapToNonNull( + type.isNullable, + ts.createCall( + // TypeName.fromJson + ts.createPropertyAccess(ts.createIdentifier(type.type.symbol.name), 'fromJson'), + [], + [ts.createIdentifier('value')] + ) + ) + ) + ); + caseStatements.push(ts.createReturn(ts.createTrue())); + } else { + // for complex types it is a bit more tricky + // if the property matches exactly, we use fromJson + // if the property starts with the field name, we try to set a sub-property + const jsonNameArray = ts.createArrayLiteral(jsonNames.map(n => ts.createStringLiteral(n))); + + statements.push( + ts.createIf( + // if(["", "core"].indexOf(property) >= 0) + ts.createBinary( + ts.createCall( + ts.createPropertyAccess(jsonNameArray, 'indexOf'), + [], + [ts.createIdentifier('property')] + ), + ts.SyntaxKind.GreaterThanEqualsToken, + ts.createNumericLiteral('0') + ), + ts.createBlock([ + // if(this.field) { + // this.field.fillFromJson(value); + // } else { + // this.field = TypeName.fromJson(value); + // } + // return true; + ts.createIf( + ts.createPropertyAccess(ts.createThis(), fieldName), + ts.createExpressionStatement( + ts.createCall( + ts.createPropertyAccess( + ts.createPropertyAccess(ts.createThis(), fieldName), + 'fillFromJson' + ), + [], + [ts.createIdentifier('value')] + ) + ), + assignField( + wrapToNonNull( + type.isNullable, + ts.createCall( + // TypeName.fromJson + ts.createPropertyAccess(ts.createIdentifier(type.type.symbol.name), 'fromJson'), + [], + [ts.createIdentifier('value')] + ) + ) + ) + ), + ts.createReturn(ts.createTrue()) + ]), + ts.createBlock([ + // for(const candidate of ["", "core"]) { + // if(candidate.indexOf(property) === 0) { + // if(!this.field) { this.field = new FieldType(); } + // if(this.field.setProperty(property.substring(candidate.length), value)) return true; + // } + // } + ts.createForOf( + undefined, + ts.createVariableDeclarationList([ts.createVariableDeclaration('$c')], ts.NodeFlags.Const), + jsonNameArray, + ts.createIf( + ts.createBinary( + ts.createCall( + ts.createPropertyAccess(ts.createIdentifier('property'), 'indexOf'), + [], + [ts.createIdentifier('$c')] + ), + ts.SyntaxKind.EqualsEqualsEqualsToken, + ts.createNumericLiteral('0') + ), + ts.createBlock([ + ts.createIf( + ts.createPrefix( + ts.SyntaxKind.ExclamationToken, + ts.createPropertyAccess(ts.createThis(), fieldName) + ), + assignField(ts.createNew(ts.createIdentifier(type.type.symbol.name), [], [])) + ), + ts.createIf( + ts.createCall( + ts.createPropertyAccess( + ts.createPropertyAccess(ts.createThis(), fieldName), + 'setProperty' + ), + [], + [ + ts.createCall( + ts.createPropertyAccess( + ts.createIdentifier('property'), + 'substring' + ), + [], + [ts.createPropertyAccess(ts.createIdentifier('$c'), 'length')] + ), + ts.createIdentifier('value') + ] + ), + ts.createReturn(ts.createTrue()) + ) + ]) + ) + ) + ]) + ) + ); + } + + if (caseStatements.length > 0) { + for (let i = 0; i < caseValues.length; i++) { + cases.push( + ts.createCaseClause( + ts.createStringLiteral(caseValues[i]), + // last case gets the statements, others are fall through + i < caseValues.length - 1 ? [] : caseStatements + ) + ); + } + } + } + + const switchExpr = ts.createSwitch(ts.createIdentifier('property'), ts.createCaseBlock(cases)); + statements.unshift(switchExpr); + statements.push(ts.createReturn(ts.createFalse())); + + return ts.createBlock(statements); +} + +function rewriteClassForJsonSerialization( + program: ts.Program, + classDeclaration: ts.ClassDeclaration, + sourceFile: ts.SourceFile +): ts.ClassDeclaration { + console.debug(`Rewriting ${classDeclaration.name.escapedText} for JSON serialization`); + let toJsonMethod: ts.MethodDeclaration = undefined; + let fromJsonMethod: ts.MethodDeclaration = undefined; + let fillToJsonMethod: ts.MethodDeclaration = undefined; + let fillFromJsonMethod: ts.MethodDeclaration = undefined; + let setPropertyMethod: ts.MethodDeclaration = undefined; + + let propertiesToSerialize: JsonProperty[] = []; + + var newMembers = []; + + // collect class state + classDeclaration.members.forEach(member => { + if (ts.isPropertyDeclaration(member)) { + const propertyDeclaration = member as ts.PropertyDeclaration; + if (!propertyDeclaration.modifiers.find(m => m.kind === ts.SyntaxKind.StaticKeyword)) { + const jsonNames = [member.name.getText(sourceFile)]; + + if (ts.getJSDocTags(member).find(t => t.tagName.text === 'json_on_parent')) { + jsonNames.push(''); + } + + propertiesToSerialize.push({ + property: propertyDeclaration, + jsonNames: jsonNames + }); + } + newMembers.push(member); + } else if (ts.isMethodDeclaration(member)) { + if (ts.isIdentifier(member.name)) { + const methodName = (member.name as ts.Identifier).escapedText; + switch (methodName) { + case 'toJson': + toJsonMethod = member; + break; + case 'fromJson': + fromJsonMethod = member; + break; + case 'fillToJson': + fillToJsonMethod = member; + break; + case 'fillFromJson': + fillFromJsonMethod = member; + break; + case 'setProperty': + setPropertyMethod = member; + break; + default: + newMembers.push(member); + break; + } + } + } else { + newMembers.push(member); + } + }); + + if (!toJsonMethod) { + toJsonMethod = ts.createMethod( + undefined, + [ts.createModifier(ts.SyntaxKind.PublicKeyword), ts.createModifier(ts.SyntaxKind.StaticKeyword)], + undefined, + 'toJson', + undefined, + undefined, + [ + ts.createParameter( + undefined, + undefined, + undefined, + 'obj', + undefined, + ts.createTypeReferenceNode(classDeclaration.name, []) + ) + ], + ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword), + undefined + ); + } + + if (!fillToJsonMethod) { + fillToJsonMethod = ts.createMethod( + undefined, + [ts.createModifier(ts.SyntaxKind.PublicKeyword)], + undefined, + 'fillToJson', + undefined, + undefined, + [ + ts.createParameter( + undefined, + undefined, + undefined, + 'json', + undefined, + ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword) + ) + ], + ts.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword), + ts.createBlock([ts.createThrow(ts.createStringLiteral('todo'))]) + ); + } + + if (!fromJsonMethod) { + fromJsonMethod = ts.createMethod( + undefined, + [ts.createModifier(ts.SyntaxKind.PublicKeyword), ts.createModifier(ts.SyntaxKind.StaticKeyword)], + undefined, + 'fromJson', + undefined, + undefined, + [ + ts.createParameter( + undefined, + undefined, + undefined, + 'json', + undefined, + ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword) + ) + ], + ts.createTypeReferenceNode(classDeclaration.name, []), + ts.createBlock([ts.createThrow(ts.createStringLiteral('todo'))]) + ); + } + + if (!fillFromJsonMethod) { + fillFromJsonMethod = ts.createMethod( + undefined, + [ts.createModifier(ts.SyntaxKind.PublicKeyword)], + undefined, + 'fillFromJson', + undefined, + undefined, + [ + ts.createParameter( + undefined, + undefined, + undefined, + 'json', + undefined, + ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword) + ) + ], + ts.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword), + ts.createBlock([ts.createThrow(ts.createStringLiteral('todo'))]) + ); + } + + if (!setPropertyMethod) { + setPropertyMethod = ts.createMethod( + undefined, + [ts.createModifier(ts.SyntaxKind.PublicKeyword)], + undefined, + 'setProperty', + undefined, + undefined, + [ + ts.createParameter( + undefined, + undefined, + undefined, + 'property', + undefined, + ts.createKeywordTypeNode(ts.SyntaxKind.StringKeyword) + ), + ts.createParameter( + undefined, + undefined, + undefined, + 'value', + undefined, + ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword) + ) + ], + ts.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword), + ts.createBlock([ts.createThrow(ts.createStringLiteral('todo'))]) + ); + } + + function updateMethodBody( + method: ts.MethodDeclaration, + paramNames: string[], + body: ts.Block + ): ts.MethodDeclaration { + if (!method) { + return; + } + const parameters = method.parameters.map((v, i) => + ts.createParameter( + method.parameters[i].decorators, + method.parameters[i].modifiers, + method.parameters[i].dotDotDotToken, + i < paramNames.length ? paramNames[i] : method.parameters[0].name, + method.parameters[i].questionToken, + method.parameters[i].type, + method.parameters[i].initializer + ) + ); + + return ts.updateMethod( + method, + method.decorators, + method.modifiers, + method.asteriskToken, + method.name, + method.questionToken, + method.typeParameters, + parameters, + method.type, + body + ); + } + + toJsonMethod = updateMethodBody( + toJsonMethod, + ['obj'], + generateToJsonBodyForClass(classDeclaration, propertiesToSerialize) + ); + + fillToJsonMethod = updateMethodBody( + fillToJsonMethod, + ['json'], + generateFillToJsonBodyForClass(program, classDeclaration, propertiesToSerialize) + ); + + fromJsonMethod = updateMethodBody( + fromJsonMethod, + ['json'], + generateFromJsonBodyForClass(classDeclaration, propertiesToSerialize) + ); + + fillFromJsonMethod = updateMethodBody( + fillFromJsonMethod, + ['json'], + generateFillFromJsonBodyForClass(classDeclaration, propertiesToSerialize) + ); + + setPropertyMethod = updateMethodBody( + setPropertyMethod, + ['property', 'value'], + generateSetPropertyMethodBodyForClass(program, classDeclaration, propertiesToSerialize) + ); + + newMembers.push(toJsonMethod); + newMembers.push(fillToJsonMethod); + newMembers.push(fromJsonMethod); + newMembers.push(fillFromJsonMethod); + newMembers.push(setPropertyMethod); + + console.debug(`Rewriting ${classDeclaration.name.escapedText} done`); + + return ts.updateClassDeclaration( + classDeclaration, + classDeclaration.decorators, + classDeclaration.modifiers, + classDeclaration.name, + classDeclaration.typeParameters, + classDeclaration.heritageClauses, + newMembers + ); +} + +export default function (program: ts.Program) { + return (ctx: ts.TransformationContext) => { + return (sourceFile: ts.SourceFile) => { + function visitor(node: ts.Node): ts.Node { + if (ts.isClassDeclaration(node)) { + if (ts.getJSDocTags(node).find(t => t.tagName.text === 'json')) { + return rewriteClassForJsonSerialization(program, node, sourceFile); + } + } + + return node; + } + + return ts.visitEachChild(sourceFile, visitor, ctx); + }; + }; +} diff --git a/src.compiler/csharp/CSharpAst.ts b/src.compiler/csharp/CSharpAst.ts new file mode 100644 index 000000000..7a2ab3c25 --- /dev/null +++ b/src.compiler/csharp/CSharpAst.ts @@ -0,0 +1,485 @@ +import * as ts from 'typescript'; + +// Base +export enum SyntaxKind { + SourceFile, + UsingDeclaration, + NamespaceDeclaration, + ClassDeclaration, + EnumDeclaration, + InterfaceDeclaration, + DelegateDeclaration, + TypeParameterDeclaration, + MethodDeclaration, + ConstructorDeclaration, + FieldDeclaration, + PropertyDeclaration, + EventDeclaration, + PropertyAccessorDeclaration, + ParameterDeclaration, + UnresolvedTypeNode, + TypeReference, + PrimitiveTypeNode, + EnumMember, + ArrayTypeNode, + + Block, + EmptyStatement, + VariableStatement, + ExpressionStatement, + IfStatement, + DoStatement, + WhileStatement, + ForStatement, + ForEachStatement, + BreakStatement, + ContinueStatement, + ReturnStatement, + SwitchStatement, + LabeledStatement, + ThrowStatement, + TryStatement, + + VariableDeclarationList, + VariableDeclaration, + CaseClause, + DefaultClause, + CatchClause, + + PrefixUnaryExpression, + PostfixUnaryExpression, + NullLiteral, + TrueLiteral, + FalseLiteral, + ThisLiteral, + BaseLiteralExpression, + StringLiteral, + AwaitExpression, + BinaryExpression, + ConditionalExpression, + LambdaExpression, + NumericLiteral, + StringTemplateExpression, + IsExpression, + ParenthesizedExpression, + ArrayCreationExpression, + MemberAccessExpression, + AnonymousObjectCreationExpression, + AnonymousObjectProperty, + ElementAccessExpression, + InvocationExpression, + NewExpression, + CastExpression, + NonNullExpression, + NullSafeExpression, + Identifier, + DefaultExpression, + ToDoExpression, + + Attribute +} + +export interface Node { + skipEmit?: boolean; + tsNode?: ts.Node; + tsSymbol?: ts.Symbol; + nodeType: SyntaxKind; + parent: Node | null; +} + +export interface SourceFile extends Node { + fileName: string; + usings: UsingDeclaration[]; + namespace: NamespaceDeclaration; +} + +export interface UsingDeclaration extends Node { + typeAlias?: string; + namespaceOrTypeName: string; +} + +export interface NamespaceDeclaration extends Node { + namespace: string; + declarations: NamespaceMember[]; +} + +type NamespaceMember = ClassDeclaration | EnumDeclaration | InterfaceDeclaration | DelegateDeclaration; + +export enum Visibility { + None, + Public, + Protected, + Private, + Internal +} + +export interface DocumentedElement { + documentation?: string; +} +export interface AttributedElement { + attributes?: Attribute[]; +} + +export interface Attribute extends Node { + type: TypeNode, + arguments?: Expression[] +} + +export interface NamedElement { + name: string; +} + +// Declarations + +export interface TypeParameterDeclaration extends NamedElement, Node { + constraint?: TypeNode; +} + +export interface NamedTypeDeclaration extends NamedElement, DocumentedElement, Node, AttributedElement { + typeParameters?: TypeParameterDeclaration[]; + visibility: Visibility; +} + +export interface ClassDeclaration extends NamedTypeDeclaration { + baseClass?: TypeNode; + interfaces?: TypeNode[]; + isAbstract: boolean; + partial: boolean; + members: ClassMember[]; +} + +export type ClassMember = + | ConstructorDeclaration + | MethodDeclaration + | FieldDeclaration + | PropertyDeclaration + | EventDeclaration + | NamedTypeDeclaration; + +export interface EnumDeclaration extends NamedTypeDeclaration { + members: EnumMember[]; +} + +export interface EnumMember extends Node, NamedElement, DocumentedElement { + initializer?: Expression; +} + +export interface InterfaceDeclaration extends NamedTypeDeclaration { + interfaces?: TypeNode[]; + members: InterfaceMember[]; +} + +export type InterfaceMember = MethodDeclaration | PropertyDeclaration | EventDeclaration; + +export interface MemberDeclaration extends NamedElement, DocumentedElement, Node { + visibility: Visibility; + isStatic: boolean; +} + +export interface MethodDeclarationBase extends MemberDeclaration { + parameters: ParameterDeclaration[]; + body?: Block | Expression; + isAsync?: boolean; +} + +export interface MethodDeclaration extends MethodDeclarationBase, AttributedElement { + isVirtual: boolean; + isOverride: boolean; + isAbstract: boolean; + returnType: TypeNode; + parameters: ParameterDeclaration[]; + body?: Block | Expression; + typeParameters?: TypeParameterDeclaration[]; +} + +export interface ConstructorDeclaration extends MethodDeclarationBase { + baseConstructorArguments?: Expression[]; +} + +export interface FieldDeclaration extends MemberDeclaration { + isReadonly: boolean; + type: TypeNode; + initializer?: Expression; +} + +export interface PropertyDeclaration extends MemberDeclaration { + isVirtual: boolean; + isOverride: boolean; + isAbstract: boolean; + + type: TypeNode; + getAccessor?: PropertyAccessorDeclaration; + setAccessor?: PropertyAccessorDeclaration; + initializer?: Expression; +} + +export interface PropertyAccessorDeclaration extends Node { + keyword: string; + body?: Block | Expression; +} + +export interface EventDeclaration extends MemberDeclaration { + eventType: TypeNode; +} + +export interface DelegateDeclaration extends NamedTypeDeclaration { + returnType: TypeNode; + parameters: ParameterDeclaration[]; +} + +export interface ParameterDeclaration extends NamedElement, Node, DocumentedElement { + type?: TypeNode; + initializer?: Expression; + params: boolean; +} + +// Type System + +export interface TypeNode extends Node { + isNullable?: boolean; + isOptional?: boolean; +} + +export interface UnresolvedTypeNode extends TypeNode { + tsType?: ts.Type; + tsSymbol?: ts.Symbol; + typeArguments?: UnresolvedTypeNode[]; +} + +export type TypeReferenceType = NamedTypeDeclaration | TypeParameterDeclaration | PrimitiveTypeNode | string; +export interface TypeReference extends TypeNode { + reference: TypeReferenceType; + typeArguments?: TypeNode[]; +} + +export interface ArrayTypeNode extends TypeNode { + elementType: TypeNode; +} + +export enum PrimitiveType { + Bool, + String, + Double, + Int, + Void, + Object, + Dynamic +} + +export interface PrimitiveTypeNode extends TypeNode { + type: PrimitiveType; +} + +// Expressions + +export interface Expression extends Node {} + +export interface PrefixUnaryExpression extends Node { + operand: Expression; + operator: string; +} + +export interface PostfixUnaryExpression extends Node { + operand: Expression; + operator: string; +} + +export interface NullLiteral extends Node {} + +export interface BooleanLiteral extends Node {} + +export interface ThisLiteral extends Node {} + +export interface BaseLiteralExpression extends Node {} + +export interface StringLiteral extends Node { + text: string; +} + +export interface AwaitExpression extends Node { + expression: Expression; +} + +export interface BinaryExpression extends Node { + left: Expression; + operator: string; + right: Expression; +} + +export interface ConditionalExpression extends Node { + condition: Expression; + whenTrue: Expression; + whenFalse: Expression; +} + +export interface LambdaExpression extends Node { + parameters: ParameterDeclaration[]; + body: Block | Expression; +} + +export interface NumericLiteral extends Node { + value: string; +} +export interface StringTemplateExpression extends Node { + chunks: (StringLiteral | Expression)[]; +} + +export interface IsExpression extends Node { + expression: Expression; + type: TypeNode; + newName?: string; +} +export interface ParenthesizedExpression extends Node { + expression: Expression; +} +export interface DefaultExpression extends Node { + type?: TypeNode; +} +export interface ArrayCreationExpression extends Node { + type?: TypeNode; + values?: Expression[]; + sizeExpression?: Expression; +} +export interface MemberAccessExpression extends Node { + expression: Expression; + member: string; + nullSafe?: boolean; +} + +export interface AnonymousObjectCreationExpression extends Node { + properties: AnonymousObjectProperty[]; +} + +export interface AnonymousObjectProperty extends Node { + name: string; + value: Expression; +} + +export interface ElementAccessExpression extends Node { + expression: Expression; + argumentExpression: Expression; +} + +export interface InvocationExpression extends Node { + expression: Expression; + arguments: Expression[]; + typeArguments?: TypeNode[]; +} + +export interface NewExpression extends Node { + type: TypeNode; + arguments: Expression[]; +} + +export interface CastExpression extends Node { + type: TypeNode; + expression: Expression; +} + +export interface NonNullExpression extends Node { + expression: Expression; +} + +export interface NullSafeExpression extends Node { + expression: Expression; +} + +export interface Identifier extends Expression { + text: string; +} + +export interface ToDoExpression extends Node {} + +// Statements + +export interface Statement extends Node {} + +export interface Block extends Statement { + statements: Statement[]; +} + +export interface EmptyStatement extends Statement {} + +export interface VariableStatement extends Statement { + declarationList: VariableDeclarationList; +} + +export interface ExpressionStatement extends Statement { + expression: Expression; +} + +export interface IfStatement extends Statement { + expression: Expression; + thenStatement: Statement; + elseStatement?: Statement; +} + +export interface DoStatement extends Statement { + expression: Expression; + statement: Statement; +} + +export interface WhileStatement extends Statement { + expression: Expression; + statement: Statement; +} + +export interface VariableDeclarationList extends Node { + declarations: VariableDeclaration[]; +} + +export interface VariableDeclaration extends Node { + type: TypeNode; + name: string; + initializer?: Expression; +} + +export interface ForStatement extends Statement { + initializer?: VariableDeclarationList | Expression; + condition?: Expression; + incrementor?: Expression; + statement: Statement; +} + +export interface ForEachStatement extends Statement { + initializer: VariableDeclarationList | Expression; + expression: Expression; + statement: Statement; +} + +export interface BreakStatement extends Statement {} + +export interface ContinueStatement extends Statement {} + +export interface ReturnStatement extends Statement { + expression?: Expression; +} + +export interface SwitchStatement extends Statement { + expression: Expression; + caseClauses: (CaseClause | DefaultClause)[]; +} + +export interface CaseClause extends Node { + expression: Expression; + statements: Statement[]; +} + +export interface DefaultClause extends Node { + statements: Statement[]; +} + +export interface ThrowStatement extends Statement { + expression?: Expression; +} + +export interface TryStatement extends Statement { + tryBlock: Block; + catchClauses?: CatchClause[]; + finallyBlock?: Block; +} + +export interface CatchClause extends Node { + variableDeclaration: VariableDeclaration; + block: Block; +} diff --git a/src.compiler/csharp/CSharpAstPrinter.ts b/src.compiler/csharp/CSharpAstPrinter.ts new file mode 100644 index 000000000..54b58c598 --- /dev/null +++ b/src.compiler/csharp/CSharpAstPrinter.ts @@ -0,0 +1,1293 @@ +import * as cs from './CSharpAst'; +import * as ts from 'typescript'; +import * as path from 'path'; +import * as fs from 'fs'; +import CSharpEmitterContext from './CSharpEmitterContext'; + +export default class CSharpAstPrinter { + private _sourceFile: cs.SourceFile; + private _fileHandle!: number; + private _isStartOfLine: boolean = true; + private _indent: number = 0; + private _context: CSharpEmitterContext; + + public diagnostics: ts.Diagnostic[] = []; + + public constructor(sourceFile: cs.SourceFile, context: CSharpEmitterContext) { + this._sourceFile = sourceFile; + this._context = context; + } + + public print() { + fs.mkdirSync(path.dirname(this._sourceFile.fileName), { recursive: true }); + this._fileHandle = fs.openSync(this._sourceFile.fileName, 'w'); + try { + this.writeSourceFile(this._sourceFile); + } finally { + fs.closeSync(this._fileHandle); + } + } + + private writeSourceFile(sourceFile: cs.SourceFile) { + this.writeLine('// '); + this.writeLine('// This code was auto-generated.'); + this.writeLine('// Changes to this file may cause incorrect behavior and will be lost if'); + this.writeLine('// the code is regenerated.'); + this.writeLine('// '); + this.writeLine('#nullable enable annotations'); + this.writeLine(); + for (const using of sourceFile.usings) { + this.writeUsing(using); + } + if (sourceFile.usings.length > 0) { + this.writeLine(); + } + this.writeNamespace(sourceFile.namespace); + } + + private writeNamespace(namespace: cs.NamespaceDeclaration) { + this.writeLine(`namespace ${namespace.namespace}`); + this.beginBlock(); + + for (const declaration of namespace.declarations) { + if (!declaration.skipEmit) { + switch (declaration.nodeType) { + case cs.SyntaxKind.ClassDeclaration: + this.writeClassDeclaration(declaration as cs.ClassDeclaration); + break; + case cs.SyntaxKind.EnumDeclaration: + this.writeEnumDeclaration(declaration as cs.EnumDeclaration); + break; + case cs.SyntaxKind.InterfaceDeclaration: + this.writeInterfaceDeclaration(declaration as cs.InterfaceDeclaration); + break; + case cs.SyntaxKind.DelegateDeclaration: + this.writeDelegateDeclaration(declaration as cs.DelegateDeclaration); + break; + } + } + } + + this.endBlock(); + } + + private writeDelegateDeclaration(d: cs.DelegateDeclaration) { + this.writeDocumentation(d); + this.writeVisibility(d.visibility); + this.writeType(d.returnType); + this.write(` ${d.name}`); + this.writeTypeParameters(d.typeParameters); + this.writeParameters(d.parameters); + this.writeLine(';'); + } + + private writeDocumentation(d: cs.DocumentedElement) { + if (d.documentation) { + this.writeLine('/// '); + this.writeDocumentationLines(d.documentation, true); + this.writeLine('/// '); + } + } + private writeDocumentationLines(documentation: string, multiLine: boolean) { + const lines = documentation.split('\n'); + if (lines.length > 1 || multiLine) { + if (!this._isStartOfLine) { + this.writeLine(); + } + lines.forEach(line => { + this.writeLine(`/// ${this.escapeXmlDoc(line)}`); + }); + } else if (lines.length === 1) { + if (this._isStartOfLine) { + this.writeLine(`/// ${this.escapeXmlDoc(lines[0])}`); + } else { + this.write(this.escapeXmlDoc(lines[0])); + } + } + } + private escapeXmlDoc(s: string): string { + return s.replace(/&/g, '&').replace(//g, '>'); + } + + private writeParameters(parameters: cs.ParameterDeclaration[]) { + this.write('('); + this.writeCommaSeparated(parameters, p => this.writeParameter(p)); + this.write(')'); + } + private writeCommaSeparated(values: T[], write: (p: T) => void) { + values.forEach((v, i) => { + if (i > 0) { + this.write(', '); + } + write(v); + }); + } + + private writeParameter(p: cs.ParameterDeclaration) { + if (p.params) { + this.write('params '); + } + if (p.type) { + this.writeType(p.type, false, p.params); + } + this.write(` ${p.name}`); + + if (p.initializer) { + this.write(' = '); + this.writeExpression(p.initializer); + } else if (p.type && p.type.isOptional) { + this.write(' = default'); + } + } + + private writeInterfaceDeclaration(d: cs.InterfaceDeclaration) { + this.writeDocumentation(d); + this.writeVisibility(d.visibility); + this.write(`interface ${d.name}`); + this.writeTypeParameters(d.typeParameters); + + if (d.interfaces && d.interfaces.length > 0) { + this.write(': '); + this.writeCommaSeparated(d.interfaces, i => this.writeType(i)); + } + + this.writeTypeParameterConstraints(d.typeParameters); + this.writeLine(); + this.beginBlock(); + + d.members.forEach(m => this.writeMember(m)); + + this.endBlock(); + } + + private writeEnumDeclaration(d: cs.EnumDeclaration) { + this.writeDocumentation(d); + this.writeVisibility(d.visibility); + this.write(`enum ${d.name}`); + this.writeLine(); + this.beginBlock(); + + d.members.forEach(m => this.writeEnumMember(m)); + + this.endBlock(); + } + private writeEnumMember(m: cs.EnumMember): void { + this.writeDocumentation(m); + this.write(m.name); + if (m.initializer) { + this.write(' = '); + this.writeExpression(m.initializer); + } + this.writeLine(','); + } + + private writeClassDeclaration(d: cs.ClassDeclaration) { + this.writeDocumentation(d); + this.writeAttributes(d); + this.writeVisibility(d.visibility); + + if (d.partial) { + this.write('partial '); + } + + if (d.isAbstract) { + this.write('abstract '); + } + + this.write(`class ${d.name}`); + this.writeTypeParameters(d.typeParameters); + + if (d.baseClass) { + this.write(': '); + this.writeType(d.baseClass); + } + + if (d.interfaces && d.interfaces.length > 0) { + if (d.baseClass) { + this.write(', '); + } else { + this.write(': '); + } + + this.writeCommaSeparated(d.interfaces, i => this.writeType(i)); + } + this.writeTypeParameterConstraints(d.typeParameters); + + this.writeLine(); + + this.beginBlock(); + + let hasConstuctor = false; + d.members.forEach(m => { + this.writeMember(m); + if (m.nodeType === cs.SyntaxKind.ConstructorDeclaration && !(m as cs.ConstructorDeclaration).isStatic) { + hasConstuctor = true; + } + }); + + if (d.baseClass && !hasConstuctor) { + let baseClass: cs.TypeReferenceType | undefined = d; + let constructorDeclaration: cs.ConstructorDeclaration | undefined = undefined; + while (baseClass && !constructorDeclaration) { + if (typeof baseClass === 'string') { + constructorDeclaration = undefined; + break; + } else if (baseClass.nodeType === cs.SyntaxKind.ClassDeclaration) { + const baseClassDeclaration = baseClass as cs.ClassDeclaration; + constructorDeclaration = baseClassDeclaration.members.find( + m => m.nodeType === cs.SyntaxKind.ConstructorDeclaration + ) as cs.ConstructorDeclaration; + if (constructorDeclaration) { + break; + } + + baseClass = + baseClassDeclaration.baseClass && + baseClassDeclaration.baseClass.nodeType === cs.SyntaxKind.TypeReference + ? (baseClassDeclaration.baseClass as cs.TypeReference).reference + : undefined; + } else { + constructorDeclaration = undefined; + break; + } + } + + if (constructorDeclaration) { + const defaultConstructor = { + parent: d, + name: '', + nodeType: cs.SyntaxKind.ConstructorDeclaration, + isStatic: false, + parameters: [], + visibility: cs.Visibility.Public, + body: { + parent: null, + nodeType: cs.SyntaxKind.Block, + statements: [], + tsNode: d.tsNode + } as cs.Block, + tsNode: d.tsNode, + baseConstructorArguments: [] + } as cs.ConstructorDeclaration; + defaultConstructor.body!.parent = defaultConstructor; + defaultConstructor.parameters = constructorDeclaration.parameters; + defaultConstructor.baseConstructorArguments = constructorDeclaration.parameters.map( + p => + ({ + parent: defaultConstructor, + nodeType: cs.SyntaxKind.Identifier, + text: p.name, + tsNode: defaultConstructor.tsNode + } as cs.Identifier) + ); + this.writeMember(defaultConstructor); + } + } + + this.endBlock(); + } + + public writeAttributes(d: cs.AttributedElement) { + if (d.attributes) { + d.attributes.forEach(a => this.writeAttribute(a)); + } + } + + public writeAttribute(a: cs.Attribute): void { + this.write('['); + this.writeType(a.type); + if (a.arguments && a.arguments.length > 0) { + this.write('('); + this.writeCommaSeparated(a.arguments!, x => this.writeExpression(x)); + this.write(')'); + } + this.writeLine(']'); + } + + private writeMember(member: cs.Node) { + if (member.skipEmit) { + return; + } + + switch (member.nodeType) { + case cs.SyntaxKind.FieldDeclaration: + this.writeFieldDeclarat1on(member as cs.FieldDeclaration); + break; + case cs.SyntaxKind.PropertyDeclaration: + this.writePropertyDeclaration(member as cs.PropertyDeclaration); + break; + case cs.SyntaxKind.ConstructorDeclaration: + this.writeConstructorDeclaration(member as cs.ConstructorDeclaration); + break; + case cs.SyntaxKind.MethodDeclaration: + this.writeMethodDeclaration(member as cs.MethodDeclaration); + break; + case cs.SyntaxKind.EventDeclaration: + this.writeEventDeclaration(member as cs.EventDeclaration); + break; + case cs.SyntaxKind.ClassDeclaration: + this.writeClassDeclaration(member as cs.ClassDeclaration); + break; + case cs.SyntaxKind.EnumDeclaration: + this.writeEnumDeclaration(member as cs.EnumDeclaration); + break; + case cs.SyntaxKind.InterfaceDeclaration: + this.writeInterfaceDeclaration(member as cs.InterfaceDeclaration); + break; + case cs.SyntaxKind.DelegateDeclaration: + this.writeDelegateDeclaration(member as cs.DelegateDeclaration); + break; + } + this.writeLine(); + } + + private writeEventDeclaration(d: cs.EventDeclaration) { + throw new Error('Method not implemented.'); + } + + private writeMethodDeclaration(d: cs.MethodDeclaration) { + this.writeDocumentation(d); + for (const p of d.parameters) { + if (p.documentation) { + this.write(`/// `); + this.writeDocumentationLines(p.documentation, false); + if (this._isStartOfLine) { + this.write('/// '); + } + this.writeLine(''); + } + } + + this.writeAttributes(d); + this.writeVisibility(d.visibility); + + if (d.isStatic) { + this.write('static '); + } + + if (d.isAsync) { + this.write('async '); + } + + if (d.isAbstract) { + this.write('abstract '); + } + + if (d.isVirtual) { + this.write('virtual '); + } + + if (d.isOverride) { + this.write('override '); + } + + if (d.isAsync) { + if ( + d.returnType.nodeType === cs.SyntaxKind.PrimitiveTypeNode && + (d.returnType as cs.PrimitiveTypeNode).type === cs.PrimitiveType.Void + ) { + this.write('System.Threading.Tasks.Task'); + } else { + this.write('System.Threading.Tasks.Task<'); + this.writeType(d.returnType); + this.write('>'); + } + } else { + this.writeType(d.returnType); + } + + this.write(` ${d.name}`); + this.writeTypeParameters(d.typeParameters); + this.writeParameters(d.parameters); + this.writeTypeParameterConstraints(d.typeParameters); + + this.writeBody(d.body); + } + + private writeTypeParameterConstraints(typeParameters: cs.TypeParameterDeclaration[] | undefined) { + if (typeParameters) { + this._indent++; + typeParameters.forEach(p => { + if (p.constraint) { + this.writeLine(); + this.write('where '); + this.write(p.name); + this.write(' : '); + this.writeType(p.constraint); + } + }); + this._indent--; + } + } + + private writeBody(body: cs.Expression | cs.Block | undefined) { + if (body) { + if (body.nodeType === cs.SyntaxKind.Block) { + this.writeLine(); + this.writeBlock(body as cs.Block); + } else { + this.write(' => '); + this.writeExpression(body as cs.Expression); + } + } else { + this.writeLine(';'); + } + } + + private writeConstructorDeclaration(d: cs.ConstructorDeclaration) { + this.writeDocumentation(d); + this.writeVisibility(d.visibility); + if (d.isStatic) { + this.write('static ') + } + this.write(`${(d.parent as cs.ClassDeclaration).name}`); + this.writeParameters(d.parameters); + + if (d.baseConstructorArguments) { + this.writeLine(); + this._indent++; + this.write(': base ('); + this.writeCommaSeparated(d.baseConstructorArguments, e => this.writeExpression(e)); + this.write(')'); + this._indent--; + } + + this.writeBody(d.body); + } + + private writePropertyDeclaration(d: cs.PropertyDeclaration) { + this.writeDocumentation(d); + this.writeVisibility(d.visibility); + + const writeAsField = this.writePropertyAsField(d); + + if (writeAsField && this.canBeConstant(d)) { + this.write('const '); + } else { + if (d.isStatic) { + this.write('static '); + } + + if (d.isAbstract) { + this.write('abstract '); + } + + if (d.isVirtual) { + this.write('virtual '); + } + + if (d.isOverride) { + this.write('override '); + } + } + + this.writeType(d.type); + this.write(` ${d.name}`); + + if (!writeAsField) { + this.writeLine(); + this.beginBlock(); + + if (d.getAccessor) { + this.writePropertyAccessor(d.getAccessor); + } + + if (d.setAccessor) { + this.writePropertyAccessor(d.setAccessor); + } + + this.endBlock(); + } + + if (d.initializer) { + this.write(' = '); + this.writeExpression(d.initializer); + this.writeLine(';'); + } else if (writeAsField) { + this.writeLine(';'); + } + } + private canBeConstant(d: cs.PropertyDeclaration): boolean { + return ( + d.isStatic && + !d.setAccessor && + d.type.nodeType === cs.SyntaxKind.PrimitiveTypeNode && + !!d.initializer && + (!d.getAccessor || !d.getAccessor.body) + ); + } + + private writePropertyAsField(d: cs.PropertyDeclaration) { + if (d.parent!.nodeType === cs.SyntaxKind.ClassDeclaration && d.visibility === cs.Visibility.Private && + (!d.getAccessor || !d.getAccessor.body)) { + return true; + } + return this.canBeConstant(d); + } + + private writePropertyAccessor(accessor: cs.PropertyAccessorDeclaration) { + this.write(accessor.keyword); + this.writeBody(accessor.body); + } + + private writeFieldDeclarat1on(d: cs.FieldDeclaration) { + this.writeDocumentation(d); + this.writeVisibility(d.visibility); + + if (this._context.isConst(d)) { + this.write('const '); + } else { + if (d.isStatic) { + this.write('static '); + } + + if (d.isReadonly) { + this.write('readonly '); + } + } + + this.writeType(d.type); + this.write(` ${d.name}`); + if (d.initializer) { + this.write(' = '); + this.writeExpression(d.initializer); + } + this.writeLine(';'); + } + + private writeType(type: cs.TypeNode, forNew: boolean = false, asNativeArray: boolean = false) { + if (!type) { + console.log('ERR'); + } + switch (type.nodeType) { + case cs.SyntaxKind.PrimitiveTypeNode: + switch ((type as cs.PrimitiveTypeNode).type) { + case cs.PrimitiveType.Bool: + this.write('bool'); + break; + case cs.PrimitiveType.Dynamic: + this.write('dynamic'); + break; + case cs.PrimitiveType.Double: + this.write('double'); + break; + case cs.PrimitiveType.Int: + this.write('int'); + break; + case cs.PrimitiveType.Object: + this.write('object'); + break; + case cs.PrimitiveType.String: + this.write('string'); + break; + case cs.PrimitiveType.Void: + this.write('void'); + break; + } + break; + case cs.SyntaxKind.ArrayTypeNode: + const arrayType = type as cs.ArrayTypeNode; + if (asNativeArray) { + this.writeType(arrayType.elementType); + this.write('[]'); + } else { + if (forNew) { + this.write('System.Collections.Generic.List<'); + } else { + this.write('System.Collections.Generic.IList<'); + } + this.writeType(arrayType.elementType); + this.write('>'); + } + + break; + case cs.SyntaxKind.TypeReference: + const typeReference = type as cs.TypeReference; + const targetType = (type as cs.TypeReference).reference; + if (typeof targetType === 'string') { + this.write(targetType); + } else { + this.writeType(targetType, forNew); + } + + if (typeReference.typeArguments && typeReference.typeArguments.length > 0) { + this.write('<'); + this.writeCommaSeparated(typeReference.typeArguments, p => this.writeType(p)); + this.write('>'); + } + break; + case cs.SyntaxKind.ClassDeclaration: + case cs.SyntaxKind.InterfaceDeclaration: + case cs.SyntaxKind.EnumDeclaration: + case cs.SyntaxKind.DelegateDeclaration: + this.write(this._context.getFullName(type as cs.NamedTypeDeclaration)); + break; + case cs.SyntaxKind.TypeParameterDeclaration: + this.write((type as cs.TypeParameterDeclaration).name); + break; + case cs.SyntaxKind.EnumMember: + this.write(this._context.getFullName((type as cs.EnumMember).parent as cs.NamedTypeDeclaration)); + break; + default: + this.write('TODO: ' + cs.SyntaxKind[type.nodeType]); + break; + } + if (type.isNullable && !forNew) { + this.write('?'); + } + } + + private writeTypeParameters(typeParameters: cs.TypeParameterDeclaration[] | undefined) { + if (typeParameters && typeParameters.length > 0) { + this.write('<'); + typeParameters.forEach((p, i) => { + if (i > 0) { + this.write(', '); + } + this.writeTypeParameter(p); + }); + this.write('>'); + } + } + + private writeTypeParameter(p: cs.TypeParameterDeclaration) { + this.write(p.name); + } + + private writeExpression(expr: cs.Expression) { + switch (expr.nodeType) { + case cs.SyntaxKind.PrefixUnaryExpression: + this.writePrefixUnaryExpression(expr as cs.PrefixUnaryExpression); + break; + case cs.SyntaxKind.PostfixUnaryExpression: + this.writePostfixUnaryExpression(expr as cs.PostfixUnaryExpression); + break; + case cs.SyntaxKind.NullLiteral: + this.writeNullLiteral(expr as cs.NullLiteral); + break; + case cs.SyntaxKind.FalseLiteral: + case cs.SyntaxKind.TrueLiteral: + this.writeBooleanLiteral(expr as cs.BooleanLiteral); + break; + case cs.SyntaxKind.ThisLiteral: + this.writeThisLiteral(expr as cs.ThisLiteral); + break; + case cs.SyntaxKind.BaseLiteralExpression: + this.writeBaseLiteralExpression(expr as cs.BaseLiteralExpression); + break; + case cs.SyntaxKind.StringLiteral: + this.writeStringLiteral(expr as cs.StringLiteral); + break; + case cs.SyntaxKind.AwaitExpression: + this.writeAwaitExpression(expr as cs.AwaitExpression); + break; + case cs.SyntaxKind.BinaryExpression: + this.writeBinaryExpression(expr as cs.BinaryExpression); + break; + case cs.SyntaxKind.ConditionalExpression: + this.writeConditionalExpression(expr as cs.ConditionalExpression); + break; + case cs.SyntaxKind.LambdaExpression: + this.writeLambdaExpression(expr as cs.LambdaExpression); + break; + case cs.SyntaxKind.NumericLiteral: + this.writeNumericLiteral(expr as cs.NumericLiteral); + break; + case cs.SyntaxKind.StringTemplateExpression: + this.writeStringTemplateExpression(expr as cs.StringTemplateExpression); + break; + case cs.SyntaxKind.IsExpression: + this.writeIsExpression(expr as cs.IsExpression); + break; + case cs.SyntaxKind.ParenthesizedExpression: + this.writeParenthesizedExpression(expr as cs.ParenthesizedExpression); + break; + case cs.SyntaxKind.ArrayCreationExpression: + this.writeArrayCreationExpression(expr as cs.ArrayCreationExpression); + break; + case cs.SyntaxKind.MemberAccessExpression: + this.writeMemberAccessExpression(expr as cs.MemberAccessExpression); + break; + case cs.SyntaxKind.AnonymousObjectCreationExpression: + this.writeAnonymousObjectCreationExpression(expr as cs.AnonymousObjectCreationExpression); + break; + case cs.SyntaxKind.AnonymousObjectProperty: + this.writeAnonymousObjectProperty(expr as cs.AnonymousObjectProperty); + break; + case cs.SyntaxKind.ElementAccessExpression: + this.writeElementAccessExpression(expr as cs.ElementAccessExpression); + break; + case cs.SyntaxKind.InvocationExpression: + this.writeInvocationExpression(expr as cs.InvocationExpression); + break; + case cs.SyntaxKind.NewExpression: + this.writeNewExpression(expr as cs.NewExpression); + break; + case cs.SyntaxKind.CastExpression: + this.writeCastExpression(expr as cs.CastExpression); + break; + case cs.SyntaxKind.NonNullExpression: + this.writeNonNullExpression(expr as cs.NonNullExpression); + break; + case cs.SyntaxKind.NullSafeExpression: + this.writeNullSafeExpression(expr as cs.NullSafeExpression); + break; + case cs.SyntaxKind.Identifier: + this.writeIdentifier(expr as cs.Identifier); + break; + case cs.SyntaxKind.ToDoExpression: + this.writeToDoExpression(expr as cs.ToDoExpression); + break; + case cs.SyntaxKind.DefaultExpression: + this.writeDefaultExpression(expr as cs.DefaultExpression); + break; + default: + throw new Error(`Unhandled expression type: ${cs.SyntaxKind[expr.nodeType]}`); + } + } + private writeDefaultExpression(expr: cs.DefaultExpression) { + this.write('default'); + if (expr.type) { + this.write('('); + this.writeType(expr.type); + this.write(')'); + } + } + + private writePrefixUnaryExpression(expr: cs.PrefixUnaryExpression) { + this.write(expr.operator); + this.writeExpression(expr.operand); + } + + private writePostfixUnaryExpression(expr: cs.PostfixUnaryExpression) { + this.writeExpression(expr.operand); + this.write(expr.operator); + } + + private writeNullLiteral(expr: cs.NullLiteral) { + this.write('null'); + } + + private writeBooleanLiteral(expr: cs.BooleanLiteral) { + this.write(expr.nodeType === cs.SyntaxKind.TrueLiteral ? 'true' : 'false'); + } + + private writeThisLiteral(expr: cs.ThisLiteral) { + this.write('this'); + } + + private writeBaseLiteralExpression(expr: cs.BaseLiteralExpression) { + this.write('base'); + } + + private writeStringLiteral(expr: cs.StringLiteral) { + this.write(JSON.stringify(expr.text)); + } + + private writeAwaitExpression(expr: cs.AwaitExpression) { + this.write('await '); + this.writeExpression(expr.expression); + } + + private writeBinaryExpression(expr: cs.BinaryExpression) { + this.writeExpression(expr.left); + this.write(' '); + this.write(expr.operator); + this.write(' '); + this.writeExpression(expr.right); + } + + private writeConditionalExpression(expr: cs.ConditionalExpression) { + this.writeExpression(expr.condition); + this.write(' ? '); + this.writeExpression(expr.whenTrue); + this.write(' : '); + this.writeExpression(expr.whenFalse); + } + + private writeLambdaExpression(expr: cs.LambdaExpression) { + this.write('('); + this.writeCommaSeparated(expr.parameters, p => this.writeParameter(p)); + this.write(') => '); + if (expr.body.nodeType === cs.SyntaxKind.Block) { + this.writeBlock(expr.body as cs.Block); + } else { + this.writeExpression(expr.body); + } + } + + private writeNumericLiteral(expr: cs.NumericLiteral) { + this.write(expr.value); + } + + private writeStringTemplateExpression(expr: cs.StringTemplateExpression) { + this.write('$@"'); + expr.chunks.forEach(c => { + if (c.nodeType === cs.SyntaxKind.StringLiteral) { + const escapedText = (c as cs.StringLiteral).text + .split('"') + .join('""') + .split('{') + .join('{{') + .split('}') + .join('}}'); + this.write(escapedText); + } else { + this.write('{'); + this.writeExpression(c as cs.Expression); + this.write('}'); + } + }); + this.write('"'); + } + + private writeIsExpression(expr: cs.IsExpression) { + this.writeExpression(expr.expression); + this.write(' is '); + this.writeType(expr.type); + } + + private writeParenthesizedExpression(expr: cs.ParenthesizedExpression) { + this.write('('); + this.writeExpression(expr.expression); + this.write(')'); + } + + private writeArrayCreationExpression(expr: cs.ArrayCreationExpression) { + if (expr.type) { + this.write('new '); + this.writeType(expr.type, true); + if (expr.values) { + if (expr.values.length > 0) { + this.writeLine('{'); + this._indent++; + this.writeCommaSeparated(expr.values, v => { + if (expr.values!.length > 10) { + this.writeLine(); + } + this.writeExpression(v); + }); + this._indent--; + this.writeLine('}'); + } else { + this.writeLine('()'); + } + } else { + this.write('['); + this.writeExpression(expr.sizeExpression!); + this.write(']'); + } + } + else if (expr.values && expr.values.length > 0) { + this.write('AlphaTab.Core.TypeHelper.CreateList('); + this.writeCommaSeparated(expr.values, v => { + if (expr.values!.length > 10) { + this.writeLine(); + } + this.writeExpression(v); + }); + this.write(')'); + } else { + this._context.addCsNodeDiagnostics(expr, 'Unknown array type', ts.DiagnosticCategory.Error); + } + } + + private writeMemberAccessExpression(expr: cs.MemberAccessExpression) { + this.writeExpression(expr.expression); + this.write(expr.nullSafe ? '?.' : '.'); + const name = this._context.getSymbolName(expr) ?? expr.member; + this.write(name); + } + + private writeAnonymousObjectCreationExpression(expr: cs.AnonymousObjectCreationExpression) { + this.write('new'); + this.beginBlock(); + + expr.properties.forEach(p => this.writeAnonymousObjectProperty(p)); + + this.endBlock(); + } + + private writeAnonymousObjectProperty(expr: cs.AnonymousObjectProperty) { + this.write(expr.name); + this.write(' = '); + this.writeExpression(expr.value); + this.writeLine(','); + } + + private writeElementAccessExpression(expr: cs.ElementAccessExpression) { + this.writeExpression(expr.expression); + this.write('['); + this.writeExpression(expr.argumentExpression); + this.write(']'); + } + + private writeInvocationExpression(expr: cs.InvocationExpression) { + this.writeExpression(expr.expression); + if (expr.typeArguments) { + this.write('<'); + this.writeCommaSeparated(expr.typeArguments, t => this.writeType(t)); + this.write('>'); + } + this.write('('); + this.writeCommaSeparated(expr.arguments, a => this.writeExpression(a)); + this.write(')'); + } + + private writeNewExpression(expr: cs.NewExpression) { + this.write('new '); + this.writeType(expr.type, true); + this.write('('); + this.writeCommaSeparated(expr.arguments, a => this.writeExpression(a)); + this.write(')'); + } + + private writeCastExpression(expr: cs.CastExpression) { + this.write('('); + this.writeType(expr.type); + this.write(')'); + this.writeExpression(expr.expression); + } + + private writeNonNullExpression(expr: cs.NonNullExpression) { + this.writeExpression(expr.expression); + this.write('!'); + } + + private writeNullSafeExpression(expr: cs.NullSafeExpression) { + this.writeExpression(expr.expression); + this.write('?'); + } + + private writeIdentifier(expr: cs.Identifier) { + const name = this._context.getSymbolName(expr) ?? expr.text; + this.write(name); + } + + private writeToDoExpression(expr: cs.ToDoExpression) { + this.write('/* TODO */'); + } + + private writeStatement(s: cs.Statement) { + switch (s.nodeType) { + case cs.SyntaxKind.EmptyStatement: + this.writeEmptyStatement(s as cs.EmptyStatement); + break; + case cs.SyntaxKind.Block: + this.writeBlock(s as cs.Block); + break; + case cs.SyntaxKind.VariableStatement: + this.writeVariableStatement(s as cs.VariableStatement); + break; + case cs.SyntaxKind.ExpressionStatement: + this.writeExpressionStatement(s as cs.ExpressionStatement); + break; + case cs.SyntaxKind.IfStatement: + this.writeIfStatement(s as cs.IfStatement); + break; + case cs.SyntaxKind.DoStatement: + this.writeDoStatement(s as cs.DoStatement); + break; + case cs.SyntaxKind.WhileStatement: + this.writeWhileStatement(s as cs.WhileStatement); + break; + case cs.SyntaxKind.ForStatement: + this.writeForStatement(s as cs.ForStatement); + break; + case cs.SyntaxKind.ForEachStatement: + this.writeForEachStatement(s as cs.ForEachStatement); + break; + case cs.SyntaxKind.BreakStatement: + this.writeBreakStatement(s as cs.BreakStatement); + break; + case cs.SyntaxKind.ContinueStatement: + this.writeContinueStatement(s as cs.ContinueStatement); + break; + case cs.SyntaxKind.ReturnStatement: + this.writeReturnStatement(s as cs.ReturnStatement); + break; + case cs.SyntaxKind.SwitchStatement: + this.writeSwitchStatement(s as cs.SwitchStatement); + break; + case cs.SyntaxKind.ThrowStatement: + this.writeThrowStatement(s as cs.ThrowStatement); + break; + case cs.SyntaxKind.TryStatement: + this.writeTryStatement(s as cs.TryStatement); + break; + } + } + + private writeTryStatement(s: cs.TryStatement) { + this.writeLine('try'); + this.writeBlock(s.tryBlock); + if (s.catchClauses) { + s.catchClauses.forEach(c => this.writeCatchClause(c)); + } + if (s.finallyBlock) { + this.writeLine('finally'); + this.writeBlock(s.finallyBlock); + } + } + + private writeCatchClause(c: cs.CatchClause): void { + this.write('catch ('); + this.writeType(c.variableDeclaration.type); + this.write(' '); + this.write(c.variableDeclaration.name); + this.writeLine(')'); + this.writeBlock(c.block); + } + + private writeThrowStatement(s: cs.ThrowStatement) { + this.write('throw'); + if (s.expression) { + this.write(' '); + this.writeExpression(s.expression); + } + this.writeLine(';'); + } + + private writeSwitchStatement(s: cs.SwitchStatement) { + this.write('switch ('); + this.writeExpression(s.expression); + this.writeLine(')'); + this.beginBlock(); + + s.caseClauses.forEach(c => { + if (c.nodeType === cs.SyntaxKind.DefaultClause) { + this.writeDefaultClause(c as cs.DefaultClause); + } else { + this.writeCaseClause(c as cs.CaseClause); + } + }); + + this.endBlock(); + } + + private writeCaseClause(c: cs.CaseClause) { + this.write('case '); + this.writeExpression(c.expression); + this.writeLine(':'); + this._indent++; + c.statements.forEach(s => this.writeStatement(s)); + this._indent--; + } + + private writeDefaultClause(c: cs.DefaultClause) { + this.writeLine('default:'); + this._indent++; + c.statements.forEach(s => this.writeStatement(s)); + this._indent--; + } + + private writeReturnStatement(r: cs.ReturnStatement) { + this.write('return'); + if (r.expression) { + this.write(' '); + this.writeExpression(r.expression); + } + this.writeLine(';'); + } + + private writeContinueStatement(_: cs.ContinueStatement) { + this.writeLine('continue;'); + } + + private writeBreakStatement(_: cs.BreakStatement) { + this.writeLine('break;'); + } + + private writeForEachStatement(s: cs.ForEachStatement) { + this.write('foreach ('); + if (s.initializer.nodeType === cs.SyntaxKind.VariableDeclarationList) { + this.writeVariableDeclarationList(s.initializer as cs.VariableDeclarationList); + } else { + this.writeExpression(s.initializer as cs.Expression); + } + this.write(' in '); + this.writeExpression(s.expression); + this.writeLine(')'); + + if (s.statement.nodeType === cs.SyntaxKind.Block) { + this.writeStatement(s.statement); + } else { + this._indent++; + this.writeStatement(s.statement); + this._indent--; + } + } + + private writeForStatement(s: cs.ForStatement) { + this.write('for ('); + if (s.initializer) { + if (s.initializer.nodeType === cs.SyntaxKind.VariableDeclarationList) { + this.writeVariableDeclarationList(s.initializer as cs.VariableDeclarationList); + } else { + this.writeExpression(s.initializer as cs.Expression); + } + } + this.write(';'); + + if (s.condition) { + this.writeExpression(s.condition); + } + this.write(';'); + + if (s.incrementor) { + this.writeExpression(s.incrementor); + } + this.writeLine(')'); + + if (s.statement.nodeType === cs.SyntaxKind.Block) { + this.writeStatement(s.statement); + } else { + this._indent++; + this.writeStatement(s.statement); + this._indent--; + } + } + + private writeWhileStatement(s: cs.WhileStatement) { + this.write('while ('); + this.writeExpression(s.expression); + this.writeLine(')'); + if (s.statement.nodeType === cs.SyntaxKind.Block) { + this.writeStatement(s.statement); + } else { + this._indent++; + this.writeStatement(s.statement); + this._indent--; + } + } + + private writeDoStatement(s: cs.DoStatement) { + this.writeLine('do'); + this.writeStatement(s.statement); + this.write('while ('); + this.writeExpression(s.expression); + this.writeLine(');'); + } + + private writeIfStatement(s: cs.IfStatement) { + this.write('if ('); + this.writeExpression(s.expression); + this.writeLine(')'); + if (s.thenStatement.nodeType === cs.SyntaxKind.Block) { + this.writeStatement(s.thenStatement); + } else { + this._indent++; + this.writeStatement(s.thenStatement); + this._indent--; + } + + if (s.elseStatement) { + this.write('else '); + if (s.elseStatement.nodeType === cs.SyntaxKind.IfStatement) { + this.writeStatement(s.elseStatement); + } else if (s.elseStatement.nodeType === cs.SyntaxKind.Block) { + this.writeLine(); + this.writeStatement(s.elseStatement); + } else { + this.writeLine(); + this._indent++; + this.writeStatement(s.elseStatement); + this._indent--; + } + } + } + private writeExpressionStatement(s: cs.ExpressionStatement) { + this.writeExpression(s.expression); + this.writeLine(';'); + } + + private writeVariableStatement(v: cs.VariableStatement) { + this.writeVariableDeclarationList(v.declarationList); + this.writeLine(';'); + } + + private writeVariableDeclarationList(declarationList: cs.VariableDeclarationList) { + this.writeType(declarationList.declarations[0].type); + + declarationList.declarations.forEach((d, i) => { + if (i === 0) { + this.write(' '); + } else { + this.write(', '); + } + + this.write(d.name); + if (d.initializer) { + this.write(' = '); + this.writeExpression(d.initializer); + } + }); + } + + private writeEmptyStatement(_: cs.EmptyStatement) { + this.writeLine(';'); + } + + private writeBlock(b: cs.Block) { + this.beginBlock(); + b.statements.forEach(s => this.writeStatement(s)); + this.endBlock(); + } + + private writeVisibility(visibility: cs.Visibility) { + switch (visibility) { + case cs.Visibility.Public: + this.write('public '); + break; + case cs.Visibility.Private: + this.write('private '); + break; + case cs.Visibility.Protected: + this.write('protected '); + break; + case cs.Visibility.Internal: + this.write('internal '); + break; + } + } + + private writeUsing(using: cs.UsingDeclaration) { + if (using.typeAlias) { + this.writeLine(`using ${using.typeAlias} = ${using.namespaceOrTypeName};`); + } else { + this.writeLine(`using ${using.namespaceOrTypeName};`); + } + } + + private writeLine(txt?: string) { + this.writeIndent(); + if (txt) { + this.write(txt); + } + this.write(ts.sys.newLine); + this._isStartOfLine = true; + } + + private write(txt: string) { + this.writeIndent(); + fs.writeSync(this._fileHandle, txt); + this._isStartOfLine = false; + } + + private writeIndent() { + if (this._isStartOfLine && this._indent > 0) { + fs.writeSync(this._fileHandle, this._indent === 1 ? ' ' : ' '.repeat(this._indent)); + this._isStartOfLine = false; + } + } + + private beginBlock() { + this.writeLine('{'); + this._indent++; + } + + private endBlock() { + this._indent--; + this.writeLine('}'); + } +} diff --git a/src.compiler/csharp/CSharpAstTransformer.ts b/src.compiler/csharp/CSharpAstTransformer.ts new file mode 100644 index 000000000..c79c6095a --- /dev/null +++ b/src.compiler/csharp/CSharpAstTransformer.ts @@ -0,0 +1,3154 @@ +import * as ts from 'typescript'; +import * as cs from './CSharpAst'; +import * as path from 'path'; +import CSharpEmitterContext from './CSharpEmitterContext'; + +export default class CSharpAstTransformer { + private _typeScriptFile: ts.SourceFile; + private _csharpFile: cs.SourceFile; + private _context: CSharpEmitterContext; + private _declarationOrAssignmentTypeStack: ts.Type[] = []; + + public constructor(typeScript: ts.SourceFile, context: CSharpEmitterContext) { + this._typeScriptFile = typeScript; + this._context = context; + + let fileName = path.relative( + path.resolve(this._context.compilerOptions.baseUrl!), + path.resolve(this._typeScriptFile.fileName) + ); + fileName = path.join(context.compilerOptions.outDir!, this.removeExtension(fileName) + '.cs'); + + this._csharpFile = { + parent: null, + tsNode: this._typeScriptFile, + nodeType: cs.SyntaxKind.SourceFile, + fileName: fileName, + usings: [ + { + namespaceOrTypeName: 'System', + nodeType: cs.SyntaxKind.UsingDeclaration + } as cs.UsingDeclaration, + { + namespaceOrTypeName: 'AlphaTab.Core', + nodeType: cs.SyntaxKind.UsingDeclaration + } as cs.UsingDeclaration + ], + namespace: { + parent: null, + nodeType: cs.SyntaxKind.NamespaceDeclaration, + namespace: 'AlphaTab', + declarations: [] + } + }; + this._csharpFile.namespace.parent = this._csharpFile; + } + + public transform() { + // if the default export is a class: + // - global statements will be put into the static constructor of the class + // - non exported variable declarations will become private static fields + // - non exported function declarations will become private static methods + // - exported variable declarations will become public static fields + // - exported function declarations will become public static methods + // - exported non default classes/enums/interfaces will just be normal namespace members (might need special treatment to avoid name clashes) + + // if the default export is no class: + // - global statements will raise an error (unsupported for now) + // - global variable declarations will raise an error (unsupported for now) + // - global method declarations will raise an error (unsupported for now) + // - exported non default classes/enums/interfaces will just be normal namespace members (might need special treatment to avoid name clashes) + + // type aliases for function types will become delegates + // other type aliases will raise an error (unsupported for now) + + // describe() calls are translated as Test Classes + // it() calls will be translated to Test Methods + + // if the file is the main library file, we remember the exported types + // to make them public within the .net library + + // collect exports and declarations + let defaultExport: ts.Declaration | null = null; + const additionalExportDeclarations: ts.Declaration[] = []; + const additionalNonExportDeclarations: ts.Declaration[] = []; + const additionalNestedExportDeclarations: ts.Declaration[] = []; + const additionalNestedNonExportsDeclarations: ts.Declaration[] = []; + const globalStatements: ts.Statement[] = []; + const testClasses: ts.CallExpression[] = []; + const globalExports: ts.ExportDeclaration[] = []; + + this._typeScriptFile.statements.forEach(s => { + if (ts.isExportDeclaration(s)) { + globalExports.push(s); + } else if (ts.isClassDeclaration(s) || ts.isInterfaceDeclaration(s) || ts.isEnumDeclaration(s)) { + const isExport = s.modifiers && !!s.modifiers.find(m => m.kind === ts.SyntaxKind.ExportKeyword); + const isDefaultExport = this.isDefaultExport(s); + if (isExport && isDefaultExport) { + defaultExport = s; + } else if (isExport) { + additionalExportDeclarations.push(s); + } else { + additionalNonExportDeclarations.push(s); + } + } else if (ts.isTypeAliasDeclaration(s)) { + if (ts.isFunctionTypeNode(s.type)) { + const isExport = s.modifiers && !!s.modifiers.find(m => m.kind === ts.SyntaxKind.ExportKeyword); + const isDefaultExport = this.isDefaultExport(s); + if (isExport && isDefaultExport) { + defaultExport = s; + } else if (isExport) { + additionalExportDeclarations.push(s); + } else { + additionalNonExportDeclarations.push(s); + } + } else { + this._context.addTsNodeDiagnostics( + s, + 'Only FunctionType type aliases are allowed', + ts.DiagnosticCategory.Error + ); + } + } else if (ts.isVariableDeclaration(s) || ts.isFunctionDeclaration(s)) { + const isExport = s.modifiers && !!s.modifiers.find(m => m.kind === ts.SyntaxKind.ExportKeyword); + if (isExport) { + additionalNestedExportDeclarations.push(s); + } else { + additionalNestedNonExportsDeclarations.push(s); + } + } else if ( + ts.isExpressionStatement(s) && + ts.isCallExpression(s.expression) && + ts.isIdentifier(s.expression.expression) && + s.expression.expression.text === 'describe' + ) { + testClasses.push(s.expression); + } else if (!ts.isImportDeclaration(s)) { + globalStatements.push(s); + } + }); + + // TODO: Introduce setting for main library name. + if (path.basename(this._typeScriptFile.fileName).toLowerCase() === 'alphatab.ts') { + globalExports.forEach(x => { + if (!x.name && x.exportClause) { + if (ts.isNamespaceExport(x.exportClause)) { + this._context.addTsNodeDiagnostics( + x.exportClause, + 'Namespace exports are not yet supported', + ts.DiagnosticCategory.Error + ); + } else { + x.exportClause.elements.forEach(e => { + const symbol = this._context.typeChecker.getTypeAtLocation(e.name)?.symbol; + if (symbol) { + this._context.registerSymbolAsExported(symbol); + } else { + this._context.addTsNodeDiagnostics( + x.exportClause!, + 'Exported symbol could not be resolved', + ts.DiagnosticCategory.Error + ); + } + }); + } + } else { + this._context.addTsNodeDiagnostics( + x, + 'Unsupported export', + ts.DiagnosticCategory.Error + ); + } + }); + + globalStatements.forEach(s => { + if (ts.isVariableStatement(s) + && s.modifiers?.find(m => m.kind === ts.SyntaxKind.ExportKeyword)) { + s.declarationList.declarations.forEach(d => { + if (d.initializer && ts.isObjectLiteralExpression(d.initializer)) { + d.initializer.properties.forEach(p => { + if (ts.isShorthandPropertyAssignment(p)) { + const symbol = this._context.typeChecker.getTypeAtLocation(p.name)?.symbol; + if (symbol) { + this._context.registerSymbolAsExported(symbol); + } else { + this._context.addTsNodeDiagnostics( + p, + 'Exported symbol could not be resolved', + ts.DiagnosticCategory.Error + ); + } + } else { + this._context.addTsNodeDiagnostics( + p, + 'Unsupported export', + ts.DiagnosticCategory.Error + ); + } + }); + } + else { + this._context.addTsNodeDiagnostics( + d, + 'Unsupported export', + ts.DiagnosticCategory.Error + ); + } + }) + + } + }) + + // TODO: register global exports + } else { + // validate global statements + if (!defaultExport || !ts.isClassDeclaration(defaultExport)) { + globalStatements.forEach(s => { + this._context.addTsNodeDiagnostics( + s, + 'Global statements in modules are only allowed if there is a default class export', + ts.DiagnosticCategory.Error + ); + }); + } + + additionalNestedExportDeclarations.forEach(s => { + this._context.addTsNodeDiagnostics( + s, + 'Global statements in modules are not yet supported', + ts.DiagnosticCategory.Error + ); + }); + additionalNestedNonExportsDeclarations.forEach(s => { + this._context.addTsNodeDiagnostics( + s, + 'Global statements in modules are not yet supported', + ts.DiagnosticCategory.Error + ); + }); + + // TODO: make root namespace configurable from outside. + let folders = path + .dirname( + path.relative( + path.resolve(this._context.compilerOptions.baseUrl!), + path.resolve(this._typeScriptFile.fileName) + ) + ) + .split(path.sep); + // TODO: make default root folders configurable from outside + if (folders.length > 0 && (folders[0] === 'src' || folders[0] === 'test')) { + folders.shift(); + } + this._csharpFile.namespace.namespace = + 'AlphaTab' + folders.map(f => '.' + f.substr(0, 1).toUpperCase() + f.substr(1)).join(''); + + if (defaultExport) { + this.visit( + defaultExport, + additionalNestedExportDeclarations, + additionalNestedNonExportsDeclarations, + globalStatements + ); + } + additionalExportDeclarations.forEach(d => this.visit(d)); + additionalNonExportDeclarations.forEach(d => this.visit(d)); + testClasses.forEach(d => this.visitTestClass(d)); + + if (this._csharpFile.namespace.declarations.length > 0) { + this._context.addSourceFile(this._csharpFile); + } + } + } + + private isDefaultExport(s: ts.NamedDeclaration): boolean { + const isDefaultExport = s.modifiers && !!s.modifiers.find(m => m.kind === ts.SyntaxKind.DefaultKeyword); + if (isDefaultExport) { + return true; + } + + // if the declaration has the same name as the file we consider it as default export + const fileName = this.removeExtension(path.basename(this._typeScriptFile.fileName)); + if (s.name && ts.isIdentifier(s.name) && fileName === s.name.text) { + return true; + } + + return false; + } + + private visit( + node: ts.Node, + additionalNestedExportDeclarations?: ts.Declaration[], + additionalNestedNonExportsDeclarations?: ts.Declaration[], + globalStatements?: ts.Statement[] + ): ts.Node { + if (this.shouldSkip(node)) { + return node; + } + + if (ts.isClassDeclaration(node)) { + this.visitClassDeclaration( + node, + additionalNestedExportDeclarations, + additionalNestedNonExportsDeclarations, + globalStatements + ); + } else if (ts.isEnumDeclaration(node)) { + this.visitEnumDeclaration(node); + } else if (ts.isInterfaceDeclaration(node)) { + this.visitInterfaceDeclaration(node); + } else if (ts.isTypeAliasDeclaration(node) && ts.isFunctionTypeNode(node.type)) { + this.visitTypeAliasDeclaration(node); + } + + return node; + } + + private shouldSkip(node: ts.Node) { + const tags = ts.getJSDocTags(node).filter(t => t.tagName.text === 'target'); + if (tags.length === 0) { + return false; + } + return !tags.find(t => t.comment === 'csharp'); + } + + private visitEnumDeclaration(node: ts.EnumDeclaration) { + const csEnum: cs.EnumDeclaration = { + visibility: cs.Visibility.Public, + name: node.name.text, + nodeType: cs.SyntaxKind.EnumDeclaration, + parent: this._csharpFile.namespace, + members: [], + tsNode: node, + skipEmit: this.shouldSkip(node), + tsSymbol: this._context.getSymbolForDeclaration(node) + }; + + if (node.name) { + csEnum.documentation = this.visitDocumentation(node.name); + } + + node.members.forEach(m => this.visitEnumMember(csEnum, m)); + + this._csharpFile.namespace.declarations.push(csEnum); + this._context.registerSymbol(csEnum); + } + + private visitEnumMember(parent: cs.EnumDeclaration, enumMember: ts.EnumMember) { + const csEnumMember: cs.EnumMember = { + parent: parent, + tsNode: enumMember, + nodeType: cs.SyntaxKind.EnumMember, + name: enumMember.name.getText(), + skipEmit: this.shouldSkip(enumMember) + }; + + if (enumMember.initializer) { + csEnumMember.initializer = this.visitExpression(csEnumMember, enumMember.initializer) ?? undefined; + } + + if (enumMember.name) { + csEnumMember.documentation = this.visitDocumentation(enumMember.name); + } + + parent.members.push(csEnumMember); + this._context.registerSymbol(csEnumMember); + } + + private visitTypeAliasDeclaration(node: ts.TypeAliasDeclaration) { + //throw new Error("Method not implemented."); + } + + private visitInterfaceDeclaration(node: ts.InterfaceDeclaration) { + let extendsClauses: ts.ExpressionWithTypeArguments[] = []; + + node.heritageClauses?.forEach(c => { + if (c.token === ts.SyntaxKind.ExtendsKeyword) { + extendsClauses = c.types.slice(); + } + }); + + const csInterface: cs.InterfaceDeclaration = { + visibility: cs.Visibility.Public, + name: node.name.text, + nodeType: cs.SyntaxKind.InterfaceDeclaration, + parent: this._csharpFile.namespace, + members: [], + tsNode: node, + skipEmit: this.shouldSkip(node), + tsSymbol: this._context.getSymbolForDeclaration(node) + }; + + if (node.name) { + csInterface.documentation = this.visitDocumentation(node.name); + } + + if (node.typeParameters) { + csInterface.typeParameters = node.typeParameters.map(p => + this.visitTypeParameterDeclaration(csInterface, p) + ); + } + + if (extendsClauses && extendsClauses.length > 0) { + csInterface.interfaces = extendsClauses.map(n => { + const inter = this.createUnresolvedTypeNode(csInterface, n); + if (n.typeArguments) { + inter.typeArguments = n.typeArguments.map(a => this.createUnresolvedTypeNode(csInterface, a)); + } else { + inter.typeArguments = undefined; + } + + return inter; + }); + } + + node.members.forEach(m => this.visitInterfaceElement(csInterface, m)); + + this._csharpFile.namespace.declarations.push(csInterface); + this._context.registerSymbol(csInterface); + } + + private visitTypeParameterDeclaration(parent: cs.Node, p: ts.TypeParameterDeclaration): cs.TypeParameterDeclaration { + const csTypeParameter: cs.TypeParameterDeclaration = { + nodeType: cs.SyntaxKind.TypeParameterDeclaration, + name: p.name.text, + parent: parent, + tsNode: p + }; + + if (p.constraint) { + csTypeParameter.constraint = this.createUnresolvedTypeNode(csTypeParameter, p.constraint); + } + + return csTypeParameter; + } + + private createUnresolvedTypeNode( + parent: cs.Node | null, + tsNode: ts.Node, + tsType?: ts.Type, + tsSymbol?: ts.Symbol + ): cs.UnresolvedTypeNode { + if (!tsType) { + tsType = this._context.typeChecker.getTypeAtLocation(tsNode); + } + + const unresolved = { + nodeType: cs.SyntaxKind.UnresolvedTypeNode, + tsNode: tsNode, + tsType: tsType, + tsSymbol: tsSymbol, + parent: parent + } as cs.UnresolvedTypeNode; + + const typeArguments = (tsType as ts.TypeReference)?.typeArguments; + if (typeArguments) { + unresolved.typeArguments = typeArguments.map(a => this.createUnresolvedTypeNode(parent, tsNode, a)); + } + + this._context.registerUnresolvedTypeNode(unresolved); + return unresolved; + } + + public visitTestClass(d: ts.CallExpression): void { + const csClass: cs.ClassDeclaration = { + visibility: cs.Visibility.Public, + name: (d.arguments[0] as ts.StringLiteral).text, + tsNode: d, + nodeType: cs.SyntaxKind.ClassDeclaration, + parent: this._csharpFile.namespace, + isAbstract: false, + partial: false, + members: [] + }; + + csClass.attributes = [ + { + parent: csClass, + nodeType: cs.SyntaxKind.Attribute, + type: { + parent: null, + nodeType: cs.SyntaxKind.TypeReference, + reference: 'Microsoft.VisualStudio.TestTools.UnitTesting.TestClass' + } as cs.TypeReference + } + ]; + + ((d.arguments![1] as ts.ArrowFunction).body as ts.Block).statements.forEach(s => { + if (ts.isExpressionStatement(s)) { + if (ts.isCallExpression(s.expression)) { + if (ts.isIdentifier(s.expression.expression) && s.expression.expression.text === 'it') { + this.visitTestMethod(csClass, s.expression); + } else { + this._context.addTsNodeDiagnostics( + s, + 'Unsupported test method function call ' + s.expression.expression.getText(), + ts.DiagnosticCategory.Error + ); + } + } else { + this._context.addTsNodeDiagnostics( + s, + 'Unsupported test class member ' + ts.SyntaxKind[s.expression.kind], + ts.DiagnosticCategory.Error + ); + } + } else if (ts.isVariableStatement(s)) { + this.visitTestClassProperty(csClass, s); + } else { + this._context.addTsNodeDiagnostics( + s, + 'Unsupported test class member ' + ts.SyntaxKind[s.kind], + ts.DiagnosticCategory.Error + ); + } + }); + + this._csharpFile.namespace.declarations.push(csClass); + } + + private visitTestMethod(parent: cs.ClassDeclaration, d: ts.CallExpression) { + const csMethod: cs.MethodDeclaration = { + parent: parent, + nodeType: cs.SyntaxKind.MethodDeclaration, + isAbstract: false, + isOverride: false, + isStatic: false, + isVirtual: false, + name: this._context.toPascalCase((d.arguments[0] as ts.StringLiteral).text), + parameters: [], + returnType: { + parent: null, + nodeType: cs.SyntaxKind.PrimitiveTypeNode, + type: cs.PrimitiveType.Void, + tsNode: d.arguments[1] + } as cs.PrimitiveTypeNode, + visibility: cs.Visibility.Public, + tsNode: d + }; + + if (csMethod.name.match(/^[^a-zA-Z].*/)) { + csMethod.name = 'Test' + csMethod.name; + } + + csMethod.attributes = [ + { + parent: csMethod, + nodeType: cs.SyntaxKind.Attribute, + type: { + parent: null, + nodeType: cs.SyntaxKind.TypeReference, + reference: 'Microsoft.VisualStudio.TestTools.UnitTesting.TestMethod' + } as cs.TypeReference + } + ]; + + const testFunction = d.arguments![1] as ts.ArrowFunction; + csMethod.isAsync = + !!testFunction.modifiers && !!testFunction.modifiers.find(m => m.kind === ts.SyntaxKind.AsyncKeyword); + + csMethod.body = this.visitBlock(csMethod, testFunction.body as ts.Block); + + parent.members.push(csMethod); + } + + private visitTestClassProperty(parent: cs.ClassDeclaration, s: ts.VariableStatement) { + s.declarationList.declarations.forEach(d => { + const type = this._context.typeChecker.getTypeAtLocation(d.name); + if (this._context.isFunctionType(type) && d.initializer && ts.isArrowFunction(d.initializer)) { + const csMethod: cs.MethodDeclaration = { + parent: parent, + nodeType: cs.SyntaxKind.MethodDeclaration, + isAbstract: false, + isOverride: false, + isStatic: false, + isVirtual: false, + name: this._context.toPascalCase(d.name.getText()), + returnType: {} as cs.TypeNode, + visibility: cs.Visibility.Private, + tsNode: d, + parameters: [] + }; + csMethod.isAsync = + !!d.initializer.modifiers && + !!d.initializer.modifiers.find(m => m.kind === ts.SyntaxKind.AsyncKeyword); + + const functionType = type.symbol.declarations.find(d => + ts.isFunctionTypeNode(d) + ) as ts.FunctionTypeNode; + + if (csMethod.isAsync) { + const mapped = this.createUnresolvedTypeNode(csMethod, functionType.type); + if (mapped.tsType && mapped.tsType.symbol && mapped.tsType.symbol.name === 'Promise') { + csMethod.returnType = mapped.typeArguments![0]; + } else { + csMethod.returnType = mapped; + } + } else { + csMethod.returnType = this.createUnresolvedTypeNode(csMethod, functionType.type); + } + + csMethod.returnType.parent = csMethod; + + d.initializer.parameters.forEach(p => csMethod.parameters.push(this.makeParameter(csMethod, p))); + this._declarationOrAssignmentTypeStack.push(type); + csMethod.body = this.visitBlock(csMethod, d.initializer.body as ts.Block); + this._declarationOrAssignmentTypeStack.pop(); + + parent.members.push(csMethod); + this._context.registerSymbol(csMethod); + } else { + const csProperty: cs.PropertyDeclaration = { + parent: parent, + nodeType: cs.SyntaxKind.PropertyDeclaration, + isAbstract: false, + isOverride: false, + isStatic: false, + isVirtual: false, + name: this._context.toPascalCase(d.name.getText()), + type: this.createUnresolvedTypeNode(null, d.type ?? d, type), + visibility: cs.Visibility.Private, + tsNode: d + }; + + csProperty.type.parent = csProperty; + csProperty.getAccessor = { + parent: csProperty, + nodeType: cs.SyntaxKind.PropertyAccessorDeclaration, + keyword: 'get' + }; + + if (d.initializer) { + this._declarationOrAssignmentTypeStack.push(type); + csProperty.initializer = this.visitExpression(csProperty, d.initializer) ?? undefined; + this._declarationOrAssignmentTypeStack.pop(); + } + + parent.members.push(csProperty); + this._context.registerSymbol(csProperty); + } + }); + } + + private visitClassDeclaration( + node: ts.ClassDeclaration, + additionalNestedExportDeclarations?: ts.Declaration[], + additionalNestedNonExportsDeclarations?: ts.Declaration[], + globalStatements?: ts.Statement[] + ) { + let extendsClause: ts.ExpressionWithTypeArguments | null = null; + let implementsClauses: ts.ExpressionWithTypeArguments[] = []; + + node.heritageClauses?.forEach(c => { + if (c.token === ts.SyntaxKind.ExtendsKeyword) { + extendsClause = c.types[0]; + } + if (c.token === ts.SyntaxKind.ImplementsKeyword) { + implementsClauses = c.types.slice(); + } + }); + + const csClass: cs.ClassDeclaration = { + visibility: cs.Visibility.Public, + name: node.name!.text, + tsNode: node, + nodeType: cs.SyntaxKind.ClassDeclaration, + parent: this._csharpFile.namespace, + isAbstract: !!node.modifiers && !!node.modifiers.find(m => m.kind === ts.SyntaxKind.AbstractKeyword), + partial: !!ts.getJSDocTags(node).find(t => t.tagName.text === 'partial'), + members: [], + skipEmit: this.shouldSkip(node), + tsSymbol: this._context.getSymbolForDeclaration(node) + }; + + if (node.name) { + csClass.documentation = this.visitDocumentation(node.name); + } + + if (node.typeParameters) { + csClass.typeParameters = node.typeParameters.map(p => this.visitTypeParameterDeclaration(csClass, p)); + } + + if (extendsClause) { + const ex = extendsClause as ts.ExpressionWithTypeArguments; + const baseClass = this.createUnresolvedTypeNode(csClass, ex); + if (ex.typeArguments) { + baseClass.typeArguments = ex.typeArguments.map(a => this.createUnresolvedTypeNode(csClass, a)); + } else { + baseClass.typeArguments = []; + } + csClass.baseClass = baseClass; + } + + if (implementsClauses && implementsClauses.length > 0) { + csClass.interfaces = implementsClauses.map(n => { + const inter = this.createUnresolvedTypeNode(csClass, n); + if (n.typeArguments) { + inter.typeArguments = n.typeArguments.map(a => this.createUnresolvedTypeNode(csClass, a)); + } else { + inter.typeArguments = undefined; + } + + return inter; + }); + } + + node.members.forEach(m => this.visitClassElement(csClass, m)); + + if (globalStatements && globalStatements.length > 0) { + const staticConstructor = { + parent: csClass, + isStatic: true, + name: 'cctor', + nodeType: cs.SyntaxKind.ConstructorDeclaration, + parameters: [], + visibility: cs.Visibility.None, + tsNode: node, + body: { + parent: null, + nodeType: cs.SyntaxKind.Block, + statements: [] + } as cs.Block + } as cs.ConstructorDeclaration; + + globalStatements.forEach(s => { + const st = this.visitStatement(staticConstructor.body!, s)!; + if (st) { + (staticConstructor.body as cs.Block).statements.push(st); + } + }); + + csClass.members.push(staticConstructor); + } + + this._csharpFile.namespace.declarations.push(csClass); + this._context.registerSymbol(csClass); + } + + private visitDocumentation(node: ts.Node): string | undefined { + let symbol = this._context.typeChecker.getSymbolAtLocation(node); + if (!symbol) { + return undefined; + } + + const docs = symbol.getDocumentationComment(this._context.typeChecker); + if (!docs || docs.length === 0) { + return undefined; + } + + let s = ''; + + for (const d of docs) { + switch ((ts.SymbolDisplayPartKind as any)[d.kind]) { + case ts.SymbolDisplayPartKind.text: + s += d.text.split('\r').join(''); + break; + case ts.SymbolDisplayPartKind.lineBreak: + s += '\n'; + break; + case ts.SymbolDisplayPartKind.space: + s += ' '; + break; + case ts.SymbolDisplayPartKind.aliasName: + case ts.SymbolDisplayPartKind.className: + case ts.SymbolDisplayPartKind.enumName: + case ts.SymbolDisplayPartKind.fieldName: + case ts.SymbolDisplayPartKind.interfaceName: + case ts.SymbolDisplayPartKind.keyword: + case ts.SymbolDisplayPartKind.numericLiteral: + case ts.SymbolDisplayPartKind.stringLiteral: + case ts.SymbolDisplayPartKind.localName: + case ts.SymbolDisplayPartKind.methodName: + case ts.SymbolDisplayPartKind.moduleName: + case ts.SymbolDisplayPartKind.operator: + case ts.SymbolDisplayPartKind.parameterName: + case ts.SymbolDisplayPartKind.propertyName: + case ts.SymbolDisplayPartKind.punctuation: + case ts.SymbolDisplayPartKind.typeParameterName: + case ts.SymbolDisplayPartKind.enumMemberName: + case ts.SymbolDisplayPartKind.functionName: + case ts.SymbolDisplayPartKind.regularExpressionLiteral: + s += d.text.split('\r').join(''); + break; + } + } + + return s; + } + + private visitClassElement(parent: cs.ClassDeclaration, classElement: ts.ClassElement) { + if (ts.isConstructorDeclaration(classElement)) { + this.visitConstructorDeclaration(parent, classElement); + } else if (ts.isMethodSignature(classElement)) { + this.visitMethodSignature(parent, classElement); + } else if (ts.isMethodDeclaration(classElement)) { + this.visitMethodDeclaration(parent, classElement); + } else if (ts.isPropertySignature(classElement)) { + this.visitPropertySignature(parent, classElement); + } else if (ts.isPropertyDeclaration(classElement)) { + this.visitPropertyDeclaration(parent, classElement); + } else if (ts.isGetAccessor(classElement)) { + this.visitGetAccessor(parent, classElement); + } else if (ts.isSetAccessor(classElement)) { + this.visitSetAccessor(parent, classElement); + } else { + this._context.addTsNodeDiagnostics( + classElement, + 'Unsupported class element: ' + ts.SyntaxKind[classElement.kind], + ts.DiagnosticCategory.Error + ); + } + } + + private visitInterfaceElement(parent: cs.InterfaceDeclaration, classElement: ts.TypeElement) { + if (ts.isMethodSignature(classElement)) { + this.visitMethodSignature(parent, classElement); + } else if (ts.isPropertySignature(classElement)) { + this.visitPropertySignature(parent, classElement); + } else { + this._context.addTsNodeDiagnostics( + classElement, + 'Unsupported interface element: ' + ts.SyntaxKind[classElement.kind], + ts.DiagnosticCategory.Error + ); + } + } + + private visitPropertySignature( + parent: cs.ClassDeclaration | cs.InterfaceDeclaration, + classElement: ts.PropertySignature + ) { + const type = this._context.typeChecker.getTypeAtLocation(classElement); + const csProperty: cs.PropertyDeclaration = { + parent: parent, + nodeType: cs.SyntaxKind.PropertyDeclaration, + isAbstract: false, + isOverride: false, + isStatic: false, + isVirtual: false, + name: this._context.toPascalCase((classElement.name as ts.Identifier).text), + type: this.createUnresolvedTypeNode(null, classElement.type ?? classElement, type), + visibility: cs.Visibility.None, + tsNode: classElement, + skipEmit: this.shouldSkip(classElement) + }; + + if (classElement.name) { + csProperty.documentation = this.visitDocumentation(classElement.name); + } + + let isReadonly = false; + if (classElement.modifiers) { + classElement.modifiers.forEach(m => { + switch (m.kind) { + case ts.SyntaxKind.ReadonlyKeyword: + isReadonly = true; + break; + } + }); + } + + csProperty.type.parent = csProperty; + csProperty.getAccessor = { + parent: csProperty, + nodeType: cs.SyntaxKind.PropertyAccessorDeclaration, + keyword: 'get' + }; + if (!isReadonly) { + csProperty.setAccessor = { + parent: csProperty, + nodeType: cs.SyntaxKind.PropertyAccessorDeclaration, + keyword: 'set' + }; + } + + parent.members.push(csProperty); + this._context.registerSymbol(csProperty); + } + + private visitGetAccessor(parent: cs.ClassDeclaration, classElement: ts.GetAccessorDeclaration) { + const propertyName = this._context.toPascalCase(classElement.name.getText()); + const member = parent.members.find(m => m.name === propertyName); + if (member && member.nodeType === cs.SyntaxKind.PropertyDeclaration) { + let existingProperty = member as cs.PropertyDeclaration; + existingProperty.getAccessor = { + keyword: 'get', + parent: existingProperty, + nodeType: cs.SyntaxKind.PropertyAccessorDeclaration, + tsNode: classElement, + body: classElement.body ? this.visitBlock(existingProperty, classElement.body) : null + } as cs.PropertyAccessorDeclaration; + } else { + const signature = this._context.typeChecker.getSignatureFromDeclaration(classElement); + const returnType = this._context.typeChecker.getReturnTypeOfSignature(signature!); + + let newProperty: cs.PropertyDeclaration = { + isAbstract: false, + isOverride: false, + isVirtual: false, + isStatic: false, + name: propertyName, + nodeType: cs.SyntaxKind.PropertyDeclaration, + parent: parent, + visibility: this.mapVisibility(classElement.modifiers), + type: this.createUnresolvedTypeNode(null, classElement.type ?? classElement, returnType), + skipEmit: this.shouldSkip(classElement) + }; + + if (newProperty.visibility === cs.Visibility.Public || newProperty.visibility === cs.Visibility.Protected) { + if (this._context.isOverride(classElement)) { + newProperty.isVirtual = false; + newProperty.isOverride = true; + } else { + newProperty.isVirtual = true; + newProperty.isOverride = false; + } + } + + if (classElement.modifiers) { + classElement.modifiers.forEach(m => { + switch (m.kind) { + case ts.SyntaxKind.AbstractKeyword: + newProperty.isAbstract = true; + parent.isAbstract = true; + newProperty.isVirtual = false; + newProperty.isOverride = false; + break; + case ts.SyntaxKind.StaticKeyword: + newProperty.isStatic = true; + newProperty.isVirtual = false; + newProperty.isOverride = false; + break; + } + }); + } + + newProperty.type.parent = newProperty; + + newProperty.getAccessor = { + keyword: 'get', + parent: newProperty, + nodeType: cs.SyntaxKind.PropertyAccessorDeclaration, + tsNode: classElement, + body: classElement.body ? this.visitBlock(newProperty, classElement.body) : null + } as cs.PropertyAccessorDeclaration; + + parent.members.push(newProperty); + } + } + + private visitSetAccessor(parent: cs.ClassDeclaration, classElement: ts.SetAccessorDeclaration) { + const propertyName = this._context.toPascalCase(classElement.name.getText()); + const member = parent.members.find(m => m.name === propertyName); + if (member && member.nodeType === cs.SyntaxKind.PropertyDeclaration) { + let existingProperty = member as cs.PropertyDeclaration; + existingProperty.setAccessor = { + keyword: 'set', + parent: existingProperty, + nodeType: cs.SyntaxKind.PropertyAccessorDeclaration, + tsNode: classElement, + body: classElement.body ? this.visitBlock(existingProperty, classElement.body) : null + } as cs.PropertyAccessorDeclaration; + } else { + const signature = this._context.typeChecker.getSignatureFromDeclaration(classElement); + const returnType = this._context.typeChecker.getReturnTypeOfSignature(signature!); + + let newProperty: cs.PropertyDeclaration = { + isAbstract: false, + isOverride: false, + isVirtual: false, + isStatic: false, + name: propertyName, + nodeType: cs.SyntaxKind.PropertyDeclaration, + parent: parent, + visibility: this.mapVisibility(classElement.modifiers), + type: this.createUnresolvedTypeNode(null, classElement.type ?? classElement, returnType), + skipEmit: this.shouldSkip(classElement) + }; + + if (newProperty.visibility === cs.Visibility.Public || newProperty.visibility === cs.Visibility.Protected) { + if (this._context.isOverride(classElement)) { + newProperty.isVirtual = false; + newProperty.isOverride = true; + } else { + newProperty.isVirtual = true; + newProperty.isOverride = false; + } + } + + if (classElement.modifiers) { + classElement.modifiers.forEach(m => { + switch (m.kind) { + case ts.SyntaxKind.AbstractKeyword: + newProperty.isAbstract = true; + parent.isAbstract = true; + newProperty.isVirtual = false; + newProperty.isOverride = false; + break; + case ts.SyntaxKind.StaticKeyword: + newProperty.isStatic = true; + newProperty.isVirtual = false; + newProperty.isOverride = false; + break; + } + }); + } + + newProperty.type.parent = newProperty; + + newProperty.setAccessor = { + keyword: 'set', + parent: newProperty, + nodeType: cs.SyntaxKind.PropertyAccessorDeclaration, + tsNode: classElement, + body: classElement.body ? this.visitBlock(newProperty, classElement.body) : null + } as cs.PropertyAccessorDeclaration; + + parent.members.push(newProperty); + } + } + + private visitPropertyDeclaration( + parent: cs.ClassDeclaration | cs.InterfaceDeclaration, + classElement: ts.PropertyDeclaration + ) { + const visibility = this.mapVisibility(classElement.modifiers); + const type = this._context.typeChecker.getTypeAtLocation(classElement); + const csProperty: cs.PropertyDeclaration = { + parent: parent, + nodeType: cs.SyntaxKind.PropertyDeclaration, + isAbstract: false, + isOverride: false, + isStatic: false, + isVirtual: false, + name: this._context.toPascalCase(classElement.name.getText()), + type: this.createUnresolvedTypeNode(null, classElement.type ?? classElement, type), + visibility: visibility, + tsNode: classElement, + skipEmit: this.shouldSkip(classElement) + }; + + if (csProperty.visibility === cs.Visibility.Public || csProperty.visibility === cs.Visibility.Protected) { + if (this._context.isOverride(classElement)) { + csProperty.isVirtual = false; + csProperty.isOverride = true; + } else { + csProperty.isVirtual = true; + csProperty.isOverride = false; + } + } + + if (classElement.name) { + csProperty.documentation = this.visitDocumentation(classElement.name); + } + + let isReadonly = false; + if (classElement.modifiers) { + classElement.modifiers.forEach(m => { + switch (m.kind) { + case ts.SyntaxKind.AbstractKeyword: + csProperty.isAbstract = true; + if (parent.nodeType === cs.SyntaxKind.ClassDeclaration) { + (parent as cs.ClassDeclaration).isAbstract = true; + } + csProperty.isVirtual = false; + csProperty.isOverride = false; + break; + case ts.SyntaxKind.StaticKeyword: + csProperty.isStatic = true; + csProperty.isVirtual = false; + csProperty.isOverride = false; + break; + case ts.SyntaxKind.ReadonlyKeyword: + isReadonly = true; + break; + } + }); + } + + csProperty.type.parent = csProperty; + csProperty.getAccessor = { + parent: csProperty, + nodeType: cs.SyntaxKind.PropertyAccessorDeclaration, + keyword: 'get' + }; + + if (!isReadonly) { + csProperty.setAccessor = { + parent: csProperty, + nodeType: cs.SyntaxKind.PropertyAccessorDeclaration, + keyword: 'set' + }; + } + + if (classElement.initializer) { + this._declarationOrAssignmentTypeStack.push(type); + csProperty.initializer = this.visitExpression(csProperty, classElement.initializer) ?? undefined; + this._declarationOrAssignmentTypeStack.pop(); + } else if (classElement.exclamationToken) { + csProperty.initializer = { + parent: csProperty, + expression: { + nodeType: cs.SyntaxKind.NullLiteral, + tsNode: csProperty.tsNode + } as cs.NullLiteral, + nodeType: cs.SyntaxKind.NonNullExpression, + tsNode: csProperty.tsNode + } as cs.NonNullExpression; + + (csProperty.initializer as cs.NonNullExpression).expression.parent = csProperty.initializer; + } + + parent.members.push(csProperty); + + this._context.registerSymbol(csProperty); + } + + private visitMethodDeclaration( + parent: cs.ClassDeclaration | cs.InterfaceDeclaration, + classElement: ts.MethodDeclaration + ) { + const signature = this._context.typeChecker.getSignatureFromDeclaration(classElement); + const returnType = this._context.typeChecker.getReturnTypeOfSignature(signature!); + + const csMethod: cs.MethodDeclaration = { + parent: parent, + nodeType: cs.SyntaxKind.MethodDeclaration, + isAbstract: false, + isOverride: false, + isStatic: false, + isVirtual: false, + name: this._context.toPascalCase((classElement.name as ts.Identifier).text), + parameters: [], + returnType: this.createUnresolvedTypeNode(null, classElement.type ?? classElement, returnType), + visibility: this.mapVisibility(classElement.modifiers), + tsNode: classElement, + skipEmit: this.shouldSkip(classElement) + }; + + if (classElement.name) { + csMethod.documentation = this.visitDocumentation(classElement.name); + } + + if (csMethod.visibility === cs.Visibility.Public || csMethod.visibility === cs.Visibility.Protected) { + if (this._context.isOverride(classElement)) { + csMethod.isVirtual = false; + csMethod.isOverride = true; + } else { + csMethod.isVirtual = true; + csMethod.isOverride = false; + } + } + + if (classElement.modifiers) { + classElement.modifiers.forEach(m => { + switch (m.kind) { + case ts.SyntaxKind.AbstractKeyword: + csMethod.isAbstract = true; + if (parent.nodeType === cs.SyntaxKind.ClassDeclaration) { + (parent as cs.ClassDeclaration).isAbstract = true; + } + csMethod.isVirtual = false; + csMethod.isOverride = false; + break; + case ts.SyntaxKind.StaticKeyword: + csMethod.isStatic = true; + csMethod.isVirtual = false; + csMethod.isOverride = false; + break; + case ts.SyntaxKind.AsyncKeyword: + csMethod.isAsync = true; + break; + } + }); + } + + csMethod.returnType.parent = csMethod; + + if (classElement.typeParameters && classElement.typeParameters.length > 0) { + csMethod.typeParameters = []; + classElement.typeParameters.forEach(p => { + const csp = { + parent: csMethod, + name: p.name.text, + nodeType: cs.SyntaxKind.TypeParameterDeclaration, + tsNode: p + } as cs.TypeParameterDeclaration; + if (p.constraint) { + csp.constraint = this.createUnresolvedTypeNode(csp, p.constraint); + } + + csMethod.typeParameters!.push(csp); + }); + } + + classElement.parameters.forEach(p => this.visitMethodParameter(csMethod, p)); + + if (classElement.body) { + csMethod.body = this.visitBlock(csMethod, classElement.body); + } + + parent.members.push(csMethod); + + this._context.registerSymbol(csMethod); + } + + private visitStatement(parent: cs.Node, s: ts.Statement): cs.Statement | null { + switch (s.kind) { + case ts.SyntaxKind.EmptyStatement: + return this.visitEmptyStatement(parent, s as ts.EmptyStatement); + case ts.SyntaxKind.DebuggerStatement: + return this.visitDebuggerStatement(parent, s as ts.DebuggerStatement); + case ts.SyntaxKind.Block: + return this.visitBlock(parent, s as ts.Block); + case ts.SyntaxKind.VariableStatement: + return this.visitVariableStatement(parent, s as ts.VariableStatement); + case ts.SyntaxKind.ExpressionStatement: + return this.visitExpressionStatement(parent, s as ts.ExpressionStatement); + case ts.SyntaxKind.IfStatement: + return this.visitIfStatement(parent, s as ts.IfStatement); + case ts.SyntaxKind.DoStatement: + return this.visitDoStatement(parent, s as ts.DoStatement); + case ts.SyntaxKind.WhileStatement: + return this.visitWhileStatement(parent, s as ts.WhileStatement); + case ts.SyntaxKind.ForStatement: + return this.visitForStatement(parent, s as ts.ForStatement); + case ts.SyntaxKind.ForOfStatement: + return this.visitForOfStatement(parent, s as ts.ForOfStatement); + case ts.SyntaxKind.ForInStatement: + return this.visitForInStatement(parent, s as ts.ForInStatement); + case ts.SyntaxKind.BreakStatement: + return this.visitBreakStatement(parent, s as ts.BreakStatement); + case ts.SyntaxKind.ContinueStatement: + return this.visitContinueStatement(parent, s as ts.ContinueStatement); + case ts.SyntaxKind.ReturnStatement: + return this.visitReturnStatement(parent, s as ts.ReturnStatement); + case ts.SyntaxKind.WithStatement: + this._context.addTsNodeDiagnostics(s, 'With statement is not supported', ts.DiagnosticCategory.Error); + return {} as cs.ThrowStatement; + case ts.SyntaxKind.SwitchStatement: + return this.visitSwitchStatement(parent, s as ts.SwitchStatement); + case ts.SyntaxKind.LabeledStatement: + this._context.addTsNodeDiagnostics( + s, + 'Labeled statement is not supported', + ts.DiagnosticCategory.Error + ); + return {} as cs.ThrowStatement; + case ts.SyntaxKind.ThrowStatement: + return this.visitThrowStatement(parent, s as ts.ThrowStatement); + case ts.SyntaxKind.TryStatement: + return this.visitTryStatement(parent, s as ts.TryStatement); + } + return {} as cs.ThrowStatement; + } + + private visitEmptyStatement(parent: cs.Node, s: ts.EmptyStatement) { + return { + nodeType: cs.SyntaxKind.EmptyStatement, + parent: parent, + tsNode: s + } as cs.EmptyStatement; + } + private visitDebuggerStatement(parent: cs.Node, s: ts.DebuggerStatement) { + return {} as cs.ThrowStatement; + + // { + // nodeType: cs.SyntaxKind.ExpressionStatement, + // parent: parent, + // tsNode: s, + // expression: {} as cs.Expression // TOOD: call System.Diagnostics.Debugger.Break(); + // } as cs.ExpressionStatement; + } + + private visitBlock(parent: cs.Node, block: ts.Block): cs.Block { + const csBlock: cs.Block = { + nodeType: cs.SyntaxKind.Block, + parent: parent, + statements: [], + tsNode: block + }; + + for (const s of block.statements) { + const csStatement = this.visitStatement(csBlock, s); + if (csStatement) { + csBlock.statements.push(csStatement); + } + } + + return csBlock; + } + + private visitVariableStatement(parent: cs.Node, s: ts.VariableStatement) { + const variableStatement = { + nodeType: cs.SyntaxKind.VariableStatement, + parent: parent, + tsNode: s, + declarationList: {} as cs.VariableDeclarationList + } as cs.VariableStatement; + + variableStatement.declarationList = this.visitVariableDeclarationList(variableStatement, s.declarationList); + + return variableStatement; + } + + private visitVariableDeclarationList(parent: cs.Node, s: ts.VariableDeclarationList): cs.VariableDeclarationList { + const variableStatement = { + nodeType: cs.SyntaxKind.VariableDeclarationList, + parent: parent, + tsNode: s, + declarations: [] + } as cs.VariableDeclarationList; + + s.declarations.forEach(d => + variableStatement.declarations.push(this.visitVariableDeclaration(variableStatement, d)) + ); + + return variableStatement; + } + private visitVariableDeclaration(parent: cs.Node, s: ts.VariableDeclaration): cs.VariableDeclaration { + const symbol = this._context.typeChecker.getSymbolAtLocation(s.name); + const type = this._context.typeChecker.getTypeOfSymbolAtLocation(symbol!, s); + + const variableStatement = { + nodeType: cs.SyntaxKind.VariableDeclaration, + parent: parent, + tsNode: s, + name: s.name.getText(), + type: {} as cs.TypeNode + } as cs.VariableDeclaration; + + if (parent.nodeType === cs.SyntaxKind.CatchClause) { + variableStatement.type = { + nodeType: cs.SyntaxKind.TypeReference, + parent: variableStatement, + tsNode: s, + reference: 'AlphaTab.Core.EcmaScript.Error' + } as cs.TypeReference; + } else { + variableStatement.type = this.createUnresolvedTypeNode(variableStatement, s.type ?? s, type); + } + + variableStatement.type.parent = variableStatement; + + if (s.initializer) { + this._declarationOrAssignmentTypeStack.push(type); + variableStatement.initializer = this.visitExpression(variableStatement, s.initializer) ?? undefined; + this._declarationOrAssignmentTypeStack.pop(); + } + + return variableStatement; + } + + private visitExpressionStatement(parent: cs.Node, s: ts.ExpressionStatement) { + const expressionStatement = { + nodeType: cs.SyntaxKind.ExpressionStatement, + parent: parent, + tsNode: s, + expression: {} as cs.Expression + } as cs.ExpressionStatement; + + expressionStatement.expression = this.visitExpression(expressionStatement, s.expression)!; + if (!expressionStatement.expression) { + return null; + } + + return expressionStatement; + } + + private visitIfStatement(parent: cs.Node, s: ts.IfStatement) { + const ifStatement = { + nodeType: cs.SyntaxKind.IfStatement, + parent: parent, + tsNode: s, + expression: {} as cs.Expression, + thenStatement: {} as cs.Statement + } as cs.IfStatement; + + ifStatement.expression = this.visitExpression(ifStatement, s.expression)!; + if (!ifStatement.expression) { + return null; + } + ifStatement.thenStatement = this.visitStatement(ifStatement, s.thenStatement)!; + if (!ifStatement.thenStatement) { + return null + } + + if (s.elseStatement) { + ifStatement.elseStatement = this.visitStatement(ifStatement, s.elseStatement)!; + if (!ifStatement.elseStatement) { + return null; + } + } + + return ifStatement; + } + + private visitDoStatement(parent: cs.Node, s: ts.DoStatement) { + const doStatement = { + nodeType: cs.SyntaxKind.DoStatement, + parent: parent, + tsNode: s, + expression: {} as cs.Expression, + statement: {} as cs.Statement + } as cs.DoStatement; + + doStatement.expression = this.visitExpression(doStatement, s.expression)!; + if (!doStatement.expression) { + return null; + } + + doStatement.statement = this.visitStatement(doStatement, s.statement)!; + if (!doStatement.statement) { + return null; + } + + return doStatement; + } + + private visitWhileStatement(parent: cs.Node, s: ts.WhileStatement) { + const whileStatement = { + nodeType: cs.SyntaxKind.WhileStatement, + parent: parent, + tsNode: s, + expression: {} as cs.Expression, + statement: {} as cs.Statement + } as cs.WhileStatement; + + whileStatement.expression = this.visitExpression(whileStatement, s.expression)!; + if (!whileStatement.expression) { + return null; + } + + whileStatement.statement = this.visitStatement(whileStatement, s.statement)!; + if (!whileStatement.statement) { + return null; + } + + return whileStatement; + } + + private visitForStatement(parent: cs.Node, s: ts.ForStatement) { + const forStatement = { + nodeType: cs.SyntaxKind.ForStatement, + parent: parent, + tsNode: s, + statement: {} as cs.Statement + } as cs.ForStatement; + + if (s.initializer) { + if (ts.isVariableDeclarationList(s.initializer)) { + forStatement.initializer = this.visitVariableDeclarationList(forStatement, s.initializer); + } else { + forStatement.initializer = this.visitExpression(forStatement, s.initializer)!; + if (!forStatement.initializer) { + return null; + } + } + } + if (s.condition) { + forStatement.condition = this.visitExpression(forStatement, s.condition)!; + if (!forStatement.condition) { + return null; + } + } + + if (s.incrementor) { + forStatement.incrementor = this.visitExpression(forStatement, s.incrementor)!; + if (!forStatement.incrementor) { + return null; + } + } + + forStatement.statement = this.visitStatement(forStatement, s.statement)!; + if (!forStatement.statement) { + return null; + } + + return forStatement; + } + + private visitForOfStatement(parent: cs.Node, s: ts.ForOfStatement) { + const forEachStatement = { + nodeType: cs.SyntaxKind.ForEachStatement, + parent: parent, + tsNode: s, + statement: {} as cs.Statement, + expression: {} as cs.Expression, + initializer: {} as cs.VariableDeclaration + } as cs.ForEachStatement; + + if (ts.isVariableDeclarationList(s.initializer)) { + forEachStatement.initializer = this.visitVariableDeclarationList(forEachStatement, s.initializer); + } else { + forEachStatement.initializer = this.visitExpression(forEachStatement, s.initializer)!; + if (!forEachStatement.initializer) { + return null; + } + } + + forEachStatement.expression = this.visitExpression(forEachStatement, s.expression)!; + if (!forEachStatement.expression) { + return null; + } + forEachStatement.statement = this.visitStatement(forEachStatement, s.statement)!; + if (!forEachStatement.statement) { + return null; + } + + return forEachStatement; + } + private visitForInStatement(parent: cs.Node, s: ts.ForInStatement) { + // TODO: Detect raw object iteration and map it + const forEachStatement = { + nodeType: cs.SyntaxKind.ForEachStatement, + parent: parent, + tsNode: s, + statement: {} as cs.Statement, + expression: {} as cs.Expression, + initializer: {} as cs.VariableDeclaration + } as cs.ForEachStatement; + + if (ts.isVariableDeclarationList(s.initializer)) { + forEachStatement.initializer = this.visitVariableDeclarationList(forEachStatement, s.initializer); + } else { + forEachStatement.initializer = this.visitExpression(forEachStatement, s.initializer)!; + if (!forEachStatement.initializer) { + return null; + } + } + + forEachStatement.expression = this.visitExpression(forEachStatement, s.expression)!; + if (!forEachStatement.expression) { + return null; + } + forEachStatement.statement = this.visitStatement(forEachStatement, s.statement)!; + if (!forEachStatement.statement) { + return null; + } + + return forEachStatement; + } + + private visitBreakStatement(parent: cs.Node, s: ts.BreakStatement) { + const breakStatement = { + nodeType: cs.SyntaxKind.BreakStatement, + parent: parent, + tsNode: s + } as cs.BreakStatement; + + return breakStatement; + } + + private visitContinueStatement(parent: cs.Node, s: ts.ContinueStatement) { + const continueStatement = { + nodeType: cs.SyntaxKind.ContinueStatement, + parent: parent, + tsNode: s + } as cs.ContinueStatement; + + return continueStatement; + } + + private visitReturnStatement(parent: cs.Node, s: ts.ReturnStatement) { + const returnStatement = { + nodeType: cs.SyntaxKind.ReturnStatement, + parent: parent, + tsNode: s + } as cs.ReturnStatement; + + if (s.expression) { + returnStatement.expression = this.visitExpression(returnStatement, s.expression)!; + if (!returnStatement.expression) { + return null; + } + } + + return returnStatement; + } + + private visitSwitchStatement(parent: cs.Node, s: ts.SwitchStatement) { + const switchStatement = { + nodeType: cs.SyntaxKind.SwitchStatement, + parent: parent, + tsNode: s, + expression: {} as cs.Expression, + caseClauses: [] + } as cs.SwitchStatement; + + switchStatement.expression = this.visitExpression(switchStatement, s.expression)!; + if (!switchStatement.expression) { + return null; + } + + s.caseBlock.clauses.forEach(c => { + if (ts.isDefaultClause(c)) { + switchStatement.caseClauses.push(this.visitDefaultClause(switchStatement, c)); + } else { + const cl = this.visitCaseClause(switchStatement, c); + if (cl) { + switchStatement.caseClauses.push(cl); + } + } + }); + + return switchStatement; + } + + private visitDefaultClause(parent: cs.SwitchStatement, s: ts.DefaultClause): cs.DefaultClause { + const defaultClause = { + nodeType: cs.SyntaxKind.DefaultClause, + parent: parent, + tsNode: s, + statements: [] + } as cs.DefaultClause; + + s.statements.forEach(c => { + const statement = this.visitStatement(defaultClause, c); + if (statement) { + defaultClause.statements.push(statement); + } + }); + + return defaultClause; + } + + private visitCaseClause(parent: cs.SwitchStatement, s: ts.CaseClause) { + const caseClause = { + nodeType: cs.SyntaxKind.CaseClause, + parent: parent, + tsNode: s, + expression: {} as cs.Expression, + statements: [] + } as cs.CaseClause; + + caseClause.expression = this.visitExpression(caseClause, s.expression)!; + if (!caseClause.expression) { + return null; + } + s.statements.forEach(c => { + const statement = this.visitStatement(caseClause, c); + if (statement) { + caseClause.statements.push(statement); + } + }); + + return caseClause; + } + + private visitThrowStatement(parent: cs.Node, s: ts.ThrowStatement) { + const throwStatement = { + nodeType: cs.SyntaxKind.ThrowStatement, + parent: parent, + tsNode: s + } as cs.ThrowStatement; + + if (s.expression) { + throwStatement.expression = this.visitExpression(throwStatement, s.expression)!; + if (!throwStatement.expression) { + return null; + } + } + + return throwStatement; + } + private visitTryStatement(parent: cs.Node, s: ts.TryStatement) { + const tryStatement = { + nodeType: cs.SyntaxKind.TryStatement, + parent: parent, + tsNode: s, + tryBlock: {} as cs.Block + } as cs.TryStatement; + + tryStatement.tryBlock = this.visitBlock(tryStatement, s.tryBlock); + if (s.catchClause) { + tryStatement.catchClauses = [ + // TODO: detect type checks and convert them to catch clauses + this.visitCatchClause(tryStatement, s.catchClause) + ]; + } + + return tryStatement; + } + + private visitCatchClause(parent: cs.TryStatement, s: ts.CatchClause): cs.CatchClause { + const catchClause = { + nodeType: cs.SyntaxKind.CatchClause, + parent: parent, + tsNode: s, + variableDeclaration: {} as cs.VariableDeclaration, + block: {} as cs.Block + } as cs.CatchClause; + + catchClause.variableDeclaration = this.visitVariableDeclaration(catchClause, s.variableDeclaration!); + catchClause.block = this.visitBlock(catchClause, s.block); + + return catchClause; + } + + private visitMethodSignature( + parent: cs.ClassDeclaration | cs.InterfaceDeclaration, + classElement: ts.MethodSignature + ) { + const signature = this._context.typeChecker.getSignatureFromDeclaration(classElement); + const returnType = this._context.typeChecker.getReturnTypeOfSignature(signature!); + + const csMethod: cs.MethodDeclaration = { + parent: parent, + nodeType: cs.SyntaxKind.MethodDeclaration, + isAbstract: false, + isOverride: false, + isStatic: false, + isVirtual: false, + name: this._context.toPascalCase((classElement.name as ts.Identifier).text), + parameters: [], + returnType: this.createUnresolvedTypeNode(null, classElement.type ?? classElement, returnType), + visibility: cs.Visibility.None, + tsNode: classElement, + skipEmit: this.shouldSkip(classElement) + }; + + if (classElement.name) { + csMethod.documentation = this.visitDocumentation(classElement.name); + } + + csMethod.returnType.parent = csMethod; + + if (classElement.typeParameters && classElement.typeParameters.length > 0) { + csMethod.typeParameters = []; + classElement.typeParameters.forEach(p => { + const csp = { + parent: csMethod, + name: p.name.text, + nodeType: cs.SyntaxKind.TypeParameterDeclaration, + tsNode: p + } as cs.TypeParameterDeclaration; + if (p.constraint) { + csp.constraint = this.createUnresolvedTypeNode(csp, p.constraint); + } + + csMethod.typeParameters!.push(csp); + }); + } + + classElement.parameters.forEach(p => this.visitMethodParameter(csMethod, p)); + + parent.members.push(csMethod); + + this._context.registerSymbol(csMethod); + } + private mapVisibility(modifiers: ts.ModifiersArray | undefined): cs.Visibility { + if (modifiers) { + for (const m of modifiers) { + switch (m.kind) { + case ts.SyntaxKind.PublicKeyword: + return cs.Visibility.Public; + case ts.SyntaxKind.PrivateKeyword: + return cs.Visibility.Private; + case ts.SyntaxKind.ProtectedKeyword: + return cs.Visibility.Protected; + } + } + } + return cs.Visibility.Public; + } + private visitMethodParameter(parent: cs.MethodDeclarationBase, p: ts.ParameterDeclaration): void { + parent.parameters.push(this.makeParameter(parent, p)); + } + + private makeParameter(csMethod: cs.Node, p: ts.ParameterDeclaration): cs.ParameterDeclaration { + const symbol = this._context.typeChecker.getSymbolAtLocation(p.name); + const type = this._context.typeChecker.getTypeOfSymbolAtLocation(symbol!, p); + + const csParameter: cs.ParameterDeclaration = { + nodeType: cs.SyntaxKind.ParameterDeclaration, + name: p.name.getText(), + parent: csMethod, + type: this.createUnresolvedTypeNode(null, p.type ?? p, type), + tsNode: p, + params: !!p.dotDotDotToken + }; + csParameter.type!.parent = csParameter; + + if (p.questionToken) { + csParameter.type!.isOptional = true; + } + + if (p.initializer) { + csParameter.initializer = this.visitExpression(csParameter, p.initializer) ?? undefined; + if (csParameter.initializer && csParameter.initializer.nodeType === cs.SyntaxKind.NullLiteral) { + csParameter.type!.isNullable = true; + } + } else if (csParameter.type!.isOptional) { + if (this._context.isDefaultValueNull(type)) { + csParameter.type!.isNullable = true; + } + } + + if (p.name) { + csParameter.documentation = this.visitDocumentation(p.name); + } + return csParameter; + } + + private visitConstructorDeclaration(parent: cs.ClassDeclaration, classElement: ts.ConstructorDeclaration) { + const csConstructor: cs.ConstructorDeclaration = { + parent: parent, + nodeType: cs.SyntaxKind.ConstructorDeclaration, + name: '.ctor', + parameters: [], + isStatic: false, + visibility: this.mapVisibility(classElement.modifiers), + tsNode: classElement, + skipEmit: this.shouldSkip(classElement) + }; + + classElement.parameters.forEach(p => this.visitMethodParameter(csConstructor, p)); + + if (classElement.body) { + csConstructor.body = this.visitBlock(csConstructor, classElement.body); + const block = csConstructor.body as cs.Block; + if ( + block.statements.length > 0 && + block.statements[0].nodeType === cs.SyntaxKind.ExpressionStatement && + (block.statements[0] as cs.ExpressionStatement).expression.nodeType === + cs.SyntaxKind.InvocationExpression && + ((block.statements[0] as cs.ExpressionStatement).expression as cs.InvocationExpression).expression + .nodeType === cs.SyntaxKind.BaseLiteralExpression + ) { + csConstructor.baseConstructorArguments = ((block.statements[0] as cs.ExpressionStatement) + .expression as cs.InvocationExpression).arguments; + block.statements.shift(); + } + } + + parent.members.push(csConstructor); + } + + private visitExpression(parent: cs.Node, expression: ts.Expression): cs.Expression | null { + switch (expression.kind) { + case ts.SyntaxKind.PrefixUnaryExpression: + return this.visitPrefixUnaryExpression(parent, expression as ts.PrefixUnaryExpression); + case ts.SyntaxKind.PostfixUnaryExpression: + return this.visitPostfixUnaryExpression(parent, expression as ts.PostfixUnaryExpression); + case ts.SyntaxKind.NullKeyword: + return this.visitNullLiteral(parent, expression as ts.NullLiteral); + case ts.SyntaxKind.TrueKeyword: + case ts.SyntaxKind.FalseKeyword: + return this.visitBooleanLiteral(parent, expression as ts.BooleanLiteral); + case ts.SyntaxKind.ThisKeyword: + return this.visitThisExpression(parent, expression as ts.ThisExpression); + case ts.SyntaxKind.SuperKeyword: + return this.visitSuperLiteralExpression(parent, expression as ts.SuperExpression); + case ts.SyntaxKind.TypeOfExpression: + return this.visitTypeOfExpression(parent, expression as ts.TypeOfExpression); + case ts.SyntaxKind.AwaitExpression: + return this.visitAwaitExpression(parent, expression as ts.AwaitExpression); + case ts.SyntaxKind.BinaryExpression: + return this.visitBinaryExpression(parent, expression as ts.BinaryExpression); + case ts.SyntaxKind.ConditionalExpression: + return this.visitConditionalExpression(parent, expression as ts.ConditionalExpression); + case ts.SyntaxKind.FunctionExpression: + return this.visitFunctionExpression(parent, expression as ts.FunctionExpression); + case ts.SyntaxKind.ArrowFunction: + return this.visitArrowExpression(parent, expression as ts.ArrowFunction); + case ts.SyntaxKind.RegularExpressionLiteral: + return this.visitRegularExpressionLiteral(parent, expression as ts.RegularExpressionLiteral); + case ts.SyntaxKind.NumericLiteral: + return this.visitNumericLiteral(parent, expression as ts.NumericLiteral); + case ts.SyntaxKind.TemplateExpression: + return this.visitTemplateExpression(parent, expression as ts.TemplateExpression); + case ts.SyntaxKind.NoSubstitutionTemplateLiteral: + return this.visitNoSubstitutionTemplateLiteral(parent, expression as ts.NoSubstitutionTemplateLiteral); + case ts.SyntaxKind.TypeAssertionExpression: + return this.visitTypeAssertionExpression(parent, expression as ts.TypeAssertion); + case ts.SyntaxKind.ParenthesizedExpression: + return this.visitParenthesizedExpression(parent, expression as ts.ParenthesizedExpression); + case ts.SyntaxKind.ArrayLiteralExpression: + return this.visitArrayLiteralExpression(parent, expression as ts.ArrayLiteralExpression); + case ts.SyntaxKind.PropertyAccessExpression: + return this.visitPropertyAccessExpression(parent, expression as ts.PropertyAccessExpression); + case ts.SyntaxKind.ObjectLiteralExpression: + return this.visitObjectLiteralExpression(parent, expression as ts.ObjectLiteralExpression); + case ts.SyntaxKind.ElementAccessExpression: + return this.visitElementAccessExpression(parent, expression as ts.ElementAccessExpression); + case ts.SyntaxKind.CallExpression: + return this.visitCallExpression(parent, expression as ts.CallExpression); + case ts.SyntaxKind.NewExpression: + return this.visitNewExpression(parent, expression as ts.NewExpression); + case ts.SyntaxKind.AsExpression: + return this.visitAsExpression(parent, expression as ts.AsExpression); + case ts.SyntaxKind.NonNullExpression: + return this.visitNonNullExpression(parent, expression as ts.NonNullExpression); + case ts.SyntaxKind.Identifier: + return this.visitIdentifier(parent, expression as ts.Identifier); + case ts.SyntaxKind.StringLiteral: + return this.visitStringLiteral(parent, expression as ts.Identifier); + case ts.SyntaxKind.SpreadElement: + return this.visitSpreadElement(parent, expression as ts.SpreadElement); + + case ts.SyntaxKind.SyntheticReferenceExpression: + case ts.SyntaxKind.CommaListExpression: + case ts.SyntaxKind.ClassExpression: + case ts.SyntaxKind.JSDocTypeExpression: + case ts.SyntaxKind.JsxExpression: + case ts.SyntaxKind.OmittedExpression: + case ts.SyntaxKind.PartiallyEmittedExpression: + case ts.SyntaxKind.ImportKeyword: + case ts.SyntaxKind.DeleteExpression: + case ts.SyntaxKind.VoidExpression: + case ts.SyntaxKind.YieldExpression: + case ts.SyntaxKind.SyntheticExpression: + case ts.SyntaxKind.BigIntLiteral: + case ts.SyntaxKind.TaggedTemplateExpression: + default: + this._context.addTsNodeDiagnostics( + expression, + `Unsupported expression type ${ts.SyntaxKind[expression.kind]}`, + ts.DiagnosticCategory.Error + ); + break; + } + + return { + nodeType: cs.SyntaxKind.ToDoExpression, + parent: parent, + tsNode: expression + } as cs.Expression; + } + private visitSpreadElement(parent: cs.Node, expression: ts.SpreadElement) { + return this.visitExpression(parent, expression.expression); + } + + private visitPrefixUnaryExpression(parent: cs.Node, expression: ts.PrefixUnaryExpression) { + const csExpr = { + parent: parent, + tsNode: expression, + nodeType: cs.SyntaxKind.PrefixUnaryExpression, + operand: {} as cs.Expression, + operator: this.mapOperator(expression.operator) + } as cs.PrefixUnaryExpression; + + csExpr.operand = this.visitExpression(csExpr, expression.operand)!; + if (!csExpr.operand) { + return null; + } + + return csExpr; + } + + private visitPostfixUnaryExpression(parent: cs.Node, expression: ts.PostfixUnaryExpression) { + const csExpr = { + parent: parent, + tsNode: expression, + nodeType: cs.SyntaxKind.PostfixUnaryExpression, + operand: {} as cs.Expression, + operator: this.mapOperator(expression.operator) + } as cs.PostfixUnaryExpression; + + csExpr.operand = this.visitExpression(csExpr, expression.operand)!; + if (!csExpr.operand) { + return null; + } + + return csExpr; + } + + private visitNullLiteral(parent: cs.Node, expression: ts.NullLiteral) { + const csExpr = { + parent: parent, + tsNode: expression, + nodeType: cs.SyntaxKind.NullLiteral + } as cs.NullLiteral; + + return csExpr; + } + + private visitBooleanLiteral(parent: cs.Node, expression: ts.BooleanLiteral) { + const csExpr = { + parent: parent, + tsNode: expression, + nodeType: + expression.kind === ts.SyntaxKind.TrueKeyword ? cs.SyntaxKind.TrueLiteral : cs.SyntaxKind.FalseLiteral + } as cs.BooleanLiteral; + + return csExpr; + } + + private visitThisExpression(parent: cs.Node, expression: ts.ThisExpression) { + const csExpr = { + parent: parent, + tsNode: expression, + nodeType: cs.SyntaxKind.ThisLiteral + } as cs.ThisLiteral; + + return csExpr; + } + + private visitSuperLiteralExpression(parent: cs.Node, expression: ts.SuperExpression) { + const csExpr = { + parent: parent, + tsNode: expression, + nodeType: cs.SyntaxKind.BaseLiteralExpression + } as cs.BaseLiteralExpression; + + return csExpr; + } + + private visitTypeOfExpression(parent: cs.Node, expression: ts.TypeOfExpression) { + // AlphaTab.Core.TypeHelper.TypeOf(expr) + const csExpr = { + parent: parent, + tsNode: expression, + nodeType: cs.SyntaxKind.InvocationExpression, + arguments: [], + expression: {} as cs.Expression + } as cs.InvocationExpression; + + csExpr.expression = this.makeMemberAccess(csExpr, 'AlphaTab.Core.TypeHelper', 'TypeOf'); + const e = this.visitExpression(csExpr, expression.expression); + if (e) { + csExpr.arguments.push(e); + } + + return csExpr; + } + + private makeMemberAccess(parent: cs.Node, identifier: string, member: string): cs.Node { + const memberAccess = { + expression: { + nodeType: cs.SyntaxKind.Identifier, + text: identifier, + parent: null + } as cs.Identifier, + member: member, + parent: parent, + nodeType: cs.SyntaxKind.MemberAccessExpression + } as cs.MemberAccessExpression; + + memberAccess.parent = memberAccess; + + return memberAccess; + } + + private visitAwaitExpression(parent: cs.Node, expression: ts.AwaitExpression) { + const awaitExpression = { + parent: parent, + nodeType: cs.SyntaxKind.AwaitExpression, + tsNode: expression, + expression: {} as cs.Expression + } as cs.AwaitExpression; + + awaitExpression.expression = this.visitExpression(awaitExpression, expression.expression)!; + if (!awaitExpression.expression) { + return null; + } + + return awaitExpression; + } + + private visitBinaryExpression(parent: cs.Node, expression: ts.BinaryExpression) { + if (expression.operatorToken.kind === ts.SyntaxKind.InKeyword) { + // AlphaTab.Core.TypeHelper.In('Text', expr) + const csExpr = { + parent: parent, + tsNode: expression, + nodeType: cs.SyntaxKind.InvocationExpression, + arguments: [], + expression: {} as cs.Expression + } as cs.InvocationExpression; + + csExpr.expression = this.makeMemberAccess(csExpr, 'AlphaTab.Core.TypeHelper', 'In'); + + let e = this.visitExpression(csExpr, expression.left)!; + if (e) { + csExpr.arguments.push(e); + } + e = this.visitExpression(csExpr, expression.right)!; + if (e) { + csExpr.arguments.push(e); + } + + return csExpr; + } else if (expression.operatorToken.kind === ts.SyntaxKind.AsteriskAsteriskToken) { + this._context.addTsNodeDiagnostics( + expression, + 'Exponentiation expresssions are not yet supported', + ts.DiagnosticCategory.Error + ); + return { + nodeType: cs.SyntaxKind.ToDoExpression, + parent: parent, + tsNode: expression + } as cs.ToDoExpression; + } else if ( + expression.operatorToken.kind === ts.SyntaxKind.BarEqualsToken || + expression.operatorToken.kind === ts.SyntaxKind.CaretEqualsToken || + expression.operatorToken.kind === ts.SyntaxKind.LessThanLessThanEqualsToken || + expression.operatorToken.kind === ts.SyntaxKind.GreaterThanGreaterThanEqualsToken || + expression.operatorToken.kind === ts.SyntaxKind.AmpersandEqualsToken + ) { + // x >>= 3; => x = ((int)x) >> (3); + const assignment = { + parent: parent, + nodeType: cs.SyntaxKind.BinaryExpression, + tsNode: expression, + left: {} as cs.Expression, + right: {} as cs.Expression, + operator: '=' + } as cs.BinaryExpression; + assignment.left = this.visitExpression(assignment, expression.left)!; + if (!assignment.left) { + return null; + } + + const bitOp = (assignment.right = { + parent: assignment, + nodeType: cs.SyntaxKind.BinaryExpression, + tsNode: expression, + left: { + parent: null, + nodeType: cs.SyntaxKind.ParenthesizedExpression, + expression: {} as cs.Expression, + tsNode: expression + } as cs.ParenthesizedExpression, + right: { + parent: null, + nodeType: cs.SyntaxKind.ParenthesizedExpression, + expression: {} as cs.Expression, + tsNode: expression + } as cs.ParenthesizedExpression, + operator: '' + } as cs.BinaryExpression); + bitOp.right.parent = bitOp; + + switch (expression.operatorToken.kind) { + case ts.SyntaxKind.BarEqualsToken: + bitOp.operator = '|'; + break; + case ts.SyntaxKind.CaretEqualsToken: + bitOp.operator = '^'; + break; + case ts.SyntaxKind.LessThanLessThanEqualsToken: + bitOp.operator = '<<'; + break; + case ts.SyntaxKind.GreaterThanGreaterThanEqualsToken: + bitOp.operator = '>>'; + break; + case ts.SyntaxKind.AmpersandEqualsToken: + bitOp.operator = '&'; + break; + } + + const toInt = ((bitOp.left as cs.ParenthesizedExpression).expression = { + parent: bitOp, + nodeType: cs.SyntaxKind.CastExpression, + expression: {} as cs.Expression, + type: { + parent: null, + nodeType: cs.SyntaxKind.PrimitiveTypeNode, + type: cs.PrimitiveType.Int, + tsNode: expression + } as cs.PrimitiveTypeNode, + tsNode: expression + } as cs.CastExpression); + toInt.expression = this.visitExpression(assignment, expression.left)!; + if (!toInt.expression) { + return null; + } + + (bitOp.right as cs.ParenthesizedExpression).expression = this.visitExpression( + bitOp.right, + expression.right + )!; + + if (!(bitOp.right as cs.ParenthesizedExpression).expression) { + return null; + } + + return assignment; + } else { + const binaryExpression = { + parent: parent, + nodeType: cs.SyntaxKind.BinaryExpression, + tsNode: expression, + left: {} as cs.Expression, + right: {} as cs.Expression, + operator: this.mapOperator(expression.operatorToken.kind) + } as cs.BinaryExpression; + + binaryExpression.left = this.visitExpression(binaryExpression, expression.left)!; + if (!binaryExpression.left) { + return null; + } + + binaryExpression.right = this.visitExpression(binaryExpression, expression.right)!; + if (!binaryExpression.right) { + return null; + } + + switch (expression.operatorToken.kind) { + case ts.SyntaxKind.AmpersandToken: + case ts.SyntaxKind.GreaterThanGreaterThanToken: + case ts.SyntaxKind.LessThanLessThanToken: + case ts.SyntaxKind.BarToken: + binaryExpression.left = this.makeInt(binaryExpression.left); + binaryExpression.right = this.makeInt(binaryExpression.right); + break; + case ts.SyntaxKind.SlashToken: + if (expression.left.kind === ts.SyntaxKind.NumericLiteral && expression.right.kind === ts.SyntaxKind.NumericLiteral) { + binaryExpression.right = this.makeDouble(binaryExpression.right); + } + break; + } + + return binaryExpression; + } + } + + private makeDouble(expression: cs.Expression): cs.Expression { + // (double)(expr) + + const cast = { + parent: expression.parent, + tsNode: expression.tsNode, + nodeType: cs.SyntaxKind.CastExpression, + expression: { + parent: null, + tsNode: expression.tsNode, + nodeType: cs.SyntaxKind.ParenthesizedExpression, + expression: expression + } as cs.ParenthesizedExpression, + type: { + nodeType: cs.SyntaxKind.PrimitiveTypeNode, + parent: null, + tsNode: expression.tsNode, + type: cs.PrimitiveType.Double + } as cs.PrimitiveTypeNode + } as cs.CastExpression; + + cast.expression.parent = cast; + cast.type.parent = cast; + + return cast; + } + + private makeInt(expression: cs.Expression): cs.Expression { + switch (expression.nodeType) { + case cs.SyntaxKind.NumericLiteral: + if ((expression as cs.NumericLiteral).value.indexOf('.') === -1) { + return expression; + } + break; + } + // (int)(expr) + + const cast = { + parent: expression.parent, + tsNode: expression.tsNode, + nodeType: cs.SyntaxKind.CastExpression, + expression: { + parent: null, + tsNode: expression.tsNode, + nodeType: cs.SyntaxKind.ParenthesizedExpression, + expression: expression + } as cs.ParenthesizedExpression, + type: { + nodeType: cs.SyntaxKind.PrimitiveTypeNode, + parent: null, + tsNode: expression.tsNode, + type: cs.PrimitiveType.Int + } as cs.PrimitiveTypeNode + } as cs.CastExpression; + + cast.expression.parent = cast; + cast.type.parent = cast; + + return cast; + } + + private visitConditionalExpression(parent: cs.Node, expression: ts.ConditionalExpression) { + const conditionalExpression = { + parent: parent, + nodeType: cs.SyntaxKind.ConditionalExpression, + tsNode: expression, + condition: {} as cs.Expression, + whenTrue: {} as cs.Expression, + whenFalse: {} as cs.Expression + } as cs.ConditionalExpression; + + conditionalExpression.condition = this.visitExpression(conditionalExpression, expression.condition)!; + if (!conditionalExpression.condition) { + return null; + } + + conditionalExpression.whenTrue = this.visitExpression(conditionalExpression, expression.whenTrue)!; + if (!conditionalExpression.whenTrue) { + return null; + } + + conditionalExpression.whenFalse = this.visitExpression(conditionalExpression, expression.whenFalse)!; + if (!conditionalExpression.whenFalse) { + return null; + } + + return conditionalExpression; + } + + private makeTruthy(expression: cs.Node, force: boolean = false): cs.Expression { + if (!force) { + if (!this._context.isBooleanSmartCast(expression.tsNode!)) { + return expression; + } + } + + const type = this._context.typeChecker.getTypeAtLocation(expression.tsNode!); + if (type.flags & ts.TypeFlags.Boolean || type.flags & ts.TypeFlags.BooleanLiteral) { + return expression; + } + + // AlphaTab.Core.TypeHelper.IsTruthy(expression); + const call = { + parent: expression.parent, + tsNode: expression.tsNode, + nodeType: cs.SyntaxKind.InvocationExpression, + expression: {} as cs.Expression, + arguments: [] + } as cs.InvocationExpression; + + const access = call.expression = { + parent: call, + tsNode: expression.tsNode, + nodeType: cs.SyntaxKind.MemberAccessExpression, + expression: {} as cs.Expression, + member: 'IsTruthy' + } as cs.MemberAccessExpression; + + access.expression = { + parent: access, + tsNode: expression.tsNode, + nodeType: cs.SyntaxKind.Identifier, + text: 'AlphaTab.Core.TypeHelper' + } as cs.Identifier; + + expression.parent = call; + call.arguments.push(expression); + + return call; + } + + private visitFunctionExpression(parent: cs.Node, expression: ts.FunctionExpression) { + if (parent.nodeType === cs.SyntaxKind.ExpressionStatement) { + this._context.addTsNodeDiagnostics( + expression, + 'Local function declarations are not yet supported', + ts.DiagnosticCategory.Error + ); + return { + nodeType: cs.SyntaxKind.ToDoExpression, + parent: parent, + tsNode: expression + } as cs.ToDoExpression; + } else { + if (expression.name) { + this._context.addTsNodeDiagnostics( + expression, + 'Local functions with names have no matching kind in C#, name will be omitted', + ts.DiagnosticCategory.Warning + ); + } + + const lambdaExpression = { + nodeType: cs.SyntaxKind.LambdaExpression, + parent: parent, + tsNode: expression, + body: {} as cs.Expression, + parameters: [] + } as cs.LambdaExpression; + + expression.parameters.forEach(p => { + lambdaExpression.parameters.push(this.makeParameter(lambdaExpression, p)); + }); + + lambdaExpression.body = this.visitBlock(lambdaExpression, expression.body); + + return lambdaExpression; + } + } + + private visitArrowExpression(parent: cs.Node, expression: ts.ArrowFunction) { + const lambdaExpression = { + nodeType: cs.SyntaxKind.LambdaExpression, + parent: parent, + tsNode: expression, + body: {} as cs.Expression, + parameters: [] + } as cs.LambdaExpression; + + expression.parameters.forEach(p => { + lambdaExpression.parameters.push(this.makeParameter(lambdaExpression, p)); + }); + + if (ts.isBlock(expression.body)) { + lambdaExpression.body = this.visitBlock(lambdaExpression, expression.body); + } else { + lambdaExpression.body = this.visitExpression(lambdaExpression, expression.body)!; + if (!lambdaExpression.body) { + lambdaExpression.body = { + parent: lambdaExpression, + nodeType: cs.SyntaxKind.Block, + statements: [] + } as cs.Block; + } + } + + return lambdaExpression; + } + + private visitRegularExpressionLiteral(parent: cs.Node, expression: ts.RegularExpressionLiteral) { + // AlphaTab.Core.TypeHelper.CreateRegex(expr) + const csExpr = { + parent: parent, + tsNode: expression, + nodeType: cs.SyntaxKind.InvocationExpression, + arguments: [], + expression: {} as cs.Expression + } as cs.InvocationExpression; + + csExpr.expression = this.makeMemberAccess(csExpr, 'AlphaTab.Core.TypeHelper', 'CreateRegex'); + csExpr.arguments.push({ + parent: csExpr, + nodeType: cs.SyntaxKind.StringLiteral, + tsNode: expression, + text: expression.text + } as cs.StringLiteral); + + return csExpr; + } + + private visitNumericLiteral(parent: cs.Node, expression: ts.NumericLiteral) { + return { + parent: parent, + nodeType: cs.SyntaxKind.NumericLiteral, + tsNode: expression, + value: expression.text + } as cs.NumericLiteral; + } + + private visitNoSubstitutionTemplateLiteral( + parent: cs.Node, + expression: ts.NoSubstitutionTemplateLiteral + ): cs.Expression { + const stringLiteral = { + parent: parent, + tsNode: expression, + nodeType: cs.SyntaxKind.StringLiteral, + text: expression.text + } as cs.StringLiteral; + + return stringLiteral; + } + + private visitTemplateExpression(parent: cs.Node, expression: ts.TemplateExpression) { + const templateString = { + parent: parent, + nodeType: cs.SyntaxKind.StringTemplateExpression, + tsNode: expression, + chunks: [] + } as cs.StringTemplateExpression; + + if (expression.head.text) { + templateString.chunks.push({ + parent: templateString, + nodeType: cs.SyntaxKind.StringLiteral, + tsNode: expression.head, + text: expression.head.text + } as cs.StringLiteral); + } + + expression.templateSpans.forEach(s => { + const e = this.visitExpression(templateString, s.expression); + if (e) { + templateString.chunks.push(e); + } + templateString.chunks.push({ + parent: templateString, + nodeType: cs.SyntaxKind.StringLiteral, + tsNode: s, + text: s.literal.text + } as cs.StringLiteral); + }); + + return templateString; + } + + private visitTypeAssertionExpression(parent: cs.Node, expression: ts.TypeAssertion) { + const csExpr = { + parent: parent, + tsNode: expression, + nodeType: cs.SyntaxKind.IsExpression, + type: this.createUnresolvedTypeNode(null, expression.type), + expression: {} as cs.Expression + } as cs.IsExpression; + + // TODO: introduce new name for typechecked expression and use it within the current block + csExpr.expression = this.visitExpression(csExpr, expression.expression)!; + if (!csExpr.expression) { + return csExpr.expression; + } + + return csExpr; + } + + private visitParenthesizedExpression(parent: cs.Node, expression: ts.ParenthesizedExpression) { + const csExpr = { + parent: parent, + tsNode: expression, + nodeType: cs.SyntaxKind.ParenthesizedExpression, + expression: {} as cs.Expression + } as cs.ParenthesizedExpression; + + csExpr.expression = this.visitExpression(csExpr, expression.expression)!; + if (!csExpr.expression) { + return null; + } + + return csExpr; + } + + private visitArrayLiteralExpression(parent: cs.Node, expression: ts.ArrayLiteralExpression) { + if (this.isMapInitializer(expression)) { + const csExpr = { + parent: parent, + tsNode: expression, + nodeType: cs.SyntaxKind.InvocationExpression, + arguments: [], + expression: {} as cs.Expression + } as cs.InvocationExpression; + + csExpr.expression = this.makeMemberAccess(csExpr, 'AlphaTab.Core.TypeHelper', 'CreateMapEntry'); + + expression.elements.forEach(e => { + const ex = this.visitExpression(csExpr, e); + if (ex) { + csExpr.arguments.push(ex); + } + }); + + return csExpr; + } else { + const csExpr = { + parent: parent, + tsNode: expression, + nodeType: cs.SyntaxKind.ArrayCreationExpression, + values: [] + } as cs.ArrayCreationExpression; + + const contextual = this._context.typeChecker.getContextualType(expression); + if (!contextual || !contextual.symbol || contextual.symbol.name !== 'Iterable') { + csExpr.type = this.createUnresolvedTypeNode(csExpr, expression, contextual); + } + + expression.elements.forEach(e => { + const ex = this.visitExpression(csExpr, e); + if (ex) { + csExpr.values!.push(ex); + } + }); + + return csExpr; + } + } + private isMapInitializer(expression: ts.ArrayLiteralExpression) { + const isCandidate = + expression.elements.length === 2 && + expression.parent.kind === ts.SyntaxKind.ArrayLiteralExpression && + expression.parent.parent.kind === ts.SyntaxKind.NewExpression; + if (!isCandidate) { + return false; + } + + switch (expression.parent.parent.parent.kind) { + case ts.SyntaxKind.PropertyDeclaration: + return this.getDeclarationOrAssignmentType()!.symbol.name === 'Map'; + } + + return false; + } + + private visitPropertyAccessExpression(parent: cs.Node, expression: ts.PropertyAccessExpression) { + const memberAccess = { + expression: {} as cs.Expression, + member: this._context.toPascalCase(expression.name.text), + parent: parent, + tsNode: expression, + tsSymbol: this._context.typeChecker.getSymbolAtLocation(expression), + nodeType: cs.SyntaxKind.MemberAccessExpression + } as cs.MemberAccessExpression; + + if ( + memberAccess.tsSymbol && + expression.parent.kind === ts.SyntaxKind.CaseClause && + (expression.parent as ts.CaseClause).expression === expression + ) { + this._context.registerSymbolAsConst(memberAccess.tsSymbol); + } + + if (memberAccess.tsSymbol) { + const parentSymbol = (memberAccess.tsSymbol as any).parent as ts.Symbol; + if (parentSymbol) { + switch (parentSymbol.name) { + case 'Array': + switch (memberAccess.tsSymbol!.name) { + case 'length': + memberAccess.member = 'Count'; + break; + case 'push': + memberAccess.member = 'Add'; + break; + } + break; + } + } + } + + if (expression.questionDotToken) { + memberAccess.nullSafe = true; + } + + memberAccess.expression = this.visitExpression(memberAccess, expression.expression)!; + if (!memberAccess.expression) { + return null; + } + + return this.wrapToSmartCast(parent, memberAccess, expression); + } + + private visitObjectLiteralExpression(parent: cs.Node, expression: ts.ObjectLiteralExpression) { + const objectLiteral = { + parent: parent, + tsNode: expression, + nodeType: cs.SyntaxKind.AnonymousObjectCreationExpression, + properties: [] + } as cs.AnonymousObjectCreationExpression; + + expression.properties.forEach(p => { + if (ts.isPropertyAssignment(p)) { + const assignment = { + parent: objectLiteral, + nodeType: cs.SyntaxKind.AnonymousObjectProperty, + name: p.name.getText(), + value: {} as cs.Expression + } as cs.AnonymousObjectProperty; + + assignment.value = this.visitExpression(objectLiteral, p.initializer)!; + if (assignment.value) { + objectLiteral.properties.push(assignment); + } + + } else if (ts.isShorthandPropertyAssignment(p)) { + const assignment = { + parent: objectLiteral, + nodeType: cs.SyntaxKind.AnonymousObjectProperty, + name: p.name.getText(), + value: {} as cs.Expression + } as cs.AnonymousObjectProperty; + + assignment.value = this.visitExpression(objectLiteral, p.objectAssignmentInitializer!)!; + if (assignment.value) { + objectLiteral.properties.push(assignment); + } + + } else if (ts.isSpreadAssignment(p)) { + this._context.addTsNodeDiagnostics(p, 'Spread operator not supported', ts.DiagnosticCategory.Error); + } else if (ts.isMethodDeclaration(p)) { + const assignment = { + parent: objectLiteral, + nodeType: cs.SyntaxKind.AnonymousObjectProperty, + name: p.name.getText(), + value: {} as cs.Expression + } as cs.AnonymousObjectProperty; + + const lambda = { + nodeType: cs.SyntaxKind.LambdaExpression, + parent: objectLiteral, + parameters: [], + body: {} as cs.Block, + tsNode: p + } as cs.LambdaExpression; + + p.parameters.forEach(param => lambda.parameters.push(this.makeParameter(lambda, param))); + lambda.body = this.visitBlock(parent, p.body!); + + assignment.value = lambda; + + objectLiteral.properties.push(assignment); + } else if (ts.isGetAccessorDeclaration(p)) { + this._context.addTsNodeDiagnostics( + p, + 'Get accessor declarations in object literals not supported', + ts.DiagnosticCategory.Error + ); + } else if (ts.isSetAccessorDeclaration(p)) { + this._context.addTsNodeDiagnostics( + p, + 'Set accessor declarations in object literals not supported', + ts.DiagnosticCategory.Error + ); + } + }); + + return objectLiteral; + } + + private visitElementAccessExpression(parent: cs.Node, expression: ts.ElementAccessExpression) { + // Enum[value] => value.ToString() + const symbol = this._context.typeChecker.getSymbolAtLocation(expression.expression); + if (symbol && symbol.flags & ts.SymbolFlags.Enum) { + const callExpr = { + parent: parent, + arguments: [], + expression: {} as cs.Expression, + nodeType: cs.SyntaxKind.InvocationExpression, + tsNode: expression + } as cs.InvocationExpression; + + const memberAccess = (callExpr.expression = { + expression: {} as cs.Expression, + member: 'ToString', + parent: callExpr, + tsNode: expression, + nodeType: cs.SyntaxKind.MemberAccessExpression + } as cs.MemberAccessExpression); + + memberAccess.expression = this.visitExpression(memberAccess, expression.argumentExpression)!; + if (!memberAccess.expression) { + return null; + } + + return callExpr; + } + + const elementAccess = { + expression: {} as cs.Expression, + argumentExpression: {} as cs.Expression, + parent: parent, + tsNode: expression, + nodeType: cs.SyntaxKind.ElementAccessExpression + } as cs.ElementAccessExpression; + + elementAccess.expression = this.visitExpression(elementAccess, expression.expression)!; + if (!elementAccess.expression) { + return null; + } + + const argumentExpression = this.visitExpression(elementAccess, expression.argumentExpression)!; + if (!argumentExpression) { + return null; + } + + const csArg = { + expression: {} as cs.Expression, + nodeType: cs.SyntaxKind.CastExpression, + parent: parent, + tsNode: expression.argumentExpression, + type: { + nodeType: cs.SyntaxKind.PrimitiveTypeNode, + type: cs.PrimitiveType.Int + } as cs.PrimitiveTypeNode + } as cs.CastExpression; + elementAccess.argumentExpression = csArg; + + const par = { + nodeType: cs.SyntaxKind.ParenthesizedExpression, + parent: csArg, + expression: argumentExpression + } as cs.ParenthesizedExpression; + argumentExpression.parent = par; + csArg.expression = par; + + return this.wrapToSmartCast(parent, elementAccess, expression); + } + + private visitCallExpression(parent: cs.Node, expression: ts.CallExpression) { + if (this.isBind(expression)) { + return this.visitExpression(parent, (expression.expression as ts.PropertyAccessExpression).expression); + } + + const callExpression = { + arguments: [], + expression: {} as cs.Expression, + parent: parent, + tsNode: expression, + nodeType: cs.SyntaxKind.InvocationExpression + } as cs.InvocationExpression; + + callExpression.expression = this.visitExpression(callExpression, expression.expression)!; + if (!callExpression.expression) { + return null; + } + + if (ts.isPropertyAccessExpression(expression.expression) && expression.expression.name.text === 'setPrototypeOf') { + return null; + } + + expression.arguments.forEach(a => { + const e = this.visitExpression(callExpression, a); + if (e) { + callExpression.arguments.push(e); + } + }); + + if (expression.typeArguments) { + callExpression.typeArguments = []; + expression.typeArguments.forEach(a => + callExpression.typeArguments!.push(this.createUnresolvedTypeNode(callExpression, a)) + ); + } + + return this.makeTruthy(callExpression); + } + private isBind(expression: ts.CallExpression) { + if (ts.isPropertyAccessExpression(expression.expression)) { + return expression.expression.name.text === 'bind' && expression.arguments.length === 1; + } + return false; + } + + private visitNewExpression(parent: cs.Node, expression: ts.NewExpression) { + const symbol = this._context.typeChecker.getSymbolAtLocation(expression.expression); + let type: ts.Type | undefined = undefined; + if (symbol) { + type = this._context.typeChecker.getTypeOfSymbolAtLocation(symbol, expression.expression) ?? null; + } + + const csType = this.createUnresolvedTypeNode(null, expression.expression, type, symbol); + const newExpression = { + arguments: [], + type: csType, + parent: parent, + tsNode: expression, + nodeType: cs.SyntaxKind.NewExpression + } as cs.NewExpression; + + newExpression.type.parent = newExpression; + if (expression.arguments) { + expression.arguments.forEach(a => { + const e = this.visitExpression(newExpression, a); + if (e) { + newExpression.arguments.push(e) + } + }); + } + + if (expression.typeArguments) { + csType.typeArguments = []; + expression.typeArguments.forEach(a => + csType.typeArguments!.push(this.createUnresolvedTypeNode(newExpression, a)) + ); + } else { + const typeAtLocation = this._context.typeChecker.getTypeAtLocation(expression) as ts.TypeReference; + if (typeAtLocation.typeArguments && typeAtLocation.typeArguments.length > 0) { + const declarationOrAssignmentType = this.getDeclarationOrAssignmentType(); + const actualTypeArguments = (declarationOrAssignmentType as ts.TypeReference)?.typeArguments; + // we have some inferred type arguments here + if (actualTypeArguments && actualTypeArguments.length === typeAtLocation.typeArguments.length) { + csType.typeArguments = []; + actualTypeArguments.forEach(a => { + csType.typeArguments!.push( + this.createUnresolvedTypeNode(newExpression, expression.expression, a) + ); + }); + } else { + this._context.addTsNodeDiagnostics( + expression, + 'Cannot infer type arguments on generic object creation', + ts.DiagnosticCategory.Error + ); + } + } + } + + if (type && type.symbol && type.symbol.name == 'ArrayConstructor' && newExpression.arguments.length === 1) { + const toInt = { + parent: newExpression, + nodeType: cs.SyntaxKind.CastExpression, + expression: {} as cs.Expression, + type: { + parent: null, + nodeType: cs.SyntaxKind.PrimitiveTypeNode, + type: cs.PrimitiveType.Int, + tsNode: expression + } as cs.PrimitiveTypeNode, + tsNode: expression + } as cs.CastExpression; + toInt.expression = newExpression.arguments[0]; + toInt.expression.parent = toInt; + + const newArray = { + parent: newExpression, + nodeType: cs.SyntaxKind.ArrayCreationExpression, + sizeExpression: toInt, + type: csType.typeArguments![0], + } as cs.ArrayCreationExpression; + toInt.parent = newExpression; + + newExpression.arguments = [newArray]; + } + + return newExpression; + } + private getDeclarationOrAssignmentType(): ts.Type | undefined { + return this._declarationOrAssignmentTypeStack.length === 0 + ? undefined + : this._declarationOrAssignmentTypeStack[this._declarationOrAssignmentTypeStack.length - 1]; + } + + private visitAsExpression(parent: cs.Node, expression: ts.AsExpression) { + const castExpression = { + type: this.createUnresolvedTypeNode(null, expression.type), + expression: {} as cs.Expression, + parent: parent, + tsNode: expression, + nodeType: cs.SyntaxKind.CastExpression + } as cs.CastExpression; + + castExpression.type.parent = castExpression; + castExpression.expression = this.visitExpression(castExpression, expression.expression)!; + if (!castExpression.expression) { + return null; + } + + return castExpression; + } + + private visitNonNullExpression(parent: cs.Node, expression: ts.NonNullExpression) { + if (this._context.isValueTypeExpression(expression)) { + const valueAccessExpression = { + expression: {} as cs.Expression, + member: 'Value', + parent: parent, + tsNode: expression, + nodeType: cs.SyntaxKind.MemberAccessExpression + } as cs.MemberAccessExpression; + + valueAccessExpression.expression = this.visitExpression(valueAccessExpression, expression.expression)!; + if (!valueAccessExpression.expression) { + return null; + } + + return valueAccessExpression; + } else { + const nonNullExpression = { + expression: {} as cs.Expression, + parent: parent, + tsNode: expression, + nodeType: cs.SyntaxKind.NonNullExpression + } as cs.NonNullExpression; + + nonNullExpression.expression = this.visitExpression(nonNullExpression, expression.expression)!; + if (!nonNullExpression.expression) { + return null; + } + + return nonNullExpression; + } + } + + private visitIdentifier(parent: cs.Node, expression: ts.Identifier) { + if (expression.text === 'undefined') { + return { + parent: parent, + tsNode: expression, + nodeType: cs.SyntaxKind.NullLiteral + } as cs.NullLiteral; + } + const identifier = { + parent: parent, + tsNode: expression, + tsSymbol: this._context.typeChecker.getSymbolAtLocation(expression), + nodeType: cs.SyntaxKind.Identifier, + text: expression.text + } as cs.Identifier; + + return this.wrapToSmartCast(parent, identifier, expression); + } + + private wrapToSmartCast(parent: cs.Node, node: cs.Node, expression: ts.Expression): cs.Expression { + if (node.tsSymbol) { + if ( + (node.tsSymbol.flags & ts.SymbolFlags.Property) === ts.SymbolFlags.Property || + (node.tsSymbol.flags & ts.SymbolFlags.Variable) === ts.SymbolFlags.Variable || + (node.tsSymbol.flags & ts.SymbolFlags.EnumMember) === ts.SymbolFlags.EnumMember || + (node.tsSymbol.flags & ts.SymbolFlags.FunctionScopedVariable) === + ts.SymbolFlags.FunctionScopedVariable || + (node.tsSymbol.flags & ts.SymbolFlags.BlockScopedVariable) === ts.SymbolFlags.BlockScopedVariable + ) { + let smartCastType = this._context.getSmartCastType(expression); + if (smartCastType) { + if (smartCastType.flags & ts.TypeFlags.Boolean) { + return this.makeTruthy(node, true); + } + + const paren = { + expression: {} as cs.Expression, + parent: parent, + tsNode: expression, + nodeType: cs.SyntaxKind.ParenthesizedExpression + } as cs.ParenthesizedExpression; + + const castExpression = (paren.expression = { + type: this.createUnresolvedTypeNode( + null, + expression, + smartCastType, + smartCastType.symbol ?? node.tsSymbol + ), + expression: node, + parent: paren, + tsNode: expression, + nodeType: cs.SyntaxKind.CastExpression + } as cs.CastExpression); + + castExpression.type.parent = castExpression; + castExpression.expression.parent = castExpression; + + return paren; + } + + const isValueTypeNotNullSmartCast = this._context.isValueTypeNotNullSmartCast(expression); + if (isValueTypeNotNullSmartCast !== undefined) { + if (isValueTypeNotNullSmartCast) { + return { + parent: parent, + nodeType: cs.SyntaxKind.MemberAccessExpression, + tsNode: expression, + expression: node, + member: 'Value' + } as cs.MemberAccessExpression; + } else { + return { + parent: parent, + nodeType: cs.SyntaxKind.NonNullExpression, + tsNode: expression, + expression: node + } as cs.NonNullExpression; + } + } + } + } + + return this.makeTruthy(node); + } + + private visitStringLiteral(parent: cs.Node, expression: ts.Identifier) { + const stringLiteral = { + parent: parent, + tsNode: expression, + nodeType: cs.SyntaxKind.StringLiteral, + text: expression.text + } as cs.StringLiteral; + + return stringLiteral; + } + + private removeExtension(fileName: string) { + return fileName.substring(0, fileName.lastIndexOf('.')); + } + + private mapOperator(operator: ts.SyntaxKind): string { + switch (operator) { + case ts.SyntaxKind.PlusPlusToken: + return '++'; + case ts.SyntaxKind.MinusMinusToken: + return '--'; + case ts.SyntaxKind.TildeToken: + return '~'; + case ts.SyntaxKind.ExclamationToken: + return '!'; + + case ts.SyntaxKind.CommaToken: + return ','; + + case ts.SyntaxKind.QuestionQuestionToken: + return '??'; + + case ts.SyntaxKind.AsteriskToken: + return '*'; + case ts.SyntaxKind.SlashToken: + return '/'; + case ts.SyntaxKind.PercentToken: + return '%'; + + case ts.SyntaxKind.PlusToken: + return '+'; + case ts.SyntaxKind.MinusToken: + return '-'; + + case ts.SyntaxKind.LessThanLessThanToken: + return '<<'; + case ts.SyntaxKind.GreaterThanGreaterThanToken: + return '>>'; + case ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken: + return '>>>'; + + case ts.SyntaxKind.LessThanToken: + return '<'; + case ts.SyntaxKind.LessThanEqualsToken: + return '<='; + case ts.SyntaxKind.GreaterThanToken: + return '>'; + case ts.SyntaxKind.GreaterThanEqualsToken: + return '>='; + case ts.SyntaxKind.InstanceOfKeyword: + return 'is'; + + case ts.SyntaxKind.EqualsEqualsToken: + case ts.SyntaxKind.EqualsEqualsEqualsToken: + return '=='; + case ts.SyntaxKind.ExclamationEqualsEqualsToken: + case ts.SyntaxKind.ExclamationEqualsToken: + return '!='; + + case ts.SyntaxKind.AmpersandToken: + return '&'; + case ts.SyntaxKind.BarToken: + return '|'; + case ts.SyntaxKind.CaretToken: + return '^'; + + case ts.SyntaxKind.AmpersandAmpersandToken: + return '&&'; + case ts.SyntaxKind.BarBarToken: + return '||'; + + case ts.SyntaxKind.EqualsToken: + return '='; + case ts.SyntaxKind.PlusEqualsToken: + return '+='; + case ts.SyntaxKind.MinusEqualsToken: + return '-='; + case ts.SyntaxKind.AsteriskAsteriskEqualsToken: + return '??='; + case ts.SyntaxKind.AsteriskEqualsToken: + return '*='; + case ts.SyntaxKind.SlashEqualsToken: + return '/='; + case ts.SyntaxKind.PercentEqualsToken: + return '%='; + case ts.SyntaxKind.AmpersandEqualsToken: + return '&='; + case ts.SyntaxKind.BarEqualsToken: + return '|='; + case ts.SyntaxKind.CaretEqualsToken: + return '^='; + case ts.SyntaxKind.LessThanLessThanEqualsToken: + return '<<='; + case ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken: + return '>>>='; + case ts.SyntaxKind.GreaterThanGreaterThanEqualsToken: + return '>>='; + } + return ''; + } +} diff --git a/src.compiler/csharp/CSharpEmitter.ts b/src.compiler/csharp/CSharpEmitter.ts new file mode 100644 index 000000000..a890bd7bb --- /dev/null +++ b/src.compiler/csharp/CSharpEmitter.ts @@ -0,0 +1,31 @@ +import * as ts from 'typescript'; +import CSharpAstTransformer from './CSharpAstTransformer'; +import CSharpEmitterContext from './CSharpEmitterContext'; +import CSharpAstPrinter from './CSharpAstPrinter'; + +export default function emit(program: ts.Program): ts.Diagnostic[] { + const diagnostics: ts.Diagnostic[] = []; + + const context = new CSharpEmitterContext(program); + + program.getRootFileNames().forEach(file => { + const sourceFile = program.getSourceFile(file)!; + const transformer = new CSharpAstTransformer(sourceFile, context); + transformer.transform(); + }); + + context.resolveAllUnresolvedTypeNodes(); + context.rewriteVisibilities(); + + if (!context.hasErrors) { + context.csharpFiles.forEach(file => { + const printer = new CSharpAstPrinter(file, context); + printer.print(); + diagnostics.push(...printer.diagnostics); + }) + } + + diagnostics.push(...context.diagnostics); + + return diagnostics; +} \ No newline at end of file diff --git a/src.compiler/csharp/CSharpEmitterContext.ts b/src.compiler/csharp/CSharpEmitterContext.ts new file mode 100644 index 000000000..8cf1055e1 --- /dev/null +++ b/src.compiler/csharp/CSharpEmitterContext.ts @@ -0,0 +1,1214 @@ +import * as cs from './CSharpAst'; +import * as ts from 'typescript'; +import * as path from 'path'; + +type SymbolKey = string; + +export default class CSharpEmitterContext { + public isNullableString(type: ts.Type) { + if (type.isUnion()) { + type = this.typeChecker.getNonNullableType(type); + } + return ((type.flags & ts.TypeFlags.String) || (type.flags & ts.TypeFlags.StringLiteral)); + } + private _fileLookup: Map = new Map(); + private _symbolLookup: Map = new Map(); + private _exportedSymbols: Map = new Map(); + private _symbolConst: Map = new Map(); + + private _diagnostics: ts.Diagnostic[] = []; + private _unresolvedTypeNodes: cs.UnresolvedTypeNode[] = []; + private _program: ts.Program; + public typeChecker: ts.TypeChecker; + + public csharpFiles: cs.SourceFile[] = []; + public get compilerOptions(): ts.CompilerOptions { + return this._program.getCompilerOptions(); + } + public get diagnostics(): readonly ts.Diagnostic[] { + return this._diagnostics; + } + public hasErrors: boolean = false; + + public getType(n: ts.Node): ts.Type { + return this.typeChecker.getTypeAtLocation(n); + } + + public isTypeAssignable(targetType: ts.Type, actualType: ts.Type) { + if (targetType.flags === ts.TypeFlags.Any || targetType.flags === ts.TypeFlags.Unknown) { + return true; + } + if (actualType.isClassOrInterface()) { + return (this.typeChecker as any).isTypeAssignableTo(actualType, targetType); + } + return false; + } + + public registerUnresolvedTypeNode(unresolved: cs.UnresolvedTypeNode) { + this._unresolvedTypeNodes.push(unresolved); + } + + public getSymbolName(expr: cs.Node): string | undefined { + const symbolKey = this.getSymbolKey(expr.tsSymbol); + if (expr.tsSymbol) { + if (this._symbolLookup.has(symbolKey)) { + const csSymbol = this._symbolLookup.get(symbolKey)!; + switch (csSymbol.nodeType) { + case cs.SyntaxKind.ClassDeclaration: + case cs.SyntaxKind.InterfaceDeclaration: + case cs.SyntaxKind.EnumDeclaration: + case cs.SyntaxKind.DelegateDeclaration: + return this.getFullName(csSymbol as cs.NamedTypeDeclaration); + } + return csSymbol.name; + } else if ( + expr.tsSymbol.flags & ts.SymbolFlags.Class || + expr.tsSymbol.flags & ts.SymbolFlags.Interface || + expr.tsSymbol.flags & ts.SymbolFlags.ConstEnum || + expr.tsSymbol.flags & ts.SymbolFlags.RegularEnum + ) { + return this.buildCoreNamespace(expr.tsSymbol) + expr.tsSymbol.name; + } else if (expr.tsSymbol.flags & ts.SymbolFlags.Function) { + if (this.isTestFunction(expr.tsSymbol)) { + return 'AlphaTab.Test.Globals.' + this.toPascalCase(expr.tsSymbol.name); + } + return 'AlphaTab.Core.Globals.' + this.toPascalCase(expr.tsSymbol.name); + } else if ( + (expr.tsSymbol.flags & ts.SymbolFlags.FunctionScopedVariable && this.isGlobalVariable(expr.tsSymbol)) || + (expr.tsSymbol.flags & ts.SymbolFlags.NamespaceModule && this.isKnownModule(expr.tsSymbol)) + ) { + return 'AlphaTab.Core.Globals.' + this.toPascalCase(expr.tsSymbol.name); + } + } + return undefined; + } + private isTestFunction(tsSymbol: ts.Symbol): boolean { + return tsSymbol.valueDeclaration?.getSourceFile().fileName.indexOf('jasmine') !== -1 ?? false; + } + private isKnownModule(tsSymbol: ts.Symbol): boolean { + switch (tsSymbol.name) { + case 'globalThis': + return true; + default: + return false; + } + } + public isGlobalVariable(symbol: ts.Symbol) { + if ((symbol.flags & ts.SymbolFlags.FunctionScopedVariable) === 0 || !symbol.valueDeclaration) { + return false; + } + + if ( + symbol.valueDeclaration.parent.kind === ts.SyntaxKind.VariableDeclarationList && + symbol.valueDeclaration.parent.parent.kind === ts.SyntaxKind.VariableStatement && + symbol.valueDeclaration.parent.parent.parent.kind === ts.SyntaxKind.SourceFile + ) { + return true; + } + + return false; + } + + public getFullName(type: cs.NamedTypeDeclaration): string { + if (!type.parent) { + return ''; + } + switch (type.parent.nodeType) { + case cs.SyntaxKind.ClassDeclaration: + case cs.SyntaxKind.InterfaceDeclaration: + case cs.SyntaxKind.EnumDeclaration: + case cs.SyntaxKind.DelegateDeclaration: + return this.getFullName(type.parent as cs.NamedTypeDeclaration) + '.' + type.name; + case cs.SyntaxKind.NamespaceDeclaration: + return (type.parent as cs.NamespaceDeclaration).namespace + '.' + type.name; + } + return ''; + } + + public resolveAllUnresolvedTypeNodes() { + for (let node of this._unresolvedTypeNodes) { + let resolved = this.resolveType(node); + if (!resolved) { + resolved = this.resolveType(node); + this.addCsNodeDiagnostics(node, 'Could not resolve type', ts.DiagnosticCategory.Error); + } + } + } + + public addCsNodeDiagnostics(node: cs.Node, message: string, category: ts.DiagnosticCategory) { + if (node.tsNode) { + this.addTsNodeDiagnostics(node.tsNode, message, category); + } else { + this.addDiagnostic({ + category: category, + code: 1, + file: undefined, + messageText: message, + start: undefined, + length: undefined + }); + } + } + + public addTsNodeDiagnostics(node: ts.Node, message: string, category: ts.DiagnosticCategory) { + const file = this.findTsSourceFile(node); + const start = node.getStart(file); + const end = node.getEnd(); + this.addDiagnostic({ + category: category, + code: 1, + file: file, + messageText: message, + start: start, + length: end - start + }); + } + + private findTsSourceFile(tsNode: ts.Node): ts.SourceFile { + if (ts.isSourceFile(tsNode)) { + return tsNode; + } + return this.findTsSourceFile(tsNode.parent); + } + + public constructor(program: ts.Program) { + this._program = program; + this.typeChecker = program.getTypeChecker(); + } + + public addDiagnostic(diagnostics: ts.Diagnostic) { + this._diagnostics.push(diagnostics); + if (diagnostics.category === ts.DiagnosticCategory.Error) { + this.hasErrors = true; + } + } + + public addSourceFile(csharpFile: cs.SourceFile) { + this.csharpFiles.push(csharpFile); + this._fileLookup.set(csharpFile.tsNode as ts.SourceFile, csharpFile); + } + + public resolveType(node: cs.UnresolvedTypeNode): cs.TypeNode | null { + if (node.nodeType !== cs.SyntaxKind.UnresolvedTypeNode) { + return node; + } + + if (!node.tsNode) { + throw new Error('Node must be set for all types'); + } + + let resolved: cs.TypeNode | null = null; + if (!node.tsType) { + node.tsType = this.typeChecker.getTypeAtLocation(node.tsNode); + } + + if (node.tsType) { + resolved = this.getTypeFromTsType(node, node.tsType, node.tsSymbol, node.typeArguments); + } + + if (resolved) { + const wasOptional = node.isOptional; + const wasNullable = node.isNullable; + for (const prop of Object.getOwnPropertyNames(node)) { + delete (node as any)[prop]; + } + for (const prop of Object.getOwnPropertyNames(resolved)) { + (node as any)[prop] = (resolved as any)[prop]; + } + if (wasOptional) { + node.isOptional = true; + } + if (wasNullable) { + node.isNullable = true; + } + return node; + } + + return null; + } + + public isBooleanType(type: ts.Type) { + if (!type) { + return false; + } + return type.symbol && type.symbol.name === 'Boolean'; + } + + public getArrayElementType(type: ts.Type | undefined): ts.Type | null { + if (!type) { + return null; + } + + if (type.symbol && type.symbol.name === 'Array') { + return (type as ts.TypeReference).typeArguments![0]; + } + + if (type.isUnion()) { + const nonNullable = this.typeChecker.getNonNullableType(type); + return this.getArrayElementType(nonNullable); + } + + return null; + } + + private getTypeFromTsType( + node: cs.Node, + tsType: ts.Type, + tsSymbol?: ts.Symbol, + typeArguments?: cs.UnresolvedTypeNode[] + ): cs.TypeNode | null { + let csType: cs.TypeNode | null = this.resolveKnownTypeSymbol(node, tsType, typeArguments); + if (csType) { + return csType; + } + + csType = this.resolvePrimitiveType(node, tsType); + if (csType) { + return csType; + } + + csType = this.resolveUnionType(node, tsType); + if (csType) { + return csType; + } + + csType = this.resolveUnknownTypeSymbol(node, tsType, tsSymbol, typeArguments); + + return csType; + } + + private resolveUnknownTypeSymbol( + node: cs.Node, + tsType: ts.Type, + tsSymbol?: ts.Symbol, + typeArguments?: cs.UnresolvedTypeNode[] + ): cs.TypeNode | null { + if (!tsSymbol) { + tsSymbol = tsType.symbol; + } + + if (!tsSymbol) { + return null; + } + + // some built in type handling + switch (tsSymbol.name) { + case 'Promise': + const promiseType = tsType as ts.TypeReference; + if (typeArguments) { + return typeArguments[0]; + } else if (promiseType.typeArguments) { + return this.getTypeFromTsType(node, promiseType.typeArguments[0]); + } + return null; + case 'Map': + const mapType = tsType as ts.TypeReference; + let mapValueType: cs.TypeNode | null = null; + if (typeArguments) { + mapValueType = this.resolveType(typeArguments[1]); + } else if (mapType.typeArguments) { + mapValueType = this.getTypeFromTsType(node, mapType.typeArguments[1]); + } + + let isValueType = false; + if (mapValueType) { + switch (mapValueType.nodeType) { + case cs.SyntaxKind.PrimitiveTypeNode: + switch ((mapValueType as cs.PrimitiveTypeNode).type) { + case cs.PrimitiveType.Bool: + case cs.PrimitiveType.Int: + case cs.PrimitiveType.Double: + isValueType = true; + break; + } + break; + case cs.SyntaxKind.TypeReference: + const ref = (mapValueType as cs.TypeReference).reference; + if (typeof ref !== 'string') { + switch (ref.nodeType) { + case cs.SyntaxKind.EnumDeclaration: + isValueType = true; + break; + } + } + break; + } + } + + return { + nodeType: cs.SyntaxKind.TypeReference, + parent: node.parent, + tsNode: node.tsNode, + reference: this.buildCoreNamespace(tsSymbol) + (isValueType ? 'ValueTypeMap' : 'Map'), + typeArguments: typeArguments + } as cs.TypeReference; + case 'Array': + const arrayType = tsType as ts.TypeReference; + let arrayElementType: cs.TypeNode | null = null; + if (typeArguments) { + arrayElementType = this.resolveType(typeArguments[0]); + } else if (arrayType.typeArguments) { + arrayElementType = this.getTypeFromTsType(node, arrayType.typeArguments[0]); + } + + if (!arrayElementType) { + arrayElementType = { + nodeType: cs.SyntaxKind.PrimitiveTypeNode, + parent: node.parent, + tsNode: node.tsNode, + type: cs.PrimitiveType.Object + } as cs.PrimitiveTypeNode; + } + + return { + nodeType: cs.SyntaxKind.ArrayTypeNode, + parent: node.parent, + tsNode: node.tsNode, + elementType: arrayElementType + } as cs.ArrayTypeNode; + + case ts.InternalSymbolName.Type: + let csType: cs.TypeNode | null = null; + + csType = this.resolveFunctionTypeFromTsType(node, tsType); + + return csType; + + default: + return { + nodeType: cs.SyntaxKind.TypeReference, + parent: node.parent, + tsNode: node.tsNode, + reference: this.buildCoreNamespace(tsSymbol) + tsSymbol.name, + typeArguments: typeArguments + } as cs.TypeReference; + } + } + + private resolveFunctionTypeFromTsType(node: cs.Node, tsType: ts.Type): cs.TypeNode | null { + // typescript compiler API somehow does not provide proper type symbols + // for function types, we need to attempt resolving the types via the function type declaration + + let functionTypeNode: ts.FunctionTypeNode | null = null; + for (const declaration of tsType.symbol.declarations) { + if (ts.isFunctionTypeNode(declaration)) { + functionTypeNode = declaration; + break; + } + } + + if (!functionTypeNode) { + return null; + } + + const typeNodeToCsType = (typeNode: ts.TypeNode) => { + const nodeTsType = this.typeChecker.getTypeAtLocation(typeNode); + if (!nodeTsType) { + return null; + } + + const nodeType = this.getTypeFromTsType(node, nodeTsType); + if (!nodeType) { + return null; + } + + return nodeType; + }; + + const returnType = typeNodeToCsType(functionTypeNode.type); + if (!returnType) { + this.addTsNodeDiagnostics( + functionTypeNode.type, + 'Could not resolve return type', + ts.DiagnosticCategory.Error + ); + return null; + } + + const parameterTypes: cs.TypeNode[] = []; + for (const p of functionTypeNode.parameters) { + const symbol = this.typeChecker.getSymbolAtLocation(p.name); + if (!symbol) { + this.addTsNodeDiagnostics(p, 'Could not resolve symbol for parameter', ts.DiagnosticCategory.Error); + return null; + } + + const pTsType = this.typeChecker.getTypeOfSymbolAtLocation(symbol, p); + if (!pTsType) { + this.addTsNodeDiagnostics(p, 'Could not resolve type for parameter', ts.DiagnosticCategory.Error); + return null; + } + + const pType = this.getTypeFromTsType(node, pTsType); + if (!pType) { + this.addTsNodeDiagnostics(p, 'Could not map type for parameter', ts.DiagnosticCategory.Error); + return null; + } + + parameterTypes.push(pType); + } + + let typeParameters: cs.TypeNode[] | undefined = undefined; + if (functionTypeNode.typeParameters) { + typeParameters = []; + for (const tp of functionTypeNode.typeParameters) { + const tpTsType = this.typeChecker.getTypeAtLocation(tp); + if (!tpTsType) { + this.addTsNodeDiagnostics(tp, 'Could not resolve type parameter', ts.DiagnosticCategory.Error); + return null; + } + + const tpType = this.getTypeFromTsType(node, tpTsType); + if (!tpType) { + this.addTsNodeDiagnostics(tp, 'Could not map type parameter', ts.DiagnosticCategory.Error); + return null; + } + + typeParameters.push(tpType); + } + } + + if ( + returnType.nodeType === cs.SyntaxKind.PrimitiveTypeNode && + (returnType as cs.PrimitiveTypeNode).type === cs.PrimitiveType.Void + ) { + return { + nodeType: cs.SyntaxKind.TypeReference, + parent: node.parent, + tsNode: node.tsNode, + reference: 'System.Action', + typeArguments: parameterTypes + } as cs.TypeReference; + } else { + parameterTypes.push(returnType); + return { + nodeType: cs.SyntaxKind.TypeReference, + parent: node.parent, + tsNode: node.tsNode, + reference: 'System.Func', + typeArguments: parameterTypes + } as cs.TypeReference; + } + } + + private resolveUnionType(parent: cs.Node, tsType: ts.Type): cs.TypeNode | null { + if (!tsType.isUnion()) { + return null; + } + + // external union type alias, refer by name + if (!tsType.symbol && tsType.aliasSymbol) { + let isNullable = false; + let isOptional = false; + for (let t of tsType.types) { + if ((t.flags & ts.TypeFlags.Null) !== 0) { + isNullable = true; + } else if ((t.flags & ts.TypeFlags.Undefined) !== 0) { + isOptional = true; + } + } + + return { + nodeType: cs.SyntaxKind.TypeReference, + parent: parent, + reference: this.buildCoreNamespace(tsType.aliasSymbol) + tsType.aliasSymbol.name, + isNullable: isNullable, + isOptional: isOptional + } as cs.TypeReference; + } + + let isNullable = false; + let isOptional = false; + let actualType: ts.Type | null = null; + let fallbackToObject = false; + for (let t of tsType.types) { + if (t.isLiteral()) { + t = this.typeChecker.getBaseTypeOfLiteralType(t); + } + + if ((t.flags & ts.TypeFlags.Null) !== 0) { + isNullable = true; + } else if ((t.flags & ts.TypeFlags.Undefined) !== 0) { + isOptional = true; + } else if (actualType == null) { + actualType = t; + } else if (actualType != null && actualType.flags !== t.flags) { + this.addCsNodeDiagnostics( + parent, + 'Union type covering multiple types detected, fallback to dynamic', + ts.DiagnosticCategory.Warning + ); + fallbackToObject = true; + } else { + actualType = t; + } + } + + if (fallbackToObject) { + return { + nodeType: cs.SyntaxKind.PrimitiveTypeNode, + parent: parent, + type: cs.PrimitiveType.Dynamic, + isNullable: isNullable, + isOptional: isOptional + } as cs.PrimitiveTypeNode; + } + + if (!actualType) { + return null; + } + const type = this.getTypeFromTsType(parent, actualType); + return { + nodeType: cs.SyntaxKind.TypeReference, + parent: parent, + reference: type, + isNullable: isNullable, + isOptional: isOptional + } as cs.TypeReference; + } + + public isDefaultValueNull(tsType: ts.Type): boolean { + tsType = this.typeChecker.getNonNullableType(tsType); + if ((tsType.flags & ts.TypeFlags.Number) !== 0 || (tsType.flags & ts.TypeFlags.NumberLiteral) !== 0) { + return false; + } + if ((tsType.flags & ts.TypeFlags.Boolean) !== 0 || (tsType.flags & ts.TypeFlags.BooleanLiteral) !== 0) { + return false; + } + return true; + } + + private resolvePrimitiveType(parent: cs.Node, tsType: ts.Type): cs.TypeNode | null { + const handleNullablePrimitive = (type: cs.PrimitiveType) => { + let isNullable = false; + let isOptional = false; + if (tsType.isUnion()) { + for (const t of tsType.types) { + if ((t.flags & ts.TypeFlags.Null) !== 0) { + isNullable = true; + } else if ((t.flags & ts.TypeFlags.Undefined) !== 0) { + isOptional = true; + } + } + } + + return { + nodeType: cs.SyntaxKind.PrimitiveTypeNode, + type: type, + isNullable: isNullable, + isOptional: isOptional + } as cs.PrimitiveTypeNode; + }; + + // raw object without symbol -> dynamic + if ((tsType.flags & ts.TypeFlags.Object) !== 0 && !tsType.symbol) { + return handleNullablePrimitive(cs.PrimitiveType.Dynamic); + } + + // any -> dynamic + if ((tsType.flags & ts.TypeFlags.Any) !== 0) { + return handleNullablePrimitive(cs.PrimitiveType.Dynamic); + } + + // unknown -> object + if ((tsType.flags & ts.TypeFlags.Unknown) !== 0) { + const unknown = handleNullablePrimitive(cs.PrimitiveType.Object); + unknown.isNullable = true; + return unknown; + } + + // number or number literal -> double + if ((tsType.flags & ts.TypeFlags.Number) !== 0 || (tsType.flags & ts.TypeFlags.NumberLiteral) !== 0) { + return handleNullablePrimitive(cs.PrimitiveType.Double); + } + + // string or string literal -> string + if ((tsType.flags & ts.TypeFlags.String) !== 0 || (tsType.flags & ts.TypeFlags.StringLiteral) !== 0) { + return handleNullablePrimitive(cs.PrimitiveType.String); + } + + // boolean or boolean literal -> bool + if ((tsType.flags & ts.TypeFlags.Boolean) !== 0 || (tsType.flags & ts.TypeFlags.BooleanLiteral) !== 0) { + return handleNullablePrimitive(cs.PrimitiveType.Bool); + } + + // void -> void + if ((tsType.flags & ts.TypeFlags.Void) !== 0) { + return { + nodeType: cs.SyntaxKind.PrimitiveTypeNode, + type: cs.PrimitiveType.Void + } as cs.PrimitiveTypeNode; + } + + // never -> dynamic + // (actually it would be void for usages on return values, but we rather have it as array element type on empty arrays) + if ((tsType.flags & ts.TypeFlags.Never) !== 0) { + return { + nodeType: cs.SyntaxKind.PrimitiveTypeNode, + type: cs.PrimitiveType.Dynamic + } as cs.PrimitiveTypeNode; + } + + return null; + } + + private resolveKnownTypeSymbol( + node: cs.Node, + tsType: ts.Type, + typeArguments?: cs.UnresolvedTypeNode[] + ): cs.TypeNode | null { + const symbolKey = this.getSymbolKey(tsType.symbol); + if (tsType.symbol && this._symbolLookup.has(symbolKey)) { + const declaration = this._symbolLookup.get(symbolKey)!; + const reference = { + nodeType: cs.SyntaxKind.TypeReference, + parent: node.parent, + tsNode: node.tsNode, + reference: declaration + } as cs.TypeReference; + + if (typeArguments) { + reference.typeArguments = []; + typeArguments.forEach(a => { + const parameterType = this.resolveType(a); + if (!parameterType) { + this.addTsNodeDiagnostics( + node.tsNode!, + 'Could not resolve type parameter', + ts.DiagnosticCategory.Error + ); + } else { + reference.typeArguments!.push(parameterType); + } + }); + } else { + const tsTypeArguments = (tsType as ts.TypeReference).typeArguments; + if (tsTypeArguments) { + reference.typeArguments = []; + tsTypeArguments.forEach(a => { + const parameterType = this.getTypeFromTsType(node, a); + if (!parameterType) { + this.addTsNodeDiagnostics( + node.tsNode!, + 'Could not resolve type parameter', + ts.DiagnosticCategory.Error + ); + } else { + reference.typeArguments!.push(parameterType); + } + }); + } + } + + return reference; + } else if (tsType.isTypeParameter()) { + return { + nodeType: cs.SyntaxKind.TypeReference, + parent: node.parent, + tsNode: node.tsNode, + reference: tsType.symbol.name + } as cs.TypeReference; + } + + return null; + } + + private buildCoreNamespace(aliasSymbol: ts.Symbol) { + let suffix = ''; + for (const decl of aliasSymbol.declarations) { + let fileName = path.basename(decl.getSourceFile().fileName).toLowerCase(); + if (fileName.startsWith('lib.') && fileName.endsWith('.d.ts')) { + fileName = fileName.substring(4, fileName.length - 5); + if (fileName.length) { + suffix = fileName.split('.').map(s => { + if (s.match(/es[0-9]{4}/)) { + return '.EcmaScript'; + } + if (s.match(/es[0-9]{1}/)) { + return '.EcmaScript'; + } + return '.' + this.toPascalCase(s); + })[0]; + } + } + } + return `AlphaTab.Core${suffix}.`; + } + + public toPascalCase(text: string): string { + if (text.indexOf('-') >= 0) { + return this.kebabCaseToPascalCase(text); + } + + return text ? text.substr(0, 1).toUpperCase() + text.substr(1) : ''; + } + + private kebabCaseToPascalCase(text: string): string { + return text + .split('-') + .map(w => this.toPascalCase(w)) + .join(''); + } + + public registerSymbolAsExported(symbol: ts.Symbol) { + const symbolKey = this.getSymbolKey(symbol); + this._exportedSymbols.set(symbolKey, true); + } + + public registerSymbol(node: cs.NamedElement & cs.Node) { + const symbol = this.getSymbolForDeclaration(node.tsNode!); + if (symbol) { + const symbolKey = this.getSymbolKey(symbol); + this._symbolLookup.set(symbolKey, node); + } else { + this.addCsNodeDiagnostics(node, 'Could not register symbol', ts.DiagnosticCategory.Error); + } + } + + public isConst(declaration: cs.FieldDeclaration) { + const symbolKey = this.getSymbolKey(declaration.tsSymbol!); + return this._symbolConst.has(symbolKey); + } + + public registerSymbolAsConst(symbol: ts.Symbol) { + const symbolKey = this.getSymbolKey(symbol); + this._symbolConst.set(symbolKey, true); + } + + private getSymbolKey(symbol: ts.Symbol | undefined): SymbolKey { + if (!symbol) { + return ''; + } + + if (symbol.flags & ts.SymbolFlags.Alias) { + symbol = this.typeChecker.getAliasedSymbol(symbol); + } + + const declaration = symbol.valueDeclaration + ? symbol.valueDeclaration + : symbol.declarations && symbol.declarations.length > 0 + ? symbol.declarations[0] + : undefined; + + if (declaration) { + return symbol.name + '_' + declaration.getSourceFile().fileName + '_' + declaration.pos; + } else { + return symbol.name; + } + } + + public getSymbolForDeclaration(node: ts.Node): ts.Symbol | undefined { + let symbol = this.typeChecker.getSymbolAtLocation(node); + if (!symbol) { + const name = (node as any).name; + if (name) { + symbol = this.typeChecker.getSymbolAtLocation(name); + } + } + return symbol; + } + + public isBooleanSmartCast(tsNode: ts.Node) { + let tsParent = tsNode.parent!; + while (tsParent.kind === ts.SyntaxKind.ParenthesizedExpression) { + tsNode = tsParent; + tsParent = tsParent.parent!; + } + switch (tsParent.kind) { + case ts.SyntaxKind.ConditionalExpression: + if ((tsParent as ts.ConditionalExpression).condition !== tsNode) { + return false; + } + break; + case ts.SyntaxKind.PrefixUnaryExpression: + if ((tsParent as ts.PrefixUnaryExpression).operator !== ts.SyntaxKind.ExclamationToken) { + return false; + } + break; + case ts.SyntaxKind.IfStatement: + if ((tsParent as ts.IfStatement).expression !== tsNode) { + return false; + } + break; + case ts.SyntaxKind.WhileStatement: + if ((tsParent as ts.WhileStatement).expression !== tsNode) { + return false; + } + break; + case ts.SyntaxKind.ForStatement: + if ((tsParent as ts.ForStatement).condition !== tsNode) { + return false; + } + break; + case ts.SyntaxKind.BinaryExpression: + switch ((tsParent as ts.BinaryExpression).operatorToken.kind) { + case ts.SyntaxKind.AmpersandAmpersandToken: + case ts.SyntaxKind.BarBarToken: + break; + default: + return false; + } + break; + default: + return false; + } + + // check if expression results in bool + const type = this.typeChecker.getTypeAtLocation(tsNode); + if (!type || this.isBooleanType(type)) { + return true; + } + + return true; + } + + public isValueTypeNotNullSmartCast(expression: ts.Expression): boolean | undefined { + if ( + expression.parent.kind === ts.SyntaxKind.AsExpression || // already a cast + expression.parent.kind === ts.SyntaxKind.NonNullExpression || // explicit non null expression + this.isBooleanSmartCast(expression) || + (ts.isBinaryExpression(expression.parent) && + expression.parent.operatorToken.kind === ts.SyntaxKind.EqualsToken && + expression.parent.left === expression) // left hand side assignment + ) { + return undefined; + } + + // unwrap symbol of expression to get declared type + let symbol = this.typeChecker.getSymbolAtLocation(expression); + if (!symbol || !symbol.declarations || symbol.declarations.length === 0) { + return undefined; + } + if (symbol.flags & ts.SymbolFlags.Alias) { + symbol = this.typeChecker.getAliasedSymbol(symbol); + } + if (symbol.flags & ts.SymbolFlags.Interface || symbol.flags & ts.SymbolFlags.Class) { + return undefined; + } + + // declared type must be nullable + let declaredType = this.typeChecker.getTypeAtLocation(symbol.declarations[0]); + if (!this.isNullableType(declaredType)) { + return undefined; + } + + // actual type at location must be non nullable + let declaredTypeNonNull = this.typeChecker.getNonNullableType(declaredType); + let contextualType = this.typeChecker.getTypeOfSymbolAtLocation(symbol, expression); + if (!contextualType || this.isNullableType(contextualType)) { + return undefined; + } + + // actual type must match non nullable declaration + if (declaredTypeNonNull === contextualType) { + return this.isValueType(declaredTypeNonNull); + } + + return undefined; + } + + public isNullableType(declaredType: ts.Type): boolean { + return ( + declaredType.isUnion() && + !!declaredType.types.find(t => t.flags & ts.TypeFlags.Null || t.flags & ts.TypeFlags.Undefined) + ); + } + + public getSmartCastType(expression: ts.Expression): ts.Type | null { + // if the parent is already casting, we have no "smart" cast. + if (expression.parent.kind === ts.SyntaxKind.AsExpression) { + return null; + } + + // we consider the expression as smart casted if the declared symbol has a different + // contextual type than the declared type. + let symbol = this.typeChecker.getSymbolAtLocation(expression); + if (!symbol || !symbol.declarations || symbol.declarations.length === 0) { + return null; + } + + if (symbol.flags & ts.SymbolFlags.Alias) { + symbol = this.typeChecker.getAliasedSymbol(symbol); + } + + if (symbol.flags & ts.SymbolFlags.Interface || symbol.flags & ts.SymbolFlags.Class) { + return null; + } + + let contextualType = this.typeChecker.getContextualType(expression); + if (!contextualType) { + contextualType = this.typeChecker.getTypeOfSymbolAtLocation(symbol, expression); + if (!contextualType) { + return null; + } + } + + let declaredType = this.typeChecker.getTypeAtLocation(symbol.declarations[0]); + + contextualType = this.typeChecker.getNonNullableType(contextualType); + declaredType = this.typeChecker.getNonNullableType(declaredType); + + if (this.shouldSkipSmartCast(contextualType)) { + return null; + } + + // cast enums to numbers in arithmetic operations + if ( + expression.parent && + (contextualType.flags & ts.TypeFlags.Enum || contextualType.flags & ts.TypeFlags.EnumLiteral) + ) { + if (ts.isBinaryExpression(expression.parent)) { + switch (expression.parent.operatorToken.kind) { + case ts.SyntaxKind.AsteriskToken: + case ts.SyntaxKind.PlusToken: + case ts.SyntaxKind.MinusEqualsToken: + case ts.SyntaxKind.SlashToken: + return (this.typeChecker as any).getNumberType(); + case ts.SyntaxKind.EqualsEqualsToken: + case ts.SyntaxKind.EqualsEqualsEqualsToken: + case ts.SyntaxKind.ExclamationEqualsToken: + case ts.SyntaxKind.ExclamationEqualsEqualsToken: + case ts.SyntaxKind.GreaterThanEqualsToken: + case ts.SyntaxKind.GreaterThanToken: + case ts.SyntaxKind.LessThanEqualsToken: + case ts.SyntaxKind.LessThanToken: + const otherExpr = + expression.parent.left === expression ? expression.parent.right : expression.parent.left; + const otherExprType = this.typeChecker.getTypeAtLocation(otherExpr); + if (otherExprType && otherExprType.flags & ts.TypeFlags.Number) { + return (this.typeChecker as any).getNumberType(); + } + break; + } + } else if ( + ts.isElementAccessExpression(expression.parent) && + expression.parent.argumentExpression === expression + ) { + return (this.typeChecker as any).getNumberType(); + } + } + + return contextualType !== declaredType && !this.isTypeAssignable(contextualType, declaredType) + ? contextualType + : null; + } + shouldSkipSmartCast(contextualType: ts.Type) { + // unions that are no enums + if (contextualType.isUnion() && (contextualType.flags & (ts.TypeFlags.Enum | ts.TypeFlags.EnumLiteral)) === 0) { + return true; + } + + // no function types + if (this.isFunctionType(contextualType)) { + return true; + } + + // some core types + if (contextualType.symbol) { + switch (contextualType.symbol.name) { + case 'ArrayLike': + return true; + } + } + + return false; + } + + public isFunctionType(contextualType: ts.Type): boolean { + if (!contextualType.symbol || !contextualType.symbol.declarations) { + return false; + } + for (const declaration of contextualType.symbol.declarations) { + if (ts.isFunctionTypeNode(declaration)) { + return true; + } + } + + return false; + } + + public isOverride(classElement: ts.ClassElement): boolean { + let parent: ts.Node = classElement; + while (parent.kind !== ts.SyntaxKind.ClassDeclaration) { + if (parent.parent) { + parent = parent.parent; + } else { + return false; + } + } + + let classDecl = parent as ts.ClassDeclaration; + let classSymbol = this.typeChecker.getSymbolAtLocation(classDecl.name!); + if (!classSymbol) { + return false; + } + + let classType = this.typeChecker.getDeclaredTypeOfSymbol(classSymbol); + if (!classType || !classType.isClass()) { + return false; + } + + if (this.hasAnyBaseTypeClassMember(classType, classElement.name!.getText())) { + return true; + } + + return false; + } + private hasAnyBaseTypeClassMember(classType: ts.InterfaceType, memberName: string) { + const baseTypes = classType.getBaseTypes(); + if (!baseTypes) { + return false; + } + + for (const baseType of baseTypes) { + if (baseType.isClass() && this.hasClassMember(baseType, memberName)) { + return true; + } + } + + return false; + } + + private hasClassMember(baseType: ts.InterfaceType, name: string): boolean { + if ( + baseType.symbol && + baseType.symbol.members && + baseType.symbol.members.has(ts.escapeLeadingUnderscores(name)) + ) { + return true; + } + + return this.hasAnyBaseTypeClassMember(baseType, name); + } + + public isValueTypeExpression(expression: ts.NonNullExpression) { + const tsType = this.typeChecker.getTypeAtLocation(expression); + return this.isValueType(tsType); + } + + public isValueType(tsType: ts.Type) { + // primitives + if ((tsType.flags & ts.TypeFlags.Number) !== 0 || (tsType.flags & ts.TypeFlags.NumberLiteral) !== 0) { + return true; + } + if ((tsType.flags & ts.TypeFlags.Boolean) !== 0 || (tsType.flags & ts.TypeFlags.BooleanLiteral) !== 0) { + return true; + } + + // enums + if (tsType.symbol && tsType.symbol.flags & ts.SymbolFlags.Enum) { + return true; + } + + return false; + } + + public rewriteVisibilities() { + const visited: Set = new Set(); + for (const kvp of this._symbolLookup) { + const symbolKey = this.getSymbolKey(kvp[1].tsSymbol!); + switch (kvp[1].nodeType) { + case cs.SyntaxKind.ClassDeclaration: + case cs.SyntaxKind.DelegateDeclaration: + case cs.SyntaxKind.EnumDeclaration: + case cs.SyntaxKind.InterfaceDeclaration: + if (!visited.has(symbolKey)) { + const csType = kvp[1] as cs.NamedTypeDeclaration; + const shouldBePublic = !!ts.getJSDocTags(csType.tsNode!).find(t => t.tagName.text === 'csharp_public'); + if (csType.visibility === cs.Visibility.Public || shouldBePublic) { + if (this._exportedSymbols.has(symbolKey) || shouldBePublic) { + this.makePublic(csType, visited); + } else { + csType.visibility = cs.Visibility.Internal; + } + } + } + break; + } + } + } + + private makePublic(node: cs.Node, visited: Set) { + if (node.tsSymbol) { + const x = this.getSymbolKey(node.tsSymbol); + if (visited.has(x)) { + return; + } + visited.add(x); + } + + switch (node.nodeType) { + case cs.SyntaxKind.ClassDeclaration: + const csClass = node as cs.ClassDeclaration; + csClass.visibility = cs.Visibility.Public; + + if (csClass.baseClass) { + this.makePublic(csClass.baseClass, visited); + } + + if (csClass.interfaces) { + csClass.interfaces.forEach(i => this.makePublic(i, visited)); + } + + csClass.members.forEach(m => { + if (m.visibility == cs.Visibility.Public || m.visibility == cs.Visibility.Protected) { + this.makePublic(m, visited) + } + }); + break; + case cs.SyntaxKind.EnumDeclaration: + const csEnum = node as cs.EnumDeclaration; + csEnum.visibility = cs.Visibility.Public; + break; + case cs.SyntaxKind.InterfaceDeclaration: + const csInterface = node as cs.InterfaceDeclaration; + csInterface.visibility = cs.Visibility.Public; + + if (csInterface.interfaces) { + csInterface.interfaces.forEach(i => this.makePublic(i, visited)); + } + + csInterface.members.forEach(m => { + this.makePublic(m, visited) + }); + + break; + + case cs.SyntaxKind.ConstructorDeclaration: + const csConstructor = node as cs.ConstructorDeclaration; + csConstructor.parameters.forEach(p => this.makePublic(p, visited)); + break; + case cs.SyntaxKind.MethodDeclaration: + const csMethod = node as cs.MethodDeclaration; + csMethod.parameters.forEach(p => this.makePublic(p, visited)); + this.makePublic(csMethod.returnType, visited); + break; + case cs.SyntaxKind.PropertyDeclaration: + const csProperty = node as cs.PropertyDeclaration; + this.makePublic(csProperty.type, visited); + break; + case cs.SyntaxKind.ParameterDeclaration: + const csParameter = node as cs.ParameterDeclaration; + if (csParameter.type) { + this.makePublic(csParameter.type, visited); + } + break; + case cs.SyntaxKind.TypeReference: + const csTypeRef = node as cs.TypeReference; + if (csTypeRef.typeArguments) { + csTypeRef.typeArguments.forEach(r => this.makePublic(r, visited)); + } + if (typeof csTypeRef.reference !== 'string') { + this.makePublic(csTypeRef.reference, visited); + } + + break; + case cs.SyntaxKind.ArrayTypeNode: + const csArrayType = node as cs.ArrayTypeNode; + if (csArrayType.elementType) { + this.makePublic(csArrayType.elementType, visited); + } + break; + } + } +} diff --git a/src.compiler/csharp/CSharpTranspiler.ts b/src.compiler/csharp/CSharpTranspiler.ts new file mode 100644 index 000000000..4a7f53c11 --- /dev/null +++ b/src.compiler/csharp/CSharpTranspiler.ts @@ -0,0 +1,87 @@ +import * as ts from 'typescript'; +import emit from './CSharpEmitter'; + +function createDiagnosticReporter(pretty?: boolean): ts.DiagnosticReporter { + const host: ts.FormatDiagnosticsHost = { + getCurrentDirectory: () => ts.sys.getCurrentDirectory(), + getNewLine: () => ts.sys.newLine, + getCanonicalFileName: ts.sys.useCaseSensitiveFileNames + ? x => x + : x => x.toLowerCase(), + }; + + if (!pretty) { + return diagnostic => ts.sys.write(ts.formatDiagnostic(diagnostic, host)); + } + + return diagnostic => { + ts.sys.write(ts.formatDiagnosticsWithColorAndContext([diagnostic], host) + host.getNewLine()); + }; +} + +const commandLine = ts.parseCommandLine(ts.sys.args); +if (!ts.sys.fileExists(commandLine.options.project!)) { + ts.sys.exit(ts.ExitStatus.InvalidProject_OutputsSkipped); +} + +let reportDiagnostic = createDiagnosticReporter(); + +const parseConfigFileHost: ts.ParseConfigFileHost = ts.sys; +parseConfigFileHost.onUnRecoverableConfigFileDiagnostic = diagnostic => { + reportDiagnostic(diagnostic); + ts.sys.exit(ts.ExitStatus.InvalidProject_OutputsSkipped); +}; + +const parsedCommandLine = ts.getParsedCommandLineOfConfigFile(commandLine.options.project!, commandLine.options, parseConfigFileHost, /*extendedConfigCache*/ undefined, commandLine.watchOptions)!; +const pretty = !!ts.sys.writeOutputIsTTY && ts.sys.writeOutputIsTTY(); +if (pretty) { + reportDiagnostic = createDiagnosticReporter(true); +} + +const program = ts.createProgram({ + rootNames: parsedCommandLine.fileNames, + options: parsedCommandLine.options, + projectReferences: parsedCommandLine.projectReferences, + host: ts.createCompilerHost(parsedCommandLine.options), +}); + +const allDiagnostics = program.getConfigFileParsingDiagnostics().slice(); +const configFileParsingDiagnosticsLength = allDiagnostics.length; +allDiagnostics.push(...program.getSyntacticDiagnostics()); + +if (allDiagnostics.length === configFileParsingDiagnosticsLength) { + allDiagnostics.push(...program.getOptionsDiagnostics()); + allDiagnostics.push(...program.getGlobalDiagnostics()); + allDiagnostics.push(...program.getSemanticDiagnostics()); +} + +const emitDiagnostics = emit(program); +allDiagnostics.push(...emitDiagnostics); + +let diagnostics = ts.sortAndDeduplicateDiagnostics(allDiagnostics); +let errorCount = 0; +let warningCount = 0; +diagnostics.forEach(d => { + switch (d.category) { + case ts.DiagnosticCategory.Error: errorCount++; break; + case ts.DiagnosticCategory.Warning: warningCount++; break; + } + reportDiagnostic(d); +}); + +if (pretty) { + reportDiagnostic({ + file: undefined, + start: undefined, + length: undefined, + code: 6194, + messageText:`Compilation completed with ${errorCount} errors and ${warningCount} warnings${ts.sys.newLine}`, + category: errorCount > 0 ? ts.DiagnosticCategory.Error : warningCount > 0 ? ts.DiagnosticCategory.Warning : ts.DiagnosticCategory.Message, + }); +} + +if (errorCount > 0) { + ts.sys.exit(ts.ExitStatus.DiagnosticsPresent_OutputsGenerated); +} else { + ts.sys.exit(ts.ExitStatus.Success); +} \ No newline at end of file diff --git a/src.csharp/AlphaTab.Test/AlphaTab.Test.csproj b/src.csharp/AlphaTab.Test/AlphaTab.Test.csproj new file mode 100644 index 000000000..4d0daefe9 --- /dev/null +++ b/src.csharp/AlphaTab.Test/AlphaTab.Test.csproj @@ -0,0 +1,34 @@ + + + + AlphaTab + netcoreapp3.1 + false + enable + 8 + true + + + + + + + + + + + + Generated\%(RecursiveDir)\%(Filename)%(Extension) + + + test-data\%(RecursiveDir)\%(Filename)%(Extension) + PreserveNewest + + + + + + + + + diff --git a/src.csharp/AlphaTab.Test/Test/Globals.cs b/src.csharp/AlphaTab.Test/Test/Globals.cs new file mode 100644 index 000000000..80d918836 --- /dev/null +++ b/src.csharp/AlphaTab.Test/Test/Globals.cs @@ -0,0 +1,75 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace AlphaTab.Test +{ + public class Globals + { + public static Expector Expect(T actual) + { + return new Expector(actual); + } + + public static void Fail(string message) + { + Assert.Fail(message); + } + } + + public class Expector + { + private readonly T _actual; + private string _message; + + public Expector(T actual) + { + _actual = actual; + _message = string.Empty; + } + + public Expector WithContext(string message) + { + _message = message; + return this; + } + + public void ToEqual(object expected, string message = null) + { + if (expected is int i && _actual is double) + { + expected = (double)i; + } + Assert.AreEqual(expected, _actual, _message + message); + } + + public void ToBe(object expected) + { + if (expected is int i && _actual is double) + { + expected = (double)i; + } + Assert.AreEqual(expected, _actual, _message); + } + + public void ToBeTruthy() + { + Assert.AreNotEqual(default, _actual, _message); + } + + public void ToBeTrue() + { + if (_actual is bool b) + { + Assert.IsTrue(b, _message); + } + else + { + Assert.Fail("ToBeTrue can only be used on bools:" + _message); + } + } + + public void ToBeFalsy() + { + Assert.AreEqual(default, _actual, _message); + } + } +} diff --git a/src.csharp/AlphaTab.Test/TestPlatform.cs b/src.csharp/AlphaTab.Test/TestPlatform.cs new file mode 100644 index 000000000..21ba9308a --- /dev/null +++ b/src.csharp/AlphaTab.Test/TestPlatform.cs @@ -0,0 +1,17 @@ +using System.IO; +using System.Threading.Tasks; +using AlphaTab.Core.EcmaScript; + +namespace AlphaTab +{ + static partial class TestPlatform + { + public static async Task LoadFile(string path) + { + await using var fs = new FileStream(path, FileMode.Open); + await using var ms = new MemoryStream(); + await fs.CopyToAsync(ms); + return new Uint8Array(ms.ToArray()); + } + } +} diff --git a/src.csharp/AlphaTab.Test/VisualTests/PixelMatch.cs b/src.csharp/AlphaTab.Test/VisualTests/PixelMatch.cs new file mode 100644 index 000000000..0e0e78174 --- /dev/null +++ b/src.csharp/AlphaTab.Test/VisualTests/PixelMatch.cs @@ -0,0 +1,326 @@ +using System; +using System.Data; +using SkiaSharp; + +namespace AlphaTab.VisualTests +{ + // Based on MapBox PixelMatch: https://github.com/mapbox/pixelmatch + + // ISC License + + // Copyright(c) 2018, Mapbox + + // Permission to use, copy, modify, and/or distribute this software for any purpose + // with or without fee is hereby granted, provided that the above copyright notice + // and this permission notice appear in all copies. + + + // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + // FITNESS.IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + // OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + // TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + // THIS SOFTWARE. + + internal class PixelMatch + { + public static unsafe PixelMatchResult Run(SKBitmap img1, SKBitmap img2, + PixelMatchOptions options) + { + var result = new PixelMatchResult(); + + if (img1.Width == img2.Width && img1.Height == img2.Height) + { + result.SizesMatch = true; + } + else + { + result.SizesMatch = false; + return result; + } + + var threshold = options.Threshold; + // maximum acceptable square distance between two colors; + // 35215 is the maximum possible value for the YIQ difference metric + var maxDelta = 35215 * threshold * threshold; + var diff = 0; + + var width = img1.Width; + var height = img1.Height; + + var img1Raw = (byte*) img1.GetPixels().ToPointer(); + var img2Raw = (byte*) img2.GetPixels().ToPointer(); + byte* outputRaw = null; + + // compare each pixel of one image against the other one + if (options.CreateOutputImage) + { + result.Output = + new SKBitmap(new SKImageInfo(width, height, SKImageInfo.PlatformColorType, + SKAlphaType.Premul)); + outputRaw = (byte*) result.Output.GetPixels().ToPointer(); + } + + var totalPixels = 0; + + for (var y = 0; y < height; y++) + { + for (var x = 0; x < width; x++) + { + var pos = (y * width + x) * 4; + // squared YUV distance between colors at this pixel position + var delta = ColorDelta(img1Raw, img2Raw, pos, pos, false, + out var wasTransparent); + + if (wasTransparent && options.IgnoreTransparent) + { + continue; + } + + totalPixels++; + + // the color difference is above the threshold + if (delta > maxDelta) + { + // check it's a real rendering difference or just anti-aliasing + if (!options.IncludeAntiAlias && + (AntiAliased(img1Raw, x, y, width, height, img2Raw) || + AntiAliased(img2Raw, x, y, width, height, img1Raw))) + { + // one of the pixels is anti-aliasing; draw as yellow and do not count as difference + if (options.CreateOutputImage) + { + DrawPixel(outputRaw, pos, 255, 255, 0); + } + } + else + { + // found substantial difference not caused by anti-aliasing; draw it as red + if (options.CreateOutputImage) + { + DrawPixel(outputRaw, pos, 255, 0, 0); + } + + diff++; + } + } + else if (options.CreateOutputImage) + { + // pixels are similar; draw background as grayscale image blended with white + var val = (byte) Blend(GrayPixel(img1Raw, pos), 0.1); + DrawPixel(outputRaw, pos, val, val, val); + } + } + } + + result.DifferentPixels = diff; + result.TotalPixels = totalPixels; + return result; + } + + private static unsafe bool AntiAliased(byte* img, int x1, int y1, int width, int height, + byte* img2 = null) + { + var x0 = Math.Max(x1 - 1, 0); + var y0 = Math.Max(y1 - 1, 0); + var x2 = Math.Min(x1 + 1, width - 1); + var y2 = Math.Min(y1 + 1, height - 1); + var pos = (y1 * width + x1) * 4; + var zeroes = 0; + double min = 0; + double max = 0; + + var minX = 0; + var minY = 0; + var maxX = 0; + var maxY = 0; + + // go through 8 adjacent pixels + for (var x = x0; x <= x2; x++) + { + for (var y = y0; y <= y2; y++) + { + if (x == x1 && y == y1) + { + continue; + } + + // brightness delta between the center pixel and adjacent one + var delta = ColorDelta(img, img, pos, (y * width + x) * 4, true, + out var wasTransparent); + + // count the number of equal, darker and brighter adjacent pixels + if (delta == 0) + { + zeroes++; + // if found more than 2 equal siblings, it's definitely not anti-aliasing + if (zeroes > 2) + { + return false; + } + } + // remember darkest pixel + else if (delta < min) + { + min = delta; + minX = x; + minY = y; + } + // remember the brightest pixel + else if (delta > max) + { + max = delta; + maxX = x; + maxY = y; + } + } + } + + // if there are no both darker and brighter pixels among siblings, it's not anti-aliasing + if (min == 0 || max == 0) + { + return false; + } + + // if either the darkest or the brightest pixel has more than 2 equal siblings in both images + // (definitely not anti-aliased), this pixel is anti-aliased + return (HasManySiblings(img, minX, minY, width, height) && + HasManySiblings(img2, minX, minY, width, height)) || + (HasManySiblings(img, maxX, maxY, width, height) && + HasManySiblings(img2, maxX, maxY, width, height)); + } + + // check if a pixel has 3+ adjacent pixels of the same color. + private static unsafe bool HasManySiblings(byte* img, int x1, int y1, int width, int height) + { + var x0 = Math.Max(x1 - 1, 0); + var y0 = Math.Max(y1 - 1, 0); + var x2 = Math.Min(x1 + 1, width - 1); + var y2 = Math.Min(y1 + 1, height - 1); + var pos = (y1 * width + x1) * 4; + var zeroes = x1 == x0 || x1 == x2 || y1 == y0 || y1 == y2 ? 1 : 0; + + // go through 8 adjacent pixels + for (var x = x0; x <= x2; x++) + { + for (var y = y0; y <= y2; y++) + { + if (x == x1 && y == y1) continue; + + var pos2 = (y * width + x) * 4; + if (img[pos] == img[pos2] && + img[pos + 1] == img[pos2 + 1] && + img[pos + 2] == img[pos2 + 2] && + img[pos + 3] == + img[pos2 + 3]) zeroes++; + + if (zeroes > 2) return true; + } + } + + return false; + } + + private static unsafe byte GrayPixel(byte* img, int i) + { + var a = img[i + 3] / 255; + var r = Blend(img[i + 0], a); + var g = Blend(img[i + 1], a); + var b = Blend(img[i + 2], a); + return (byte) Rgb2Y(r, g, b); + } + + private static unsafe void DrawPixel(byte* outputRaw, int pos, byte r, byte g, byte b) + { + outputRaw[pos + 0] = r; + outputRaw[pos + 1] = g; + outputRaw[pos + 2] = b; + outputRaw[pos + 3] = 255; + } + + private static unsafe double ColorDelta(byte* img1, byte* img2, int k, int m, bool yOnly, + out bool wasTransparent) + { + double r1 = img1[k + 0]; + double g1 = img1[k + 1]; + double b1 = img1[k + 2]; + double a1 = img1[k + 3]; + + double r2 = img2[m + 0]; + double g2 = img2[m + 1]; + double b2 = img2[m + 2]; + double a2 = img2[m + 3]; + + wasTransparent = Math.Abs(a1) < 0.01 && Math.Abs(a2) < 0.01; + + if (a1 == a2 && r1 == r2 && g1 == g2 && b1 == b2) return 0; + + if (a1 < 255) { + a1 /= 255; + r1 = Blend(r1, a1); + g1 = Blend(g1, a1); + b1 = Blend(b1, a1); + } + + if (a2 < 255) { + a2 /= 255; + r2 = Blend(r2, a2); + g2 = Blend(g2, a2); + b2 = Blend(b2, a2); + } + + var y = Rgb2Y(r1, g1, b1) - Rgb2Y(r2, g2, b2); + + if (yOnly) return y; // brightness difference only + + var i = Rgb2I(r1, g1, b1) - Rgb2I(r2, g2, b2); + var q = Rgb2Q(r1, g1, b1) - Rgb2Q(r2, g2, b2); + + return 0.5053 * y * y + 0.299 * i * i + 0.1957 * q * q; + } + + private static double Rgb2Q(double r, double g, double b) + { + return r * 0.21147017 - g * 0.52261711 + b * 0.31114694; + } + + private static double Rgb2I(double r, double g, double b) + { + return r * 0.59597799 - g * 0.27417610 - b * 0.32180189; + } + + private static double Rgb2Y(double r, double g, double b) + { + return r * 0.29889531 + g * 0.58662247 + b * 0.11448223; + } + + private static double Blend(double c, double a) + { + return 255 + (c - 255) * a; + } + } + + internal class PixelMatchOptions + { + public double Threshold { get; set; } + public bool IncludeAntiAlias { get; set; } + + public bool CreateOutputImage { get; set; } + public bool IgnoreTransparent { get; set; } + + public PixelMatchOptions() + { + Threshold = 0.1; + } + } + + internal class PixelMatchResult + { + public SKBitmap Output { get; set; } + public int DifferentPixels { get; set; } + public int TotalPixels { get; set; } + public double Mismatch => DifferentPixels / (double) TotalPixels; + public bool SizesMatch { get; set; } + } +} diff --git a/src.csharp/AlphaTab.Test/VisualTests/VisualTestHelper.cs b/src.csharp/AlphaTab.Test/VisualTests/VisualTestHelper.cs new file mode 100644 index 000000000..571e2f348 --- /dev/null +++ b/src.csharp/AlphaTab.Test/VisualTests/VisualTestHelper.cs @@ -0,0 +1,224 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using AlphaTab.Core; +using AlphaTab.Core.EcmaScript; +using AlphaTab.Importer; +using AlphaTab.Io; +using AlphaTab.Model; +using AlphaTab.Rendering; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using SkiaSharp; + +namespace AlphaTab.VisualTests +{ + public class VisualTestHelper + { + public static async Task RunVisualTest(string inputFile, Settings? settings = null, + IList? tracks = null, string? message = null) + { + try + { + inputFile = $"test-data/visual-tests/{inputFile}"; + var inputFileData = + await TestPlatform.LoadFile(inputFile); + var referenceFileName = TestPlatform.ChangeExtension(inputFile, ".png"); + var score = ScoreLoader.LoadScoreFromBytes(inputFileData, settings); + + await VisualTestHelper.RunVisualTestScore(score, referenceFileName, settings, + tracks, message); + } + catch (Exception e) + { + Assert.Fail($"Failed to run visual test {e}"); + } + } + + public static async Task RunVisualTestTex(string tex, string referenceFileName, + Settings? settings = null, + IList? tracks = null, string? message = null) + { + try + { + if (settings == null) + { + settings = new Settings(); + } + + var importer = new AlphaTexImporter(); + importer.Init(ByteBuffer.FromString(tex), settings); + var score = importer.ReadScore(); + + await VisualTestHelper.RunVisualTestScore(score, referenceFileName, settings, + tracks, message); + } + catch (Exception e) + { + Assert.Fail($"Failed to run visual test {e}"); + } + } + + public static async Task RunVisualTestScore(Score score, string referenceFileName, + Settings? settings = null, + IList? tracks = null, string? message = null) + { + if (settings == null) + { + settings = new Settings(); + } + + if (tracks == null) + { + tracks = new List {0}; + } + + settings.Core.Engine = "skia"; + settings.Core.EnableLazyLoading = false; + settings.Core.UseWorkers = false; + + if(!referenceFileName.StartsWith("test-data/")) { + referenceFileName = $"test-data/visual-tests/{referenceFileName}"; + } + + var referenceFileData = + await TestPlatform.LoadFile(referenceFileName); + + var result = new List(); + var totalWidth = 0.0; + var totalHeight = 0.0; + + var task = new TaskCompletionSource(); + var renderer = new ScoreRenderer(settings); + renderer.Width = 1300; + + renderer.PartialRenderFinished.On(e => + { + if (e != null) + { + result.Add(e); + } + }); + renderer.RenderFinished.On(e => + { + totalWidth = e.TotalWidth; + totalHeight = e.TotalHeight; + result.Add(e); + task.SetResult(null); + }); + renderer.Error.On((e) => { task.SetException(e); }); + renderer.RenderScore(score, tracks); + + if (await Task.WhenAny(task.Task, Task.Delay(2000)) == task.Task) + { + await CompareVisualResult( + totalWidth, + totalHeight, + result, + referenceFileName, + referenceFileData, + message + ); + } + else + { + Assert.Fail("Rendering did not complete within timeout"); + } + } + + private static async Task CompareVisualResult(double totalWidth, double totalHeight, + List result, string referenceFileName, + Uint8Array referenceFileData, string? message) + { + // TODO: get Skia to render like Chrome + // https://github.com/mono/SkiaSharp/issues/1253 + return; + + SKBitmap finalBitmap; + + using (var finalImageSurface = SKSurface.Create(new SKImageInfo((int) totalWidth, + (int) totalHeight, + SKImageInfo.PlatformColorType, SKAlphaType.Premul))) + { + var point = new SKPoint(); + var rowHeight = 0; + foreach (var partialResult in result) + { + var partialCanvas = partialResult.RenderResult; + if (partialCanvas is SKImage img) + { + finalImageSurface.Canvas.DrawImage(img, point); + if (partialResult.Height > rowHeight) + { + rowHeight = img.Height; + } + + point.X += img.Width; + + if (point.X >= totalWidth) + { + point.X = 0; + point.Y += rowHeight; + rowHeight = 0; + } + } + } + + using var finalImage = finalImageSurface.Snapshot(); + finalBitmap = SKBitmap.FromImage(finalImage); + } + + var finalImageFileName = Path.ChangeExtension(referenceFileName, ".new.png"); + using (finalBitmap) + { + var dir = Path.GetDirectoryName(finalImageFileName); + Directory.CreateDirectory(dir); + + using (var fileStream = new SKFileWStream(finalImageFileName)) + { + SKPixmap.Encode(fileStream, finalBitmap, SKEncodedImageFormat.Png, 100); + } + + SKBitmap referenceBitmap; + using (var data = SKData.CreateCopy(referenceFileData.Buffer.Raw)) + { + referenceBitmap = SKBitmap.Decode(data); + } + + using (referenceBitmap) + { + var compareResult = PixelMatch.Run(finalBitmap, referenceBitmap, + new PixelMatchOptions + { + Threshold = 0.8, + IncludeAntiAlias = false, + IgnoreTransparent = true, + CreateOutputImage = true + }); + + + using (compareResult.Output) + { + Assert.IsTrue(compareResult.SizesMatch, "Dimensions differ"); + if (compareResult.Mismatch > 0.01) + { + var diffImageName = + Path.ChangeExtension(referenceFileName, ".diff.png"); + using (var fileStream = new SKFileWStream(diffImageName)) + { + SKPixmap.Encode(fileStream, compareResult.Output, + SKEncodedImageFormat.Png, 100); + } + + Assert.Fail( + $"Difference between original and new image is too big: {compareResult.Mismatch:P}, {compareResult.DifferentPixels}/{compareResult.TotalPixels}"); + } + } + } + } + + File.Delete(finalImageFileName); + } + } +} diff --git a/src.csharp/AlphaTab.Windows/AlphaTab.Windows.csproj b/src.csharp/AlphaTab.Windows/AlphaTab.Windows.csproj new file mode 100644 index 000000000..74bad065a --- /dev/null +++ b/src.csharp/AlphaTab.Windows/AlphaTab.Windows.csproj @@ -0,0 +1,29 @@ + + + + AlphaTab + AlphaTab.Windows + AlphaTab.Windows + true + true + netcoreapp3.1 + true + true + $(NoWarn);NU5105 + enable + 8 + + + + + + + + + + + + + + + diff --git a/src.csharp/AlphaTab.Windows/AssemblyInfo.cs b/src.csharp/AlphaTab.Windows/AssemblyInfo.cs new file mode 100644 index 000000000..061bf8685 --- /dev/null +++ b/src.csharp/AlphaTab.Windows/AssemblyInfo.cs @@ -0,0 +1,6 @@ +using System.Windows; + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, + ResourceDictionaryLocation.SourceAssembly +)] diff --git a/src.csharp/AlphaTab.Windows/DelegatedEventEmitter.cs b/src.csharp/AlphaTab.Windows/DelegatedEventEmitter.cs new file mode 100644 index 000000000..3660cbbaa --- /dev/null +++ b/src.csharp/AlphaTab.Windows/DelegatedEventEmitter.cs @@ -0,0 +1,48 @@ +using System; + +namespace AlphaTab +{ + internal class DelegatedEventEmitter : IEventEmitter + { + private readonly Action _on; + private readonly Action _off; + + public DelegatedEventEmitter(Action on, Action off) + { + _on = on; + _off = off; + } + + public void On(Action value) + { + _on(value); + } + + public void Off(Action value) + { + _off(value); + } + } + + internal class DelegatedEventEmitter : IEventEmitterOfT + { + private readonly Action> _on; + private readonly Action> _off; + + public DelegatedEventEmitter(Action> on, Action> off) + { + _on = on; + _off = off; + } + + public void On(Action value) + { + _on(value); + } + + public void Off(Action value) + { + _off(value); + } + } +} diff --git a/src.csharp/AlphaTab.Windows/NAudioSynthOutput.cs b/src.csharp/AlphaTab.Windows/NAudioSynthOutput.cs new file mode 100644 index 000000000..7cce0aeaf --- /dev/null +++ b/src.csharp/AlphaTab.Windows/NAudioSynthOutput.cs @@ -0,0 +1,159 @@ +using System; +using AlphaTab.Synth; +using AlphaTab.Synth.Ds; +using AlphaTab.Core.EcmaScript; +using NAudio.Wave; + +namespace AlphaTab +{ + /// + /// A implementation that uses NAudio to play the + /// sound via Directout. + /// + public class NAudioSynthOutput : WaveProvider32, ISynthOutput, IDisposable + { + private const int BufferSize = 4096; + private const int BufferCount = 10; + private const int PreferredSampleRate = 44100; + + private DirectSoundOut _context; + private CircularSampleBuffer _circularBuffer; + private bool _finished; + + /// + public double SampleRate => PreferredSampleRate; + + /// + /// Initializes a new instance of the class. + /// + public NAudioSynthOutput() + : base(PreferredSampleRate, 2) + { + _context = null!; + _circularBuffer = null!; + } + + /// + public void Activate() + { + } + + + /// + public void Open() + { + _finished = false; + _circularBuffer = new CircularSampleBuffer(BufferSize * BufferCount); + + _context = new DirectSoundOut(100); + _context.Init(this); + + ((EventEmitter) Ready).Trigger(); + } + + /// + public void Dispose() + { + Close(); + } + + /// + /// Closes the synth output and disposes all resources. + /// + public void Close() + { + _finished = true; + _context.Stop(); + _circularBuffer.Clear(); + _context.Dispose(); + } + + /// + public void Play() + { + RequestBuffers(); + _finished = false; + _context.Play(); + } + + /// + public void Pause() + { + _context.Pause(); + } + + /// + public void SequencerFinished() + { + _finished = true; + } + + /// + public void AddSamples(Float32Array f) + { + _circularBuffer.Write(f, 0, f.Length); + } + + /// + public void ResetSamples() + { + _circularBuffer.Clear(); + } + + private void RequestBuffers() + { + // if we fall under the half of buffers + // we request one half + const int count = BufferCount / 2 * BufferSize; + if (_circularBuffer.Count < count && SampleRequest != null) + { + for (var i = 0; i < BufferCount / 2; i++) + { + ((EventEmitter) SampleRequest).Trigger(); + } + } + } + + /// + public override int Read(float[] buffer, int offset, int count) + { + if (_circularBuffer.Count < count) + { + if (_finished) + { + ((EventEmitter) Finished).Trigger(); + } + } + else + { + var read = new Float32Array(count); + _circularBuffer.Read(read, 0, read.Length); + + Buffer.BlockCopy(read.Data, 0, buffer, offset * sizeof(float), + count * sizeof(float)); + + var samples = count / 2; + ((EventEmitterOfT) SamplesPlayed).Trigger(samples); + } + + if (!_finished) + { + RequestBuffers(); + } + + return count; + } + + /// + public IEventEmitter Ready { get; } = new EventEmitter(); + + /// + public IEventEmitterOfT SamplesPlayed { get; } = new EventEmitterOfT(); + + /// + public IEventEmitter SampleRequest { get; } = new EventEmitter(); + + /// + public IEventEmitter Finished { get; } = new EventEmitter(); + } +} diff --git a/src.csharp/AlphaTab.Windows/Themes/Generic.xaml b/src.csharp/AlphaTab.Windows/Themes/Generic.xaml new file mode 100644 index 000000000..4097ad141 --- /dev/null +++ b/src.csharp/AlphaTab.Windows/Themes/Generic.xaml @@ -0,0 +1,21 @@ + + + diff --git a/src.csharp/AlphaTab.Windows/WinForms/AlphaTabControl.cs b/src.csharp/AlphaTab.Windows/WinForms/AlphaTabControl.cs new file mode 100644 index 000000000..4e69aaf78 --- /dev/null +++ b/src.csharp/AlphaTab.Windows/WinForms/AlphaTabControl.cs @@ -0,0 +1,177 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Drawing; +using System.Windows.Forms; +using AlphaTab.Model; + +namespace AlphaTab.WinForms +{ + /// + /// A WinForms UI control to display an instance of alphaTab via + /// items. + /// + public sealed class AlphaTabControl : Panel + { + private IEnumerable? _tracks; + + private readonly AlphaTabLayoutPanel _layoutPanel; + private Settings _settings; + + /// + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public IEnumerable? Tracks + { + get => _tracks; + set + { + if (Equals(_tracks, value)) + { + return; + } + + if (_tracks is INotifyCollectionChanged observable) + { + observable.CollectionChanged -= OnTracksChanged; + } + + _tracks = value; + + if (_tracks is INotifyCollectionChanged observable2) + { + observable2.CollectionChanged += OnTracksChanged; + } + + RenderTracks(); + } + } + + /// + [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] + public Settings Settings + { + get => _settings; + set + { + if (_settings == value) + { + return; + } + + _settings = value; + OnSettingsChanged(value); + } + } + + /// + /// Gets the alphaTab API object. + /// + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public AlphaTabApiBase Api { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + public AlphaTabControl() + { + _settings = null!; + _layoutPanel = new AlphaTabLayoutPanel(); + AutoScroll = true; + Controls.Add(_layoutPanel); + + Settings = new Settings(); + Settings.Player.EnablePlayer = true; + Settings.Player.EnableCursor = true; + } + + /// + protected override void OnHandleCreated(EventArgs e) + { + Api = new AlphaTabApiBase(new WinFormsUiFacade(this, _layoutPanel), + this); + base.OnHandleCreated(e); + } + + /// + protected override void OnPaddingChanged(EventArgs e) + { + base.OnPaddingChanged(e); + if (_layoutPanel != null) + { + _layoutPanel.Location = new Point(Padding.Left, Padding.Top); + } + } + + /// + protected override void OnControlAdded(ControlEventArgs e) + { + base.OnControlAdded(e); + if (e.Control != _layoutPanel) + { + Controls.Remove(e.Control); + } + } + + /// + protected override void OnForeColorChanged(EventArgs e) + { + base.OnForeColorChanged(e); + if (_layoutPanel != null) + { + _layoutPanel.BackColor = ForeColor; + } + } + + private void OnTracksChanged(object sender, NotifyCollectionChangedEventArgs e) + { + RenderTracks(); + } + + /// + /// Initiates a rendering of the currently configured tracks. + /// + public void RenderTracks() + { + if (Tracks == null) + { + return; + } + + Score? score = null; + var trackIndexes = new List(); + foreach (var track in Tracks) + { + if (score == null) + { + score = track.Score; + } + + if (score == track.Score) + { + trackIndexes.Add(track.Index); + } + } + + if (score != null) + { + Api.RenderScore(score, trackIndexes); + } + } + + /// + /// Fired when the settings object changed. + /// + /// + /// Only fires when the whole object changes but not if individual properties changed. + /// + public event Action? SettingsChanged; + + private void OnSettingsChanged(Settings obj) + { + SettingsChanged?.Invoke(obj); + } + } +} diff --git a/src.csharp/AlphaTab.Windows/WinForms/AlphaTabLayoutPanel.cs b/src.csharp/AlphaTab.Windows/WinForms/AlphaTabLayoutPanel.cs new file mode 100644 index 000000000..a017dab5e --- /dev/null +++ b/src.csharp/AlphaTab.Windows/WinForms/AlphaTabLayoutPanel.cs @@ -0,0 +1,56 @@ +using System.Drawing; +using System.Windows.Forms; +using System.Windows.Forms.Layout; + +namespace AlphaTab.WinForms +{ + internal class AlphaTabLayoutPanel : Panel + { + private AlphaTabLayoutEngine? _laoyutEngine; + + public override LayoutEngine LayoutEngine => + _laoyutEngine ??= new AlphaTabLayoutEngine(); + + public AlphaTabLayoutPanel() + { + base.DoubleBuffered = true; + ResizeRedraw = true; + } + + private class AlphaTabLayoutEngine : LayoutEngine + { + public override bool Layout(object container, LayoutEventArgs layoutEventArgs) + { + var parent = (Control) container; + + var xChild = 0; + var yChild = 0; + + var rowHeight = 0; + + foreach (Control? child in parent.Controls) + { + if (child != null) + { + child.Location = new Point(xChild, yChild); + + xChild += child.Width; + if (child.Height > rowHeight) + { + rowHeight = child.Height; + } + + if (xChild >= parent.Width) + { + xChild = 0; + yChild += rowHeight; + rowHeight = 0; + } + } + } + + return false; + } + } + } +} diff --git a/src.csharp/AlphaTab.Windows/WinForms/ControlContainer.cs b/src.csharp/AlphaTab.Windows/WinForms/ControlContainer.cs new file mode 100644 index 000000000..e7d30cc9b --- /dev/null +++ b/src.csharp/AlphaTab.Windows/WinForms/ControlContainer.cs @@ -0,0 +1,136 @@ +using System.Drawing; +using System.Windows.Forms; +using AlphaTab.Platform; + +namespace AlphaTab.WinForms +{ + internal class ControlContainer : IContainer + { + public Control Control { get; } + + public ControlContainer(Control control) + { + Control = control; + + Scroll = new DelegatedEventEmitter( + value => + { + if (Control is ScrollableControl scroll) + { + scroll.Scroll += (sender, args) => value(); + } + }, + value => { } + ); + + Resize = new DelegatedEventEmitter( + value => { Control.Resize += (sender, args) => value(); }, + value => { } + ); + + MouseDown = new DelegatedEventEmitter( + value => + { + Control.MouseDown += (sender, args) => value(new WinFormsMouseEventArgs(Control, args)); + }, + value => { } + ); + + MouseMove = new DelegatedEventEmitter( + value => + { + Control.MouseMove += (sender, args) => value(new WinFormsMouseEventArgs(Control, args)); + }, + value => { } + ); + + MouseUp = new DelegatedEventEmitter( + value => + { + Control.MouseUp += (sender, args) => value(new WinFormsMouseEventArgs(Control, args)); + }, + value => { } + ); + } + + public double Top + { + get => Control.Top; + set => Control.Top = (int)value; + } + + public double Left + { + get => Control.Left; + set => Control.Left = (int)value; + } + + public double Width + { + get => Control.ClientSize.Width - Control.Padding.Horizontal; + set => Control.Width = (int)value + Control.Padding.Horizontal; + } + + + public double Height + { + get => Control.ClientSize.Height - Control.Padding.Vertical; + set => Control.Height = (int)value + Control.Padding.Vertical; + } + + public bool IsVisible => Control.Visible && Control.Width > 0; + + public double ScrollLeft + { + get => Control is ScrollableControl scroll ? scroll.AutoScrollPosition.X : 0; + set + { + if (Control is ScrollableControl scroll) + { + scroll.AutoScrollPosition = new Point((int)value, scroll.AutoScrollPosition.Y); + } + } + } + + public double ScrollTop + { + get => Control is ScrollableControl scroll ? scroll.VerticalScroll.Value : 0; + set + { + if (Control is ScrollableControl scroll) + { + scroll.AutoScrollPosition = new Point(scroll.AutoScrollPosition.X, (int)value); + } + } + } + public void AppendChild(IContainer child) + { + Control.Controls.Add(((ControlContainer)child).Control); + } + + public void StopAnimation() + { + //Control.BeginAnimation(Canvas.LeftProperty, null); + } + + public void TransitionToX(double duration, double x) + { + // TODO: Animation + Control.Left = (int)x; + + //Control.BeginAnimation(Canvas.LeftProperty, + // new DoubleAnimation(x, new Duration(TimeSpan.FromMilliseconds(duration)))); + } + + public void Clear() + { + Control.Controls.Clear(); + } + + public IEventEmitter Scroll { get; set; } + public IEventEmitter Resize { get; set; } + public IEventEmitterOfT MouseDown { get; set; } + public IEventEmitterOfT MouseMove { get; set; } + public IEventEmitterOfT MouseUp { get; set; } + } +} diff --git a/src.csharp/AlphaTab.Windows/WinForms/SkiaUtil.cs b/src.csharp/AlphaTab.Windows/WinForms/SkiaUtil.cs new file mode 100644 index 000000000..7fac9695d --- /dev/null +++ b/src.csharp/AlphaTab.Windows/WinForms/SkiaUtil.cs @@ -0,0 +1,27 @@ +using System.Drawing; +using System.Drawing.Imaging; +using SkiaSharp; + +namespace AlphaTab.WinForms +{ + internal class SkiaUtil + { + public static Bitmap ToBitmap(object data) + { + var image = (SKImage) data; + var info = new SKImageInfo(image.Width, image.Height); + var bitmap = new Bitmap(image.Width, image.Height, PixelFormat.Format32bppPArgb); + var bitmapData = + bitmap.LockBits(new Rectangle(Point.Empty, bitmap.Size), ImageLockMode.WriteOnly, + bitmap.PixelFormat); + // copy + using (var pixmap = new SKPixmap(info, bitmapData.Scan0, bitmapData.Stride)) + { + image.ReadPixels(pixmap, 0, 0); + } + + bitmap.UnlockBits(bitmapData); + return bitmap; + } + } +} diff --git a/src.csharp/AlphaTab.Windows/WinForms/WinFormsMouseEventArgs.cs b/src.csharp/AlphaTab.Windows/WinForms/WinFormsMouseEventArgs.cs new file mode 100644 index 000000000..bbe762173 --- /dev/null +++ b/src.csharp/AlphaTab.Windows/WinForms/WinFormsMouseEventArgs.cs @@ -0,0 +1,35 @@ +using System.Windows.Forms; +using AlphaTab.Platform; + +namespace AlphaTab.WinForms +{ + internal class WinFormsMouseEventArgs : IMouseEventArgs + { + private readonly Control _sender; + private readonly MouseEventArgs _args; + + public WinFormsMouseEventArgs(Control sender, MouseEventArgs args) + { + _sender = sender; + _args = args; + } + + public bool IsLeftMouseButton => _args.Button == MouseButtons.Left; + + public double GetX(IContainer relativeTo) + { + var relativeControl = ((ControlContainer) relativeTo).Control; + return relativeControl.PointToClient(_sender.PointToScreen(_args.Location)).X; + } + + public double GetY(IContainer relativeTo) + { + var relativeControl = ((ControlContainer) relativeTo).Control; + return relativeControl.PointToClient(_sender.PointToScreen(_args.Location)).Y; + } + + public void PreventDefault() + { + } + } +} diff --git a/src.csharp/AlphaTab.Windows/WinForms/WinFormsUiFacade.cs b/src.csharp/AlphaTab.Windows/WinForms/WinFormsUiFacade.cs new file mode 100644 index 000000000..90f322d33 --- /dev/null +++ b/src.csharp/AlphaTab.Windows/WinForms/WinFormsUiFacade.cs @@ -0,0 +1,265 @@ +using System; +using System.Drawing; +using System.IO; +using System.Windows.Forms; +using AlphaTab.Synth; +using AlphaTab.Platform; +using AlphaTab.Platform.CSharp; +using AlphaTab.Rendering; +using AlphaTab.Rendering.Utils; +using SkiaSharp; + +namespace AlphaTab.WinForms +{ + internal class WinFormsUiFacade : ManagedUiFacade + { + private readonly AlphaTabLayoutPanel _layoutPanel; + private event Action? InternalRootContainerBecameVisible; + + public override IContainer RootContainer { get; } + public override IEventEmitter RootContainerBecameVisible { get; } + + public WinFormsUiFacade(AlphaTabControl scrollViewer, AlphaTabLayoutPanel layoutPanel) + { + _layoutPanel = layoutPanel; + RootContainer = new ControlContainer(scrollViewer); + RootContainerBecameVisible = new DelegatedEventEmitter( + value => + { + if (RootContainer.IsVisible) + { + value(); + } + else + { + void OnSizeChanged(object? sender, EventArgs e) + { + SettingsContainer.VisibleChanged -= OnVisibilityChanged; + SettingsContainer.SizeChanged -= OnSizeChanged; + if (SettingsContainer.Visible && SettingsContainer.Width > 0) + { + InternalRootContainerBecameVisible?.Invoke(); + InternalRootContainerBecameVisible = null; + } + } + + void OnVisibilityChanged(object? sender, EventArgs e) + { + SettingsContainer.VisibleChanged -= OnVisibilityChanged; + SettingsContainer.SizeChanged -= OnSizeChanged; + if (SettingsContainer.Visible && SettingsContainer.Width > 0) + { + InternalRootContainerBecameVisible?.Invoke(); + + InternalRootContainerBecameVisible = null; + } + } + + InternalRootContainerBecameVisible += value; + SettingsContainer.VisibleChanged += OnVisibilityChanged; + SettingsContainer.SizeChanged += OnSizeChanged; + } + }, + value => { InternalRootContainerBecameVisible -= value; } + ); + } + + protected override Stream OpenDefaultSoundFont() + { + return typeof(NAudioSynthOutput).Assembly.GetManifestResourceStream( + typeof(NAudioSynthOutput), "default.sf2"); + } + + public override void Initialize(AlphaTabApiBase api, + AlphaTabControl control) + { + base.Initialize(api, control); + api.Settings = control.Settings; + control.SettingsChanged += OnSettingsChanged; + } + + private void OnSettingsChanged(Settings s) + { + Api.Settings = s; + Api.UpdateSettings(); + Api.Render(); + } + + protected override void RenderTracks() + { + SettingsContainer.RenderTracks(); + } + + protected override ISynthOutput CreateSynthOutput() + { + return new NAudioSynthOutput(); + } + + public override void Destroy() + { + SettingsContainer.SettingsChanged -= OnSettingsChanged; + _layoutPanel.Controls.Clear(); + } + + public override IContainer CreateCanvasElement() + { + return new ControlContainer(_layoutPanel); + } + + public override void TriggerEvent(IContainer container, string eventName, + object? details = null, IMouseEventArgs? originalEvent = null) + { + } + + public override void BeginAppendRenderResults(RenderFinishedEventArgs? r) + { + SettingsContainer.BeginInvoke((Action) (renderResult => + { + var panel = _layoutPanel; + + // null result indicates that the rendering finished + if (renderResult == null) + { + if (TotalResultCount.TryDequeue(out var counter)) + { + // so we remove elements that might be from a previous render session + while (panel.Controls.Count > counter.Count) + { + var control = panel.Controls[^1]; + panel.Controls.RemoveAt(panel.Controls.Count - 1); + control.Dispose(); + } + } + } + // NOTE: here we try to replace existing children + else + { + var body = renderResult.RenderResult; + + Bitmap? source = null; + if (body is string) + { + // TODO: svg support + return; + } + + if (body is SKImage skiaImage) + { + using (skiaImage) + { + source = SkiaUtil.ToBitmap(skiaImage); + } + } + else if (body is Bitmap image) + { + source = image; + } + + if (source != null) + { + if (TotalResultCount.TryPeek(out var counter)) + { + if (counter.Count < panel.Controls.Count) + { + var img = (PictureBox) panel.Controls[counter.Count]; + img.Width = (int) renderResult.Width; + img.Height = (int) renderResult.Height; + var oldImg = img.Image; + img.Image = source; + oldImg?.Dispose(); + } + else + { + var img = new PictureBox + { + AutoSize = false, + BackColor = _layoutPanel.ForeColor, + Width = (int) renderResult.Width, + Height = (int) renderResult.Height, + Image = source, + Padding = Padding.Empty, + Margin = Padding.Empty, + BorderStyle = BorderStyle.None + }; + panel.Controls.Add(img); + } + + counter.Count++; + } + } + } + }), r); + } + + + public override void DestroyCursors() + { + } + + public override Platform.Cursors? CreateCursors() + { + // no cursors for winforms, why? - It lacks of proper transparency support + // maybe if somebody asks for it, it's worth an investigation. + return null; + } + + public override void BeginInvoke(Action action) + { + SettingsContainer.BeginInvoke(action); + } + + public override void RemoveHighlights() + { + } + + public override void HighlightElements(string groupId) + { + } + + public override IContainer? CreateSelectionElement() + { + return null; + } + + public override IContainer GetScrollContainer() + { + return new ControlContainer(SettingsContainer); + } + + public override Bounds GetOffset(IContainer? relativeTo, IContainer container) + { + var containerWinForms = ((ControlContainer) container).Control; + + var left = 0; + var top = 0; + + var c = containerWinForms; + while (c != null && c != _layoutPanel) + { + left += c.Left; + top += c.Top; + c = c.Parent; + } + + return new Bounds + { + X = left, + Y = top, + W = containerWinForms.Width, + H = containerWinForms.Height + }; + } + + public override void ScrollToY(IContainer scrollElement, double offset, double speed) + { + var c = ((ControlContainer) scrollElement).Control; + c.AutoScrollOffset = new Point(c.AutoScrollOffset.X, (int) offset); + } + + public override void ScrollToX(IContainer scrollElement, double offset, double speed) + { + var c = ((ControlContainer) scrollElement).Control; + c.AutoScrollOffset = new Point((int) offset, c.AutoScrollOffset.Y); + } + } +} diff --git a/src.csharp/AlphaTab.Windows/Wpf/AlphaTab.cs b/src.csharp/AlphaTab.Windows/Wpf/AlphaTab.cs new file mode 100644 index 000000000..527efbacb --- /dev/null +++ b/src.csharp/AlphaTab.Windows/Wpf/AlphaTab.cs @@ -0,0 +1,235 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; +using AlphaTab.Model; +using Color = System.Windows.Media.Color; + +namespace AlphaTab.Wpf +{ + /// + /// A WPF UI control to display an instance of alphaTab via + /// controls. + /// + public class AlphaTab : Control + { + static AlphaTab() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(AlphaTab), + new FrameworkPropertyMetadata(typeof(AlphaTab))); + } + + private ScrollViewer _scrollView; + + #region Tracks + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty TracksProperty = + DependencyProperty.Register("Tracks", typeof(IEnumerable), typeof(AlphaTab), + new PropertyMetadata(default(IEnumerable), OnTracksChanged)); + + private static void OnTracksChanged(DependencyObject d, + DependencyPropertyChangedEventArgs e) + { + var observable = e.OldValue as INotifyCollectionChanged; + if (observable != null) + { + ((AlphaTab) d).UnregisterObservableCollection(observable); + } + + observable = e.NewValue as INotifyCollectionChanged; + if (observable != null) + { + ((AlphaTab) d).RegisterObservableCollection(observable); + } + + ((AlphaTab) d).RenderTracks(); + } + + private void RegisterObservableCollection(INotifyCollectionChanged collection) + { + collection.CollectionChanged += OnTracksChanged; + } + + private void UnregisterObservableCollection(INotifyCollectionChanged collection) + { + collection.CollectionChanged -= OnTracksChanged; + } + + private void OnTracksChanged(object sender, NotifyCollectionChangedEventArgs e) + { + RenderTracks(); + } + + /// + public IEnumerable Tracks + { + get => (IEnumerable) GetValue(TracksProperty); + set => SetValue(TracksProperty, value); + } + + #endregion + + #region Settings + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty SettingsProperty = DependencyProperty.Register( + "Settings", typeof(Settings), typeof(AlphaTab), + new PropertyMetadata(new Settings(), OnSettingsChanged)); + + private static void OnSettingsChanged(DependencyObject d, + DependencyPropertyChangedEventArgs e) + { + ((AlphaTab) d).SettingsChanged?.Invoke((Settings) e.NewValue); + } + + /// + public Settings Settings + { + get => (Settings) GetValue(SettingsProperty); + set => SetValue(SettingsProperty, value); + } + + #endregion + + #region BarCursorFill + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty BarCursorFillProperty = + DependencyProperty.Register( + "BarCursorFill", typeof(Brush), typeof(AlphaTab), + new PropertyMetadata(new SolidColorBrush(Color.FromArgb(64, 255, 242, 0)))); + + /// + /// Gets or sets the brush used for filling the bar cursor. + /// + public Brush BarCursorFill + { + get => (Brush) GetValue(BarCursorFillProperty); + set => SetValue(BarCursorFillProperty, value); + } + + #endregion + + #region BeatCursorFill + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty BeatCursorFillProperty = + DependencyProperty.Register( + "BeatCursorFill", typeof(Brush), typeof(AlphaTab), + new PropertyMetadata(new SolidColorBrush(Color.FromArgb(191, 64, 64, 255)))); + + /// + /// Gets or sets the brush used for filling the beat cursor. + /// + public Brush BeatCursorFill + { + get => (Brush) GetValue(BeatCursorFillProperty); + set => SetValue(BeatCursorFillProperty, value); + } + + #endregion + + #region SelectionFill + + /// + /// Identifies the dependency property. + /// + public static readonly DependencyProperty SelectionCursorFillProperty = + DependencyProperty.Register( + "SelectionFill", typeof(Brush), typeof(AlphaTab), + new PropertyMetadata(new SolidColorBrush(Color.FromArgb(25, 64, 64, 255)))); + + /// + /// Gets or sets the brush used to fill the elements that highlight the selected area. + /// + public Brush SelectionFill + { + get => (Brush) GetValue(SelectionCursorFillProperty); + set => SetValue(SelectionCursorFillProperty, value); + } + + #endregion + + /// + /// Gets the alphaTab API object. + /// + public AlphaTabApiBase Api { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + public AlphaTab() + { + _scrollView = null!; + Api = null!; + + SnapsToDevicePixels = true; + Settings = new Settings(); + Settings.Player.EnablePlayer = true; + Settings.Player.EnableCursor = true; + } + + /// + public override void OnApplyTemplate() + { + base.OnApplyTemplate(); + _scrollView = (ScrollViewer) Template.FindName("PART_ScrollView", this); + Api = new AlphaTabApiBase(new WpfUiFacade(_scrollView), this); + } + + /// + /// Initiates a rendering of the currently configured tracks if the API object is ready. + /// + public void RenderTracks() + { + if (Api == null) + { + return; + } + + if (Tracks == null) + { + return; + } + + Score? score = null; + var trackIndexes = new List(); + foreach (var track in Tracks) + { + if (score == null) + { + score = track.Score; + } + + if (score == track.Score) + { + trackIndexes.Add(track.Index); + } + } + + if (score != null) + { + Api.RenderScore(score, trackIndexes); + } + } + + /// + /// Fired when the settings object changed. + /// + /// + /// Only fires when the whole object changes but not if individual properties changed. + /// + public event Action? SettingsChanged; + } +} diff --git a/src.csharp/AlphaTab.Windows/Wpf/AlphaTabLayoutPanel.cs b/src.csharp/AlphaTab.Windows/Wpf/AlphaTabLayoutPanel.cs new file mode 100644 index 000000000..abf06c5f0 --- /dev/null +++ b/src.csharp/AlphaTab.Windows/Wpf/AlphaTabLayoutPanel.cs @@ -0,0 +1,53 @@ +using System.Windows; +using System.Windows.Controls; + +namespace AlphaTab.Wpf +{ + internal class AlphaTabLayoutPanel : Panel + { + protected override Size MeasureOverride(Size availableSize) + { + foreach (UIElement? child in InternalChildren) + { + child?.Measure(availableSize); + } + return new Size + { + Width = double.IsInfinity(availableSize.Width) ? MinWidth : availableSize.Width, + Height = double.IsInfinity(availableSize.Height) ? MinHeight : availableSize.Height + }; + } + + protected override Size ArrangeOverride(Size finalSize) + { + var xChild = 0.0; + var yChild = 0.0; + + var rowHeight = 0.0; + + foreach (UIElement? child in InternalChildren) + { + if (child != null) + { + child.Arrange(new Rect(xChild, yChild, child.DesiredSize.Width, + child.DesiredSize.Height)); + + xChild += child.DesiredSize.Width; + if (child.DesiredSize.Height > rowHeight) + { + rowHeight = child.DesiredSize.Height; + } + + if (xChild >= finalSize.Width) + { + xChild = 0; + yChild += rowHeight; + rowHeight = 0; + } + } + } + + return finalSize; + } + } +} diff --git a/src.csharp/AlphaTab.Windows/Wpf/FrameworkElementContainer.cs b/src.csharp/AlphaTab.Windows/Wpf/FrameworkElementContainer.cs new file mode 100644 index 000000000..d47b9670c --- /dev/null +++ b/src.csharp/AlphaTab.Windows/Wpf/FrameworkElementContainer.cs @@ -0,0 +1,162 @@ +using System; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media.Animation; +using AlphaTab.Platform; + +namespace AlphaTab.Wpf +{ + internal class FrameworkElementContainer : IContainer + { + public FrameworkElement Control { get; } + + public FrameworkElementContainer(FrameworkElement control) + { + Control = control; + + Scroll = new DelegatedEventEmitter( + value => + { + if (Control is ScrollViewer scroll) + { + scroll.ScrollChanged += (sender, args) => value(); + } + }, + value => { } + ); + + Resize = new DelegatedEventEmitter( + value => { Control.SizeChanged += (sender, args) => value(); }, + value => { } + ); + + MouseDown = new DelegatedEventEmitter( + value => + { + Control.MouseDown += (sender, args) => value(new WpfMouseEventArgs(args)); + }, + value => { } + ); + + MouseMove = new DelegatedEventEmitter( + value => + { + Control.MouseMove += (sender, args) => value(new WpfMouseEventArgs(args)); + }, + value => { } + ); + + MouseUp = new DelegatedEventEmitter( + value => + { + Control.MouseUp += (sender, args) => value(new WpfMouseEventArgs(args)); + }, + value => { } + ); + } + + public double Top + { + get => (float) Canvas.GetTop(Control); + set => Canvas.SetTop(Control, value); + } + + public double Left + { + get => (float) Canvas.GetLeft(Control); + set => Canvas.SetLeft(Control, value); + } + + public double Width + { + get => (float) Control.ActualWidth; + set => Control.Width = value; + } + + + public double Height + { + get => (float) Control.ActualHeight; + set => Control.Height = value; + } + + public bool IsVisible => Control.IsVisible && Control.ActualWidth > 0; + + public double ScrollLeft + { + get => Control is ScrollViewer scroll ? (float) scroll.HorizontalOffset : 0; + + set + { + if (Control is ScrollViewer scroll) + { + scroll.ScrollToHorizontalOffset(value); + } + } + } + + public double ScrollTop + { + get => Control is ScrollViewer scroll ? (float) scroll.VerticalOffset : 0; + + set + { + if (Control is ScrollViewer scroll) + { + scroll.ScrollToVerticalOffset(value); + } + } + } + + public void AppendChild(IContainer child) + { + if (Control is Panel p) + { + p.Children.Add(((FrameworkElementContainer) child).Control); + } + else if (Control is ScrollViewer s && s.Content is ContentControl sc) + { + sc.Content = ((FrameworkElementContainer) child).Control; + } + else if (Control is ScrollViewer ss && ss.Content is Decorator d) + { + d.Child = ((FrameworkElementContainer) child).Control; + } + else if (Control is ScrollViewer sss && sss.Content is Panel pp) + { + pp.Children.Add(((FrameworkElementContainer) child).Control); + } + else if (Control is ContentControl c) + { + c.Content = ((FrameworkElementContainer) child).Control; + } + } + + + public void StopAnimation() + { + Control.BeginAnimation(Canvas.LeftProperty, null); + } + + public void TransitionToX(double duration, double x) + { + Control.BeginAnimation(Canvas.LeftProperty, + new DoubleAnimation(x, new Duration(TimeSpan.FromMilliseconds(duration)))); + } + + + public void Clear() + { + if (Control is Panel p) + { + p.Children.Clear(); + } + } + + public IEventEmitter Scroll { get; set; } + public IEventEmitter Resize { get; set; } + public IEventEmitterOfT MouseDown { get; set; } + public IEventEmitterOfT MouseMove { get; set; } + public IEventEmitterOfT MouseUp { get; set; } + } +} diff --git a/src.csharp/AlphaTab.Windows/Wpf/GdiImageSource.cs b/src.csharp/AlphaTab.Windows/Wpf/GdiImageSource.cs new file mode 100644 index 000000000..85cdd2e8c --- /dev/null +++ b/src.csharp/AlphaTab.Windows/Wpf/GdiImageSource.cs @@ -0,0 +1,24 @@ +using System.Drawing; +using System.Drawing.Imaging; +using System.Windows.Media; +using System.Windows.Media.Imaging; + +namespace AlphaTab.Wpf +{ + internal static class GdiImageSource + { + public static BitmapSource Create(Bitmap image) + { + var bitmapData = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadOnly, + image.PixelFormat); + + var bitmapSource = BitmapSource.Create( + bitmapData.Width, bitmapData.Height, 96, 96, PixelFormats.Pbgra32, null, + bitmapData.Scan0, bitmapData.Stride * bitmapData.Height, bitmapData.Stride); + + image.UnlockBits(bitmapData); + + return bitmapSource; + } + } +} diff --git a/src.csharp/AlphaTab.Windows/Wpf/SkImageSource.cs b/src.csharp/AlphaTab.Windows/Wpf/SkImageSource.cs new file mode 100644 index 000000000..d6a8a827b --- /dev/null +++ b/src.csharp/AlphaTab.Windows/Wpf/SkImageSource.cs @@ -0,0 +1,26 @@ +using System.Windows; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using SkiaSharp; + +namespace AlphaTab.Wpf +{ + internal static class SkImageSource + { + public static BitmapSource Create(object data) + { + var image = (SKImage) data; + var info = new SKImageInfo(image.Width, image.Height); + var bitmap = new WriteableBitmap(image.Width, image.Height, 96, 96, PixelFormats.Pbgra32, null); + bitmap.Lock(); + // copy + using (var pixmap = new SKPixmap(info, bitmap.BackBuffer, bitmap.BackBufferStride)) + { + image.ReadPixels(pixmap, 0, 0); + } + bitmap.AddDirtyRect(new Int32Rect(0, 0, info.Width, info.Height)); + bitmap.Unlock(); + return bitmap; + } + } +} diff --git a/src.csharp/AlphaTab.Windows/Wpf/WpfMouseEventArgs.cs b/src.csharp/AlphaTab.Windows/Wpf/WpfMouseEventArgs.cs new file mode 100644 index 000000000..8ce5a5750 --- /dev/null +++ b/src.csharp/AlphaTab.Windows/Wpf/WpfMouseEventArgs.cs @@ -0,0 +1,42 @@ +using System.Windows.Input; +using AlphaTab.Platform; + +namespace AlphaTab.Wpf +{ + internal class WpfMouseEventArgs : IMouseEventArgs + { + private readonly MouseEventArgs _args; + + public WpfMouseEventArgs(MouseEventArgs args) + { + _args = args; + } + + public WpfMouseEventArgs(MouseButtonEventArgs args) + { + _args = args; + IsLeftMouseButton = args.ChangedButton == MouseButton.Left && args.ButtonState == MouseButtonState.Pressed; + } + + public bool IsLeftMouseButton { get; } + + public double GetX(IContainer relativeTo) + { + var relativeControl = ((FrameworkElementContainer)relativeTo).Control; + var position = _args.GetPosition(relativeControl); + return (float)position.X; + } + + public double GetY(IContainer relativeTo) + { + var relativeControl = ((FrameworkElementContainer)relativeTo).Control; + var position = _args.GetPosition(relativeControl); + return (float)position.Y; + } + + public void PreventDefault() + { + _args.Handled = true; + } + } +} diff --git a/src.csharp/AlphaTab.Windows/Wpf/WpfUiFacade.cs b/src.csharp/AlphaTab.Windows/Wpf/WpfUiFacade.cs new file mode 100644 index 000000000..302ec0b5c --- /dev/null +++ b/src.csharp/AlphaTab.Windows/Wpf/WpfUiFacade.cs @@ -0,0 +1,326 @@ +using System; +using System.IO; +using System.Linq; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; +using AlphaTab.Synth; +using AlphaTab.Platform; +using AlphaTab.Platform.CSharp; +using AlphaTab.Rendering; +using AlphaTab.Rendering.Utils; +using SkiaSharp; +using Point = System.Windows.Point; +using Image = System.Windows.Controls.Image; + +namespace AlphaTab.Wpf +{ + internal class WpfUiFacade : ManagedUiFacade + { + private readonly ScrollViewer _scrollViewer; + private event Action? InternalRootContainerBecameVisible; + + public override IContainer RootContainer { get; } + + public override IEventEmitter RootContainerBecameVisible { get; } + + public WpfUiFacade(ScrollViewer scrollViewer) + { + _scrollViewer = scrollViewer; + RootContainer = new FrameworkElementContainer(scrollViewer); + RootContainerBecameVisible = new DelegatedEventEmitter( + value => + { + if (RootContainer.IsVisible) + { + value(); + } + else + { + void OnSizeChanged(object sender, SizeChangedEventArgs e) + { + _scrollViewer.IsVisibleChanged -= OnVisibilityChanged; + _scrollViewer.SizeChanged -= OnSizeChanged; + if (_scrollViewer.IsVisible && _scrollViewer.ActualWidth > 0) + { + InternalRootContainerBecameVisible?.Invoke(); + InternalRootContainerBecameVisible = null; + } + } + + void OnVisibilityChanged(object sender, + DependencyPropertyChangedEventArgs e) + { + _scrollViewer.IsVisibleChanged -= OnVisibilityChanged; + _scrollViewer.SizeChanged -= OnSizeChanged; + if (_scrollViewer.IsVisible && _scrollViewer.ActualWidth > 0) + { + InternalRootContainerBecameVisible?.Invoke(); + InternalRootContainerBecameVisible = null; + } + } + + InternalRootContainerBecameVisible += value; + _scrollViewer.IsVisibleChanged += OnVisibilityChanged; + _scrollViewer.SizeChanged += OnSizeChanged; + } + }, + value => { InternalRootContainerBecameVisible -= value; } + ); + } + + protected override Stream OpenDefaultSoundFont() + { + return typeof(NAudioSynthOutput).Assembly.GetManifestResourceStream( + typeof(NAudioSynthOutput), "default.sf2"); + } + + public override void Initialize(AlphaTabApiBase api, AlphaTab control) + { + base.Initialize(api, control); + api.Settings = control.Settings; + control.SettingsChanged += OnSettingsChanged; + } + + private void OnSettingsChanged(Settings s) + { + Api.Settings = s; + Api.UpdateSettings(); + Api.Render(); + } + + protected override void RenderTracks() + { + SettingsContainer.RenderTracks(); + } + + protected override ISynthOutput CreateSynthOutput() + { + return new NAudioSynthOutput(); + } + + public override void Destroy() + { + SettingsContainer.SettingsChanged -= OnSettingsChanged; + _scrollViewer.Content = null; + } + + public override IContainer CreateCanvasElement() + { + var canvas = new WrapPanel + { + VerticalAlignment = VerticalAlignment.Top, + Orientation = Orientation.Horizontal + }; + return new FrameworkElementContainer(canvas); + } + + public override void TriggerEvent(IContainer container, string eventName, + object? details = null, IMouseEventArgs? originalEvent = null) + { + } + + public override void BeginAppendRenderResults(RenderFinishedEventArgs? r) + { + SettingsContainer.Dispatcher?.BeginInvoke( + (Action) (renderResult => + { + var panel = (WrapPanel) ((FrameworkElementContainer) Api.CanvasElement).Control; + + // null result indicates that the rendering finished + if (renderResult == null) + { + if (TotalResultCount.TryDequeue(out var counter)) + { + // so we remove elements that might be from a previous render session + while (panel.Children.Count > counter.Count) + { + panel.Children.RemoveAt(panel.Children.Count - 1); + } + } + } + // NOTE: here we try to replace existing children + else + { + var body = renderResult.RenderResult; + + ImageSource? source = null; + if (body is string) + { + // TODO: svg support + return; + } + + if (body is SKImage skiaImage) + { + using (skiaImage) + { + source = SkImageSource.Create(skiaImage); + } + } + else if (body is System.Drawing.Bitmap image) + { + using (image) + { + source = GdiImageSource.Create(image); + } + } + + if (source != null) + { + if (TotalResultCount.TryPeek(out var counter)) + { + if (counter.Count < panel.Children.Count) + { + var img = (Image) panel.Children[counter.Count]; + img.Width = renderResult.Width; + img.Height = renderResult.Height; + img.Stretch = Stretch.None; + img.SnapsToDevicePixels = true; + img.Source = source; + } + else + { + var img = new Image + { + Width = renderResult.Width, + Height = renderResult.Height, + Source = source + }; + panel.Children.Add(img); + } + + counter.Count++; + } + } + } + }), + r); + } + + public override void DestroyCursors() + { + var element = (Panel) ((FrameworkElementContainer) Api.CanvasElement).Control.Parent; + var cursors = element.Children.OfType() + .FirstOrDefault(c => "at-cursors".Equals(c.Tag)); + if (cursors != null) + { + element.Children.Remove(cursors); + } + } + + public override Cursors CreateCursors() + { + var cursorWrapper = new Canvas + { + Tag = "at-cursors", + HorizontalAlignment = HorizontalAlignment.Left, + VerticalAlignment = VerticalAlignment.Top + }; + + var selectionWrapper = new Canvas(); + + var barCursor = new System.Windows.Shapes.Rectangle + { + Fill = SettingsContainer.BarCursorFill, + IsHitTestVisible = false + }; + + var beatCursor = new System.Windows.Shapes.Rectangle + { + Fill = SettingsContainer.BeatCursorFill, + IsHitTestVisible = false, + Width = 3 + }; + + cursorWrapper.Children.Add(selectionWrapper); + cursorWrapper.Children.Add(barCursor); + cursorWrapper.Children.Add(beatCursor); + + // add cursors to UI + var element = (Panel) ((FrameworkElementContainer) Api.CanvasElement).Control.Parent; + element.Children.Insert(0, cursorWrapper); + + return new Cursors( + new FrameworkElementContainer(cursorWrapper), + new FrameworkElementContainer(barCursor), + new FrameworkElementContainer(beatCursor), + new FrameworkElementContainer(selectionWrapper) + ); + } + + public override void BeginInvoke(Action action) + { + SettingsContainer.Dispatcher?.BeginInvoke(action); + } + + public override void RemoveHighlights() + { + } + + public override void HighlightElements(string groupId) + { + } + + public override IContainer CreateSelectionElement() + { + var selection = new System.Windows.Shapes.Rectangle + { + Fill = SettingsContainer.SelectionFill, + IsHitTestVisible = false + }; + return new FrameworkElementContainer(selection); + } + + public override IContainer GetScrollContainer() + { + return new FrameworkElementContainer(_scrollViewer); + } + + public override Bounds GetOffset(IContainer? relativeTo, IContainer container) + { + var containerWpf = ((FrameworkElementContainer) container).Control; + + var canvas = ((FrameworkElementContainer) Api.CanvasElement).Control; + var position = containerWpf.TranslatePoint(new Point(0, 0), canvas); + + if (relativeTo != null && + ((FrameworkElementContainer) relativeTo).Control is ScrollViewer sv) + { + position.Y -= sv.VerticalOffset; + position.X -= sv.HorizontalOffset; + } + + return new Bounds + { + X = (float) position.X, + Y = (float) position.Y, + W = (float) containerWpf.ActualWidth, + H = (float) containerWpf.ActualHeight + }; + } + + public override void ScrollToY(IContainer scrollElement, double offset, double speed) + { + if (((FrameworkElementContainer) scrollElement).Control is ScrollViewer s) + { + s.ScrollToVerticalOffset(offset); + } + + //scrollElementWpf.BeginAnimation(ScrollViewer.VerticalOffsetProperty, + // new DoubleAnimation(offset, new System.Windows.Duration(TimeSpan.FromMilliseconds(speed)))); + } + + public override void ScrollToX(IContainer scrollElement, double offset, double speed) + { + if (((FrameworkElementContainer) scrollElement).Control is ScrollViewer s) + { + s.ScrollToHorizontalOffset(offset); + } + + //var scrollElementWpf = ((FrameworkElementContainer)scrollElement).Control; + //scrollElementWpf.BeginAnimation(ScrollViewer.HorizontalOffsetProperty, + // new DoubleAnimation(offset, new System.Windows.Duration(TimeSpan.FromMilliseconds(speed)))); + } + } +} diff --git a/src.csharp/AlphaTab.sln b/src.csharp/AlphaTab.sln new file mode 100644 index 000000000..3ff5fb962 --- /dev/null +++ b/src.csharp/AlphaTab.sln @@ -0,0 +1,76 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29920.165 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AlphaTab", "AlphaTab\AlphaTab.csproj", "{8D2AFF3B-4124-4040-B674-D95CD53FDF9D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AlphaTab.Test", "AlphaTab.Test\AlphaTab.Test.csproj", "{55F4E906-C702-4068-9101-028303310ADA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AlphaTab.Windows", "AlphaTab.Windows\AlphaTab.Windows.csproj", "{FAF5C549-DDAA-4254-B9E4-BB487C433626}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AlphaTab.Samples.Player", "Samples\AlphaTab.Samples.Player\AlphaTab.Samples.Player.csproj", "{30B7CFE6-FC7B-40F7-9D3C-BC27DE4911E8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AlphaTab.Samples.PngDump", "Samples\AlphaTab.Samples.PngDump\AlphaTab.Samples.PngDump.csproj", "{461D19B0-AD20-4427-8569-FAF78311DA26}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AlphaTab.Samples.ScoreDump", "Samples\AlphaTab.Samples.ScoreDump\AlphaTab.Samples.ScoreDump.csproj", "{6A365654-90EE-4726-BD99-4CA5358AB258}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AlphaTab.Samples.WinForms", "Samples\AlphaTab.Samples.WinForms\AlphaTab.Samples.WinForms.csproj", "{479765C9-F70E-45EE-984D-A97A33758F32}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AlphaTab.Samples.Wpf", "Samples\AlphaTab.Samples.Wpf\AlphaTab.Samples.Wpf.csproj", "{48677CA2-2131-4F70-9D56-E7A272DE26B8}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{6ED0C3C1-5142-4D44-AB33-0D9DAFDCEE77}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8D2AFF3B-4124-4040-B674-D95CD53FDF9D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8D2AFF3B-4124-4040-B674-D95CD53FDF9D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8D2AFF3B-4124-4040-B674-D95CD53FDF9D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8D2AFF3B-4124-4040-B674-D95CD53FDF9D}.Release|Any CPU.Build.0 = Release|Any CPU + {55F4E906-C702-4068-9101-028303310ADA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55F4E906-C702-4068-9101-028303310ADA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55F4E906-C702-4068-9101-028303310ADA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55F4E906-C702-4068-9101-028303310ADA}.Release|Any CPU.Build.0 = Release|Any CPU + {FAF5C549-DDAA-4254-B9E4-BB487C433626}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FAF5C549-DDAA-4254-B9E4-BB487C433626}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FAF5C549-DDAA-4254-B9E4-BB487C433626}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FAF5C549-DDAA-4254-B9E4-BB487C433626}.Release|Any CPU.Build.0 = Release|Any CPU + {30B7CFE6-FC7B-40F7-9D3C-BC27DE4911E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {30B7CFE6-FC7B-40F7-9D3C-BC27DE4911E8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {30B7CFE6-FC7B-40F7-9D3C-BC27DE4911E8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {30B7CFE6-FC7B-40F7-9D3C-BC27DE4911E8}.Release|Any CPU.Build.0 = Release|Any CPU + {461D19B0-AD20-4427-8569-FAF78311DA26}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {461D19B0-AD20-4427-8569-FAF78311DA26}.Debug|Any CPU.Build.0 = Debug|Any CPU + {461D19B0-AD20-4427-8569-FAF78311DA26}.Release|Any CPU.ActiveCfg = Release|Any CPU + {461D19B0-AD20-4427-8569-FAF78311DA26}.Release|Any CPU.Build.0 = Release|Any CPU + {6A365654-90EE-4726-BD99-4CA5358AB258}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6A365654-90EE-4726-BD99-4CA5358AB258}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6A365654-90EE-4726-BD99-4CA5358AB258}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6A365654-90EE-4726-BD99-4CA5358AB258}.Release|Any CPU.Build.0 = Release|Any CPU + {479765C9-F70E-45EE-984D-A97A33758F32}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {479765C9-F70E-45EE-984D-A97A33758F32}.Debug|Any CPU.Build.0 = Debug|Any CPU + {479765C9-F70E-45EE-984D-A97A33758F32}.Release|Any CPU.ActiveCfg = Release|Any CPU + {479765C9-F70E-45EE-984D-A97A33758F32}.Release|Any CPU.Build.0 = Release|Any CPU + {48677CA2-2131-4F70-9D56-E7A272DE26B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {48677CA2-2131-4F70-9D56-E7A272DE26B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {48677CA2-2131-4F70-9D56-E7A272DE26B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {48677CA2-2131-4F70-9D56-E7A272DE26B8}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {CDCE91E9-ED42-4D67-8B9D-13E73BBA1225} + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {30B7CFE6-FC7B-40F7-9D3C-BC27DE4911E8} = {6ED0C3C1-5142-4D44-AB33-0D9DAFDCEE77} + {461D19B0-AD20-4427-8569-FAF78311DA26} = {6ED0C3C1-5142-4D44-AB33-0D9DAFDCEE77} + {6A365654-90EE-4726-BD99-4CA5358AB258} = {6ED0C3C1-5142-4D44-AB33-0D9DAFDCEE77} + {479765C9-F70E-45EE-984D-A97A33758F32} = {6ED0C3C1-5142-4D44-AB33-0D9DAFDCEE77} + {48677CA2-2131-4F70-9D56-E7A272DE26B8} = {6ED0C3C1-5142-4D44-AB33-0D9DAFDCEE77} + EndGlobalSection +EndGlobal diff --git a/src.csharp/AlphaTab/AlphaTab.csproj b/src.csharp/AlphaTab/AlphaTab.csproj new file mode 100644 index 000000000..89e3e356f --- /dev/null +++ b/src.csharp/AlphaTab/AlphaTab.csproj @@ -0,0 +1,31 @@ + + + + AlphaTab + AlphaTab + AlphaTab + true + true + $(NoWarn);0162;1591;1573;NU5105;0168 + $(NoWarn);8600;8601;8602;8603;8604;8605 + netstandard20 + enable + 8 + + + + + Generated\%(RecursiveDir)\%(Filename)%(Extension) + + + + + + + + + + + + + diff --git a/src.csharp/AlphaTab/Core/Console.cs b/src.csharp/AlphaTab/Core/Console.cs new file mode 100644 index 000000000..15168c36d --- /dev/null +++ b/src.csharp/AlphaTab/Core/Console.cs @@ -0,0 +1,31 @@ +using System.Diagnostics; + +namespace AlphaTab.Core +{ + public class Console + { + public virtual void Debug(string format, object?[]? details) + { + var message = details != null ? string.Format(format, details) : format; + Trace.Write(message, "AlphaTab Debug"); + } + + public virtual void Warn(string format, object?[]? details) + { + var message = details != null ? string.Format(format, details) : format; + Trace.Write(message, "AlphaTab Warn"); + } + + public virtual void Info(string format, object?[]? details) + { + var message = details != null ? string.Format(format, details) : format; + Trace.Write(message, "AlphaTab Info"); + } + + public virtual void Error(string format, object?[]? details) + { + var message = details != null ? string.Format(format, details) : format; + Trace.Write(message, "AlphaTab Error"); + } + } +} diff --git a/src.csharp/AlphaTab/Core/Dom/TextDecoder.cs b/src.csharp/AlphaTab/Core/Dom/TextDecoder.cs new file mode 100644 index 000000000..475f9cea8 --- /dev/null +++ b/src.csharp/AlphaTab/Core/Dom/TextDecoder.cs @@ -0,0 +1,20 @@ +using System.Text; +using AlphaTab.Core.EcmaScript; + +namespace AlphaTab.Core.Dom +{ + public class TextDecoder + { + private readonly Encoding _encoding; + + public TextDecoder(string encoding) + { + _encoding = Encoding.GetEncoding(encoding); + } + + public string Decode(ArrayBuffer data) + { + return _encoding.GetString(data.Raw.Array, data.Raw.Offset, data.Raw.Count); + } + } +} diff --git a/src.csharp/AlphaTab/Core/EcmaScript/ArrayBuffer.cs b/src.csharp/AlphaTab/Core/EcmaScript/ArrayBuffer.cs new file mode 100644 index 000000000..4c83ab502 --- /dev/null +++ b/src.csharp/AlphaTab/Core/EcmaScript/ArrayBuffer.cs @@ -0,0 +1,17 @@ +using System; + +namespace AlphaTab.Core.EcmaScript +{ + public class ArrayBuffer + { + public ArraySegment Raw { get; } + public ArrayBuffer(double size) + { + Raw = new ArraySegment(new byte[(int) size]); + } + public ArrayBuffer(ArraySegment raw) + { + Raw = raw; + } + } +} diff --git a/src.csharp/AlphaTab/Core/EcmaScript/DataView.cs b/src.csharp/AlphaTab/Core/EcmaScript/DataView.cs new file mode 100644 index 000000000..483f4f8a9 --- /dev/null +++ b/src.csharp/AlphaTab/Core/EcmaScript/DataView.cs @@ -0,0 +1,99 @@ +using System; + +namespace AlphaTab.Core.EcmaScript +{ + public class DataView + { + private readonly ArrayBuffer _buffer; + + public DataView(ArrayBuffer buffer) + { + _buffer = buffer; + } + + public void SetUint16(double offset, double value, bool littleEndian) + { + var bytes = BitConverter.GetBytes((ushort) value); + if (littleEndian != BitConverter.IsLittleEndian) + { + Array.Reverse(bytes); + } + + Buffer.BlockCopy(bytes, 0, _buffer.Raw.Array, _buffer.Raw.Offset + (int) offset, + bytes.Length); + } + + public double GetInt16(double offset, bool littleEndian) + { + var bytes = new byte[sizeof(short)]; + Buffer.BlockCopy(_buffer.Raw.Array, _buffer.Raw.Offset + (int) offset, bytes, 0, + bytes.Length); + if (littleEndian != BitConverter.IsLittleEndian) + { + Array.Reverse(bytes); + } + + return BitConverter.ToInt16(bytes, 0); + } + + public void SetInt16(double offset, double value, bool littleEndian) + { + var bytes = BitConverter.GetBytes((short) value); + if (littleEndian != BitConverter.IsLittleEndian) + { + Array.Reverse(bytes); + } + + Buffer.BlockCopy(bytes, 0, _buffer.Raw.Array, _buffer.Raw.Offset + (int) offset, + bytes.Length); + } + + public double GetUint32(double offset, bool littleEndian) + { + var bytes = new byte[sizeof(uint)]; + Buffer.BlockCopy(_buffer.Raw.Array, _buffer.Raw.Offset + (int) offset, bytes, 0, + bytes.Length); + if (littleEndian != BitConverter.IsLittleEndian) + { + Array.Reverse(bytes); + } + + return BitConverter.ToUInt32(bytes, 0); + } + + public void SetInt32(double offset, double value, bool littleEndian) + { + var bytes = BitConverter.GetBytes((int) value); + if (littleEndian != BitConverter.IsLittleEndian) + { + Array.Reverse(bytes); + } + + Buffer.BlockCopy(bytes, 0, _buffer.Raw.Array, _buffer.Raw.Offset + (int) offset, bytes + .Length); + } + + public double GetUint16(double offset, bool littleEndian) + { + var bytes = new byte[sizeof(ushort)]; + Buffer.BlockCopy(_buffer.Raw.Array, _buffer.Raw.Offset + (int) offset, bytes, 0, + bytes.Length); + if (littleEndian != BitConverter.IsLittleEndian) + { + Array.Reverse(bytes); + } + + return BitConverter.ToUInt16(bytes, 0); + } + + public void SetUint8(double offset, double value) + { + _buffer.Raw.Array[_buffer.Raw.Offset + (int) offset] = (byte) value; + } + + public double GetInt8(double offset) + { + return (sbyte) _buffer.Raw.Array[_buffer.Raw.Offset + (int) offset]; + } + } +} diff --git a/src.csharp/AlphaTab/Core/EcmaScript/Date.cs b/src.csharp/AlphaTab/Core/EcmaScript/Date.cs new file mode 100644 index 000000000..751ca3a4b --- /dev/null +++ b/src.csharp/AlphaTab/Core/EcmaScript/Date.cs @@ -0,0 +1,17 @@ +using System; + +namespace AlphaTab.Core.EcmaScript +{ + public static class Date + { + private static readonly DateTime UnixEpoch = + new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + + public static double Now() + { + return DateTime.UtcNow + .Subtract(UnixEpoch) + .TotalMilliseconds; + } + } +} diff --git a/src.csharp/AlphaTab/Core/EcmaScript/Error.cs b/src.csharp/AlphaTab/Core/EcmaScript/Error.cs new file mode 100644 index 000000000..025b02b6a --- /dev/null +++ b/src.csharp/AlphaTab/Core/EcmaScript/Error.cs @@ -0,0 +1,10 @@ +namespace AlphaTab.Core.EcmaScript + +{ + public class Error : System.Exception + { + public Error(string message) : base(message) + { + } + } +} diff --git a/src.csharp/AlphaTab/Core/EcmaScript/Float32Array.cs b/src.csharp/AlphaTab/Core/EcmaScript/Float32Array.cs new file mode 100644 index 000000000..4e43b3836 --- /dev/null +++ b/src.csharp/AlphaTab/Core/EcmaScript/Float32Array.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using AlphaTab.Rendering.Glyphs; + +namespace AlphaTab.Core.EcmaScript +{ + public class Float32Array + { + public readonly float[] Data; + public double Length => Data.Length; + + + public Float32Array(ArrayBuffer buffer) + : this(buffer.Raw.Count / sizeof(float)) + { + Buffer.BlockCopy(buffer.Raw.Array, buffer.Raw.Offset, Data, 0, buffer.Raw.Count); + } + + private Float32Array(float[] data) + { + Data = data; + } + + public Float32Array(double size) + { + Data = new float[(int) size]; + } + + public Float32Array(IEnumerable values) + { + Data = values.Select(d => (float) d).ToArray(); + } + + public Float32Array(IEnumerable values) + { + Data = values.Select(d => (float) d).ToArray(); + } + + public double this[double index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Data[(int) index]; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => Data[(int) index] = (float) value; + } + + public Float32Array Subarray(double start, double end) + { + var sub = new float[(int)(end - start)]; + Buffer.BlockCopy(Data, (int) start * sizeof(float), sub, 0, sub.Length * sizeof(float)); + return new Float32Array(sub); + } + + public void Set(Float32Array subarray, double offset) + { + Buffer.BlockCopy(subarray.Data, + 0, + Data, + (int) offset * sizeof(float), + subarray.Data.Length * sizeof(float)); + } + } +} diff --git a/src.csharp/AlphaTab/Core/EcmaScript/Float64Array.cs b/src.csharp/AlphaTab/Core/EcmaScript/Float64Array.cs new file mode 100644 index 000000000..05c0b1789 --- /dev/null +++ b/src.csharp/AlphaTab/Core/EcmaScript/Float64Array.cs @@ -0,0 +1,22 @@ +using System; +using System.Runtime.CompilerServices; + +namespace AlphaTab.Core.EcmaScript +{ + public class Float64Array + { + private readonly double[] _data; + + public Float64Array(ArrayBuffer buffer) + { + _data = new double[buffer.Raw.Count / sizeof(double)]; + Buffer.BlockCopy(buffer.Raw.Array, buffer.Raw.Offset, _data, 0, buffer.Raw.Count); + } + + public double this[double index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _data[(int) index]; + } + } +} diff --git a/src.csharp/AlphaTab/Core/EcmaScript/Int16Array.cs b/src.csharp/AlphaTab/Core/EcmaScript/Int16Array.cs new file mode 100644 index 000000000..decb35b5f --- /dev/null +++ b/src.csharp/AlphaTab/Core/EcmaScript/Int16Array.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace AlphaTab.Core.EcmaScript +{ + public class Int16Array + { + private readonly short[] _data; + + public Int16Array(double size) + { + _data = new short[(int) size]; + } + + public double this[double index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _data[(int) index]; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => _data[(int) index] = (short) value; + } + } +} diff --git a/src.csharp/AlphaTab/Core/EcmaScript/Int32Array.cs b/src.csharp/AlphaTab/Core/EcmaScript/Int32Array.cs new file mode 100644 index 000000000..0ee1ba2c2 --- /dev/null +++ b/src.csharp/AlphaTab/Core/EcmaScript/Int32Array.cs @@ -0,0 +1,40 @@ +using System; +using System.Runtime.CompilerServices; + +namespace AlphaTab.Core.EcmaScript +{ + public class Int32Array + { + private readonly int[] _data; + + public double Length => _data.Length; + + public Int32Array(double size) + { + _data = new int[(int) size]; + } + + public double this[double index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _data[(int) index]; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => _data[(int) index] = (int) value; + } + + public void Fill(int i) + { + if (i == 0) + { + Array.Clear(_data, 0, _data.Length); + } + else + { + for (var j = 0; j < _data.Length; j++) + { + _data[j] = i; + } + } + } + } +} diff --git a/src.csharp/AlphaTab/Core/EcmaScript/Map.cs b/src.csharp/AlphaTab/Core/EcmaScript/Map.cs new file mode 100644 index 000000000..4cfc545a3 --- /dev/null +++ b/src.csharp/AlphaTab/Core/EcmaScript/Map.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace AlphaTab.Core.EcmaScript +{ + public class Map : IEnumerable> + where TValue : class? + { + private readonly Dictionary _data; + + public Map() + { + _data = new Dictionary(); + } + + public Map(IEnumerable> entries) + { + _data = entries.ToDictionary(e => e.Key, e => e.Value); + } + + public double Size => _data.Count; + + public TValue Get(TKey key) + { + if (_data.TryGetValue(key, out var value)) + { + return value; + } + +#pragma warning disable 8653 + return default; +#pragma warning restore 8653 + } + + public void Set(TKey key, TValue value) + { + _data[key] = value; + } + + public bool Has(TKey key) + { + return _data.ContainsKey(key); + } + + public void Delete(TKey key) + { + _data.Remove(key); + } + + public void Clear() + { + _data.Clear(); + } + + public IEnumerator> GetEnumerator() + { + return _data.Select(d => new MapEntry(d.Key, d.Value)).GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public void ForEach(Action callback) + { + foreach (var kvp in _data) + { + callback(kvp.Value); + } + } + + public void ForEach(Action callback) + { + foreach (var kvp in _data) + { + callback(kvp.Value, kvp.Key); + } + } + + } +} diff --git a/src.csharp/AlphaTab/Core/EcmaScript/MapEntry.cs b/src.csharp/AlphaTab/Core/EcmaScript/MapEntry.cs new file mode 100644 index 000000000..039f6af3c --- /dev/null +++ b/src.csharp/AlphaTab/Core/EcmaScript/MapEntry.cs @@ -0,0 +1,14 @@ +namespace AlphaTab.Core.EcmaScript +{ + public class MapEntry + { + public TKey Key { get; set; } + public TValue Value { get; set; } + + public MapEntry(TKey key, TValue value) + { + Key = key; + Value = value; + } + } +} diff --git a/src.csharp/AlphaTab/Core/EcmaScript/Math.cs b/src.csharp/AlphaTab/Core/EcmaScript/Math.cs new file mode 100644 index 000000000..c0961ee27 --- /dev/null +++ b/src.csharp/AlphaTab/Core/EcmaScript/Math.cs @@ -0,0 +1,90 @@ +using System; + +namespace AlphaTab.Core.EcmaScript +{ + public static class Math + { + private static readonly Random Rnd = new Random(); + public static double PI => System.Math.PI; + + public static double Random() + { + return Rnd.NextDouble(); + } + + public static double Abs(double v) + { + return System.Math.Abs(v); + } + + public static double Max(double a, double b) + { + return System.Math.Max(a, b); + } + + public static double Min(double a, double b) + { + return System.Math.Min(a, b); + } + + public static double Exp(double e) + { + return System.Math.Exp(e); + } + + public static double Log(double d) + { + return System.Math.Log(d); + } + + public static double Pow(double x, double y) + { + return System.Math.Pow(x, y); + } + + public static double Floor(double d) + { + return System.Math.Floor(d); + } + + public static double Round(double d) + { + return System.Math.Round(d); + } + + public static double Sin(double a) + { + return System.Math.Sin(a); + } + + public static double Log2(double d) + { + return System.Math.Log(d, 2); + } + + public static double Sqrt(double d) + { + return System.Math.Sqrt(d); + } + + public static double Ceil(double d) + { + return System.Math.Ceiling(d); + } + + public static double Asin(double d) + { + return System.Math.Asin(d); + } + + public static double Tan(double d) + { + return System.Math.Tan(d); + } + + public static double Log10(double d) + { + return System.Math.Log10(d); + } + } +} diff --git a/src.csharp/AlphaTab/Core/EcmaScript/RegExp.cs b/src.csharp/AlphaTab/Core/EcmaScript/RegExp.cs new file mode 100644 index 000000000..ccdd6d348 --- /dev/null +++ b/src.csharp/AlphaTab/Core/EcmaScript/RegExp.cs @@ -0,0 +1,19 @@ +using System.Text.RegularExpressions; + +namespace AlphaTab.Core.EcmaScript +{ + public class RegExp + { + private readonly Regex _regex; + + public RegExp(string regex) + { + _regex = new Regex(regex, RegexOptions.Compiled); + } + + public bool Exec(string s) + { + return _regex.IsMatch(s); + } + } +} diff --git a/src.csharp/AlphaTab/Core/EcmaScript/String.cs b/src.csharp/AlphaTab/Core/EcmaScript/String.cs new file mode 100644 index 000000000..69c11dc2b --- /dev/null +++ b/src.csharp/AlphaTab/Core/EcmaScript/String.cs @@ -0,0 +1,10 @@ +namespace AlphaTab.Core.EcmaScript +{ + public static class String + { + public static string FromCharCode(double code) + { + return "" + (char) (int) code; + } + } +} diff --git a/src.csharp/AlphaTab/Core/EcmaScript/Uint8Array.cs b/src.csharp/AlphaTab/Core/EcmaScript/Uint8Array.cs new file mode 100644 index 000000000..3571e4c3d --- /dev/null +++ b/src.csharp/AlphaTab/Core/EcmaScript/Uint8Array.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace AlphaTab.Core.EcmaScript +{ + public class Uint8Array + { + private ArraySegment _data; + + public double Length => _data.Count; + + public ArrayBuffer Buffer => new ArrayBuffer(_data); + + public Uint8Array(byte[] data) + { + _data = new ArraySegment(data); + } + + private Uint8Array(ArraySegment data) + { + _data = data; + } + + public Uint8Array(double size) + : this(new byte[(int) size]) + { + } + + public Uint8Array(IEnumerable values) + : this(values.Select(d => (byte) d).ToArray()) + { + } + + public double this[double index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _data.Array[_data.Offset + (int) index]; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => _data.Array[_data.Offset + (int) index] = (byte) value; + } + + public Uint8Array Subarray(double begin, double end) + { + return new Uint8Array(new ArraySegment(_data.Array, _data.Offset + (int) begin, + (int) (end - begin))); + } + + public void Set(Uint8Array subarray, double pos) + { + var buffer = subarray.Buffer.Raw; + System.Buffer.BlockCopy(buffer.Array, (int) buffer.Offset, _data.Array, + _data.Offset + (int) pos, buffer.Count); + } + + public static implicit operator Uint8Array(byte[] v) + { + return new Uint8Array(v); + } + } +} diff --git a/src.csharp/AlphaTab/Core/EcmaScript/ValueTypeMap.cs b/src.csharp/AlphaTab/Core/EcmaScript/ValueTypeMap.cs new file mode 100644 index 000000000..1f7a8f0b3 --- /dev/null +++ b/src.csharp/AlphaTab/Core/EcmaScript/ValueTypeMap.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace AlphaTab.Core.EcmaScript +{ + public class ValueTypeMap : IEnumerable> + where TValue : struct + { + private readonly Dictionary _data; + + public ValueTypeMap() + { + _data = new Dictionary(); + } + + public ValueTypeMap(IEnumerable> entries) + { + _data = entries.ToDictionary(e => e.Key, e => e.Value); + } + + public double Size => _data.Count; + + public TValue? Get(TKey key) + { + if (_data.TryGetValue(key, out var value)) + { + return value; + } + + return null; + } + + public void Set(TKey key, TValue value) + { + _data[key] = value; + } + + public bool Has(TKey key) + { + return _data.ContainsKey(key); + } + + public void Delete(TKey key) + { + _data.Remove(key); + } + + public void Clear() + { + _data.Clear(); + } + + public IEnumerator> GetEnumerator() + { + return _data.Select(d => new MapEntry(d.Key, d.Value)).GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public void ForEach(Action callback) + { + foreach (var kvp in _data) + { + callback(kvp.Value, kvp.Key); + } + } + } +} diff --git a/src.csharp/AlphaTab/Core/Globals.cs b/src.csharp/AlphaTab/Core/Globals.cs new file mode 100644 index 000000000..338a5f7b6 --- /dev/null +++ b/src.csharp/AlphaTab/Core/Globals.cs @@ -0,0 +1,46 @@ +using System.Globalization; + +namespace AlphaTab.Core +{ + public static class Globals + { + public static Console Console { get; } = new Console(); + + public static double ParseInt(string s) + { + if (double.TryParse(s, NumberStyles.Number, CultureInfo.InvariantCulture, out var d)) + { + return (int) d; + } + + return double.NaN; + } + + public static double ParseInt(string s, int radix) + { + if (radix == 16 && int.TryParse(s, NumberStyles.HexNumber, + CultureInfo.InvariantCulture, + out var d)) + { + return d; + } + + return double.NaN; + } + + public static double ParseFloat(string s) + { + if (double.TryParse(s, NumberStyles.Number, CultureInfo.InvariantCulture, out var d)) + { + return d; + } + + return double.NaN; + } + + public static bool IsNaN(double d) + { + return double.IsNaN(d); + } + } +} diff --git a/src.csharp/AlphaTab/Core/Lazy.cs b/src.csharp/AlphaTab/Core/Lazy.cs new file mode 100644 index 000000000..c412dc86f --- /dev/null +++ b/src.csharp/AlphaTab/Core/Lazy.cs @@ -0,0 +1,28 @@ +namespace AlphaTab.Core +{ + public class Lazy where T:class? + { + private readonly System.Func _factory; + + private bool _created = false; + private T _value; + + public Lazy(System.Func factory) + { + _factory = factory; + _value = null!; + } + + public T Value + { + get + { + if (!_created) + { + _value = _factory(); + } + return _value; + } + } + } +} diff --git a/src.csharp/AlphaTab/Core/TypeHelper.cs b/src.csharp/AlphaTab/Core/TypeHelper.cs new file mode 100644 index 000000000..f72836f84 --- /dev/null +++ b/src.csharp/AlphaTab/Core/TypeHelper.cs @@ -0,0 +1,250 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Linq; +using System.Runtime.CompilerServices; +using AlphaTab.Core.EcmaScript; +using AlphaTab.Rendering.Glyphs; + +namespace AlphaTab.Core +{ + internal static class TypeHelper + { + public static IList CreateList(params T[] values) + { + return new List(values); + } + + public static IList Splice(this IList data, double start) + { + var count = data.Count - (int) start; + var items = data.GetRange((int) start, count); + data.RemoveRange((int) start, count); + return new List(items); + } + + public static IList Splice(this IList data, double start, double deleteCount) + { + var items = data.GetRange((int) start, (int) deleteCount); + data.RemoveRange((int) start, (int) deleteCount); + return new List(items); + } + + public static IList Splice(this IList data, double start, double deleteCount, + params T[] newItems) + { + var items = data.GetRange((int) start, (int) deleteCount); + data.RemoveRange((int) start, (int) deleteCount); + data.InsertRange((int) start, newItems); + + return new List(items); + } + + public static IList Slice(this IList data) + { + return new List(new System.Collections.Generic.List(data)); + } + + public static void Reverse(this IList data) + { + if (data is List l) + { + l.Reverse(); + } + else if (data is T[] array) + { + Array.Reverse(array); + } + else + { + throw new NotSupportedException("Cannot reverse list of type " + data.GetType().FullName); + } + } + + public static IList Slice(this IList data, double start) + { + return new List(data.GetRange((int) start, data.Count - (int) start)); + } + + public static IList GetRange(this IList data, int index, int count) + { + if (data is List l) + { + return l.GetRange(index, count); + } + + var newList = new List(); + newList.InsertRange(0, data.Skip(index).Take(count)); + return newList; + } + + public static void RemoveRange(this IList data, int index, int count) + { + if (data is List l) + { + l.RemoveRange(index, count); + } + else + { + while (count > 0 && index >= data.Count) + { + data.RemoveAt(index); + count--; + } + } + } + + public static string Join(this IList data, string separator) + { + return string.Join(separator, data); + } + + public static IList Filter(this IList data, Func func) + { + return data.Where(func).ToList(); + } + + public static void Unshift(this IList data, T item) + { + data.Insert(0, item); + } + + public static T Pop(this IList data) + { + if (data.Count > 0) + { + var last = data.Last(); + data.RemoveAt(data.Count - 1); + return last; + } + +#pragma warning disable 8653 + return default; +#pragma warning restore 8653 + } + + + public static IList Fill(this IList data, T i) + { + for (var j = 0; j < data.Count; j++) + { + data[j] = i; + } + + return data; + } + + public static void InsertRange(this IList data, int index, IEnumerable newItems) + { + if (data is System.Collections.Generic.List l) + { + l.InsertRange(index, newItems); + } + else + { + foreach (var item in newItems) + { + data.Insert(index, item); + index++; + } + } + } + + public static void Sort(this IList data, Func func) + { + if (data is System.Collections.Generic.List l) + { + l.Sort((a, b) => (int) func(a, b)); + } + else if(data is T[] array) + { + Array.Sort(array, (a, b) => (int) func(a, b)); + } + else + { + throw new NotSupportedException("Cannot sort list of type " + data.GetType().FullName); + } + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Substr(this string s, double start, double length) + { + return s.Substring((int) start, (int) length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Substr(this string s, double start) + { + return s.Substring((int) start); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int CharCodeAt(this string s, double index) + { + return s[(int) index]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string ToLowerCase(this string s) + { + return s.ToLowerInvariant(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string ToUpperCase(this string s) + { + return s.ToUpperInvariant(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IList Split(this string s, string separator) + { + return new List(s.Split(new[] {separator}, StringSplitOptions.None)); + } + + public static MapEntry CreateMapEntry(int key, TValue value) + { + return new MapEntry(key, value); + } + + public static MapEntry CreateMapEntry(TKey key, int value) + { + return new MapEntry(key, value); + } + + public static MapEntry CreateMapEntry(TKey key, TValue value) + { + return new MapEntry(key, value); + } + + public static string ToString(this double num, int radix) + { + if (radix == 16) + { + return ((int) num).ToString("X"); + } + + return num.ToString(CultureInfo.InvariantCulture); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsTruthy(string? s) + { + return !string.IsNullOrEmpty(s); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsTruthy(object? s) + { + return s != null; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsTruthy(double s) + { + return !double.IsNaN(s) && s != 0; + } + } +} diff --git a/src.csharp/AlphaTab/Environment.cs b/src.csharp/AlphaTab/Environment.cs new file mode 100644 index 000000000..bf8454f65 --- /dev/null +++ b/src.csharp/AlphaTab/Environment.cs @@ -0,0 +1,53 @@ + +using System; +using System.Threading; +using System.Threading.Tasks; +using AlphaTab.Core.EcmaScript; +using AlphaTab.Platform.CSharp; + +namespace AlphaTab +{ + partial class Environment + { + public const bool SupportsTextDecoder = true; + public static void PlatformInit() + { + + } + + + public static Action Throttle(Action action, double delay) + { + CancellationTokenSource? cancellationTokenSource = null; + return () => + { + cancellationTokenSource?.Cancel(); + cancellationTokenSource = new CancellationTokenSource(); + + Task.Run(async () => + { + await Task.Delay((int)delay, cancellationTokenSource.Token); + action(); + }, + cancellationTokenSource.Token); + }; + } + + private static void CreatePlatformSpecificRenderEngines(Map renderEngines) + { + renderEngines.Set( + "skia", + new RenderEngineFactory(true, () => { + return new SkiaCanvas(); + }) + ); + renderEngines.Set( + "gdi", + new RenderEngineFactory(true, () => { + return new GdiCanvas(); + }) + ); + renderEngines.Set("default", renderEngines.Get("skia")!); + } + } +} diff --git a/src.csharp/AlphaTab/Platform/CSharp/AlphaSynthWorkerApiBase.cs b/src.csharp/AlphaTab/Platform/CSharp/AlphaSynthWorkerApiBase.cs new file mode 100644 index 000000000..a245949e2 --- /dev/null +++ b/src.csharp/AlphaTab/Platform/CSharp/AlphaSynthWorkerApiBase.cs @@ -0,0 +1,209 @@ +using System; +using AlphaTab.Core.EcmaScript; +using AlphaTab.Midi; +using AlphaTab.Synth; + +namespace AlphaTab.Platform.CSharp +{ + internal abstract class AlphaSynthWorkerApiBase : IAlphaSynth + { + private readonly ISynthOutput _output; + private LogLevel _logLevel; + + protected AlphaSynth Player; + + protected AlphaSynthWorkerApiBase(ISynthOutput output, LogLevel logLevel) + { + _output = output; + _logLevel = logLevel; + Player = null!; + } + + public abstract void Destroy(); + protected abstract void DispatchOnUiThread(Action action); + protected abstract void DispatchOnWorkerThread(Action action); + + protected void Initialize() + { + Player = new AlphaSynth(_output); + Player.PositionChanged.On(OnPositionChanged); + Player.StateChanged.On(OnStateChanged); + Player.Finished.On(OnFinished); + Player.SoundFontLoaded.On(OnSoundFontLoaded); + Player.SoundFontLoadFailed.On(OnSoundFontLoadFailed); + Player.MidiLoaded.On(OnMidiLoaded); + Player.MidiLoadFailed.On(OnMidiLoadFailed); + Player.ReadyForPlayback.On(OnReadyForPlayback); + + DispatchOnUiThread(OnReady); + } + + public bool IsReady => Player.IsReady; + public bool IsReadyForPlayback => Player.IsReadyForPlayback; + + public PlayerState State => Player == null ? PlayerState.Paused : Player.State; + + public LogLevel LogLevel + { + get => _logLevel; + set + { + _logLevel = value; + DispatchOnWorkerThread(() => { Player.LogLevel = value; }); + } + } + + public double MasterVolume + { + get => Player.MasterVolume; + set => DispatchOnWorkerThread(() => { Player.MasterVolume = value; }); + } + + public double MetronomeVolume + { + get => Player.MetronomeVolume; + set => DispatchOnWorkerThread(() => { Player.MetronomeVolume = value; }); + } + + public double PlaybackSpeed + { + get => Player.PlaybackSpeed; + set => DispatchOnWorkerThread(() => { Player.PlaybackSpeed = value; }); + } + + public double TickPosition + { + get => Player.TickPosition; + set => DispatchOnWorkerThread(() => { Player.TickPosition = value; }); + } + + public double TimePosition + { + get => Player.TimePosition; + set => DispatchOnWorkerThread(() => { Player.TimePosition = value; }); + } + + public PlaybackRange? PlaybackRange + { + get => Player.PlaybackRange; + set => DispatchOnWorkerThread(() => { Player.PlaybackRange = value; }); + } + + public bool IsLooping + { + get => Player.IsLooping; + set => DispatchOnWorkerThread(() => { Player.IsLooping = value; }); + } + + public bool Play() + { + if (State == PlayerState.Playing || !IsReadyForPlayback) + { + return false; + } + + DispatchOnWorkerThread(() => { Player.Play(); }); + return true; + } + + public void Pause() + { + DispatchOnWorkerThread(() => { Player.Pause(); }); + } + + public void PlayPause() + { + DispatchOnWorkerThread(() => { Player.PlayPause(); }); + } + + public void Stop() + { + DispatchOnWorkerThread(() => { Player.Stop(); }); + } + + public void LoadSoundFont(Uint8Array data) + { + DispatchOnWorkerThread(() => { Player.LoadSoundFont(data); }); + } + + public void LoadMidiFile(MidiFile midi) + { + DispatchOnWorkerThread(() => { Player.LoadMidiFile(midi); }); + } + + public void SetChannelMute(double channel, bool mute) + { + DispatchOnWorkerThread(() => { Player.SetChannelMute(channel, mute); }); + } + + public void ResetChannelStates() + { + DispatchOnWorkerThread(() => { Player.ResetChannelStates(); }); + } + + public void SetChannelSolo(double channel, bool solo) + { + DispatchOnWorkerThread(() => { Player.SetChannelSolo(channel, solo); }); + } + + public void SetChannelVolume(double channel, double volume) + { + DispatchOnWorkerThread(() => { Player.SetChannelVolume(channel, volume); }); + } + + public IEventEmitter Ready { get; } = new EventEmitter(); + public IEventEmitter ReadyForPlayback { get; } = new EventEmitter(); + public IEventEmitter Finished { get; } = new EventEmitter(); + public IEventEmitter SoundFontLoaded { get; } = new EventEmitter(); + public IEventEmitterOfT SoundFontLoadFailed { get; } =new EventEmitterOfT(); + public IEventEmitter MidiLoaded { get; } = new EventEmitter(); + public IEventEmitterOfT MidiLoadFailed { get; } = new EventEmitterOfT(); + public IEventEmitterOfT StateChanged { get; } = new EventEmitterOfT(); + public IEventEmitterOfT PositionChanged { get; } = new EventEmitterOfT(); + + protected virtual void OnReady() + { + DispatchOnUiThread(() => ((EventEmitter)Ready).Trigger()); + } + + protected virtual void OnReadyForPlayback() + { + DispatchOnUiThread(() => ((EventEmitter)ReadyForPlayback).Trigger()); + } + + protected virtual void OnFinished() + { + DispatchOnUiThread(() => ((EventEmitter)Finished).Trigger()); + } + + protected virtual void OnSoundFontLoaded() + { + DispatchOnUiThread(() => ((EventEmitter)SoundFontLoaded).Trigger()); + } + + protected virtual void OnSoundFontLoadFailed(Error e) + { + DispatchOnUiThread(() => ((EventEmitterOfT)SoundFontLoadFailed).Trigger(e)); + } + + protected virtual void OnMidiLoaded() + { + DispatchOnUiThread(() => ((EventEmitter)MidiLoaded).Trigger()); + } + + protected virtual void OnMidiLoadFailed(Error e) + { + DispatchOnUiThread(() => ((EventEmitterOfT)MidiLoadFailed).Trigger(e)); + } + + protected virtual void OnStateChanged(PlayerStateChangedEventArgs obj) + { + DispatchOnUiThread(() => ((EventEmitterOfT)StateChanged).Trigger(obj)); + } + + protected virtual void OnPositionChanged(PositionChangedEventArgs obj) + { + DispatchOnUiThread(() => ((EventEmitterOfT)PositionChanged).Trigger(obj)); + } + } +} diff --git a/src.csharp/AlphaTab/Platform/CSharp/GdiCanvas.cs b/src.csharp/AlphaTab/Platform/CSharp/GdiCanvas.cs new file mode 100644 index 000000000..d43d460c6 --- /dev/null +++ b/src.csharp/AlphaTab/Platform/CSharp/GdiCanvas.cs @@ -0,0 +1,446 @@ +using AlphaTab.Rendering.Glyphs; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.Drawing.Text; +using System.Reflection; +using System.Runtime.InteropServices; +using GdiFont = System.Drawing.Font; +using GdiFontStyle = System.Drawing.FontStyle; +using GdiColor = System.Drawing.Color; +using String = AlphaTab.Core.EcmaScript.String; + +namespace AlphaTab.Platform.CSharp +{ + internal sealed class GdiCanvas : ICanvas + { + private static readonly Graphics MeasurementGraphics; + private static readonly PrivateFontCollection MusicFontCollection; + private static readonly StringFormat MusicFontFormat; + private static readonly StringFormat MusicFontFormatCenter; + + static GdiCanvas() + { + var measurementImage = new Bitmap(1, 1); + var newGraphics = MeasurementGraphics = Graphics.FromImage(measurementImage); + newGraphics.SmoothingMode = SmoothingMode.HighQuality; + newGraphics.TextRenderingHint = TextRenderingHint.AntiAlias; + newGraphics.Clear(GdiColor.Transparent); + + MusicFontFormat = new StringFormat(StringFormat.GenericTypographic) + { + LineAlignment = StringAlignment.Center, + Alignment = StringAlignment.Near + }; + + MusicFontFormatCenter = new StringFormat(StringFormat.GenericTypographic) + { + LineAlignment = StringAlignment.Center, + Alignment = StringAlignment.Center + }; + + MusicFontCollection = new PrivateFontCollection(); + + var type = typeof(GdiCanvas).GetTypeInfo(); + using var bravura = + type.Assembly.GetManifestResourceStream(type.Namespace + ".Bravura.ttf"); + var dataPtr = Marshal.AllocCoTaskMem((int) bravura.Length); + try + { + var fontData = new byte[bravura.Length]; + bravura.Read(fontData, 0, fontData.Length); + Marshal.Copy(fontData, 0, dataPtr, fontData.Length); + + MusicFontCollection.AddMemoryFont(dataPtr, fontData.Length); + } + finally + { + Marshal.FreeCoTaskMem(dataPtr); + } + } + + private static readonly Dictionary FontLookup = + new Dictionary(); + + private static GdiFont GetMusicFont(double scale) + { + if (!FontLookup.TryGetValue(scale, out var font)) + { + FontLookup[scale] = font = + new GdiFont(MusicFontCollection.Families[0], (float) (34 * scale), + GdiFontStyle.Regular, + GraphicsUnit.Pixel); + } + + return font; + } + + + private Bitmap _image; + private double _width; + private double _height; + private Graphics _graphics; + + private GraphicsPath _currentPath; + + private double _currentX; + private double _currentY; + + private readonly StringFormat _stringFormat; + + private double _lineWidth; + private GdiFont _font; + private TextAlign _textAlign; + private TextBaseline _textBaseline; + + private SolidBrush _brush; + private Pen _pen; + private GdiColor _color; + + public Settings Settings { get; set; } + + public Model.Color Color + { + get => new Model.Color(_color.R, _color.G, _color.B, _color.A); + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + _color = GdiColor.FromArgb((byte) value.A, (byte) value.R, (byte) value.G, + (byte) value.B); + RecreateBrush(); + RecreatePen(); + } + } + + public double LineWidth + { + get => _lineWidth; + set + { + _lineWidth = value; + RecreatePen(); + } + } + + + public Model.Font Font + { + get + { + var fs = Model.FontStyle.Plain; + if (_font.Bold) + { + fs = Model.FontStyle.Bold; + } + + if (_font.Italic) + { + fs = Model.FontStyle.Italic; + } + + return new Model.Font(_font.FontFamily.Name, _font.Size * Settings.Display.Scale, + fs); + } + set + { + var fontStyle = GdiFontStyle.Regular; + if (value.IsBold) + { + fontStyle |= GdiFontStyle.Bold; + } + + if (value.IsItalic) + { + fontStyle = GdiFontStyle.Italic; + } + + _font = new GdiFont(value.Family, (float) (value.Size * Settings.Display.Scale), + fontStyle, + GraphicsUnit.Pixel); + } + } + + public TextAlign TextAlign + { + get => _textAlign; + set + { + _textAlign = value; + switch (value) + { + case TextAlign.Left: + _stringFormat.Alignment = StringAlignment.Near; + break; + case TextAlign.Center: + _stringFormat.Alignment = StringAlignment.Center; + break; + case TextAlign.Right: + _stringFormat.Alignment = StringAlignment.Far; + break; + } + } + } + + public TextBaseline TextBaseline + { + get => _textBaseline; + set + { + _textBaseline = value; + switch (value) + { + case TextBaseline.Top: + _stringFormat.LineAlignment = StringAlignment.Near; + break; + case TextBaseline.Middle: + _stringFormat.LineAlignment = StringAlignment.Center; + break; + case TextBaseline.Bottom: + _stringFormat.LineAlignment = StringAlignment.Far; + break; + } + } + } + + public GdiCanvas() + { + _image = null!; + _graphics = null!; + _brush = null!; + _pen = null!; + Settings = null!; + + _width = 1; + _height = 1; + + _currentPath = new GraphicsPath(FillMode.Winding); + _stringFormat = new StringFormat(StringFormat.GenericTypographic) + { + LineAlignment = StringAlignment.Near, + Alignment = StringAlignment.Near + }; + + _lineWidth = 1; + _currentX = 0; + _currentY = 0; + _font = new GdiFont("Arial", 10, GraphicsUnit.Pixel); + _textAlign = TextAlign.Left; + _textBaseline = TextBaseline.Top; + + Color = new Model.Color(255, 255, 255); + + RecreateImage(); + } + + public void BeginRender(double width, double height) + { + _width = width; + _height = height; + RecreateImage(); + } + + public object EndRender() + { + _graphics.Dispose(); + return _image; + } + + public object OnRenderFinished() + { + // nothing to do + return null; + } + + public void BeginGroup(string identifier) + { + } + + public void EndGroup() + { + } + + private void RecreateImage() + { + var newImage = new Bitmap((int) _width, (int) _height, PixelFormat.Format32bppPArgb); + var newGraphics = Graphics.FromImage(newImage); + newGraphics.CompositingMode = CompositingMode.SourceOver; + newGraphics.SmoothingMode = SmoothingMode.HighQuality; + newGraphics.TextRenderingHint = TextRenderingHint.AntiAlias; + + _graphics?.Dispose(); + + _image = newImage; + _graphics = newGraphics; + } + + private void RecreatePen() + { + var newPen = new Pen(_color, (float) _lineWidth); + _pen?.Dispose(); + + _pen = newPen; + } + + private void RecreateBrush() + { + var newBrush = new SolidBrush(_color); + _brush?.Dispose(); + + _brush = newBrush; + } + + public void FillRect(double x, double y, double w, double h) + { + _graphics.FillRectangle(_brush, (float) x, (float) y, (float) w, (float) h); + } + + public void StrokeRect(double x, double y, double w, double h) + { + _graphics.DrawRectangle(_pen, (float) x, (float) y, (float) w, (float) h); + } + + public void BeginPath() + { + _currentPath.StartFigure(); + } + + public void ClosePath() + { + _currentPath.CloseFigure(); + } + + public void MoveTo(double x, double y) + { + _currentX = x; + _currentY = y; + } + + public void LineTo(double x, double y) + { + _currentPath.AddLine((float) _currentX, (float) _currentY, (float) x, (float) y); + _currentX = x; + _currentY = y; + } + + public void QuadraticCurveTo(double cpx, double cpy, double x, double y) + { + _currentPath.AddBezier((float) _currentX, (float) _currentY, (float) cpx, (float) cpy, + (float) cpx, (float) cpy, (float) x, (float) y); + _currentX = x; + _currentY = y; + } + + public void BezierCurveTo(double cp1x, double cp1y, double cp2x, double cp2y, double x, + double y) + { + _currentPath.AddBezier((float) _currentX, (float) _currentY, (float) cp1x, (float) cp1y, + (float) cp2x, (float) cp2y, (float) x, (float) y); + _currentX = x; + _currentY = y; + } + + public void FillCircle(double x, double y, double radius) + { + _currentPath.StartFigure(); + _currentPath.AddEllipse((float) (x - radius), (float) (y - radius), (float) radius * 2, + (float) radius * 2); + _currentPath.CloseFigure(); + _currentX = x; + _currentY = y; + Fill(); + } + + public void StrokeCircle(double x, double y, double radius) + { + _currentPath.StartFigure(); + _currentPath.AddEllipse((float) (x - radius), (float) (y - radius), (float) radius * 2, + (float) radius * 2); + _currentPath.CloseFigure(); + _currentX = x; + _currentY = y; + Stroke(); + } + + public void Fill() + { + _graphics.FillPath(_brush, _currentPath); + _currentPath.Dispose(); + _currentPath = new GraphicsPath(FillMode.Winding); + } + + public void Stroke() + { + _graphics.DrawPath(_pen, _currentPath); + _currentPath.Dispose(); + _currentPath = new GraphicsPath(FillMode.Winding); + } + + public void FillText(string text, double x, double y) + { + _graphics.DrawString(text, _font, _brush, new PointF((float) x, (float) y), _stringFormat); + } + + public double MeasureText(string text) + { + lock (MeasurementGraphics) + { + return MeasurementGraphics.MeasureString(text, _font, new PointF(0,0), _stringFormat).Width; + } + } + + public void FillMusicFontSymbol(double x, double y, double scale, MusicFontSymbol symbol, + bool centerAtPosition = + false) + { + if (symbol == MusicFontSymbol.None) + { + return; + } + + // for whatever reason the padding on GDI font rendering is a bit messed up, there is 1px padding on the left + x += scale; + + _graphics.DrawString(String.FromCharCode((int) symbol), GetMusicFont(scale), + _brush, (float) x, (float) y, + centerAtPosition ? MusicFontFormatCenter : MusicFontFormat); + } + + public void FillMusicFontSymbols(double x, double y, double scale, + IList symbols, + bool centerAtPosition + = false) + { + var s = ""; + foreach (var symbol in symbols) + { + if (symbol != MusicFontSymbol.None) + { + s += String.FromCharCode((int) symbol); + } + } + + // for whatever reason the padding on GDI font rendering is a bit messed up, there is 1px padding on the left + x += scale; + + _graphics.DrawString(s, GetMusicFont(scale), _brush, (float) x, (float) y, + centerAtPosition ? MusicFontFormatCenter : MusicFontFormat); + } + + public void BeginRotate(double centerX, double centerY, double angle) + { + _graphics.TranslateTransform((float) centerX, (float) centerY); + _graphics.RotateTransform((float) angle); + } + + public void EndRotate() + { + _graphics.ResetTransform(); + } + } +} diff --git a/src.csharp/AlphaTab/Platform/CSharp/ManagedThreadAlphaSynthWorkerApi.cs b/src.csharp/AlphaTab/Platform/CSharp/ManagedThreadAlphaSynthWorkerApi.cs new file mode 100644 index 000000000..d3ee39dc2 --- /dev/null +++ b/src.csharp/AlphaTab/Platform/CSharp/ManagedThreadAlphaSynthWorkerApi.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Concurrent; +using System.Threading; +using AlphaTab.Synth; + +namespace AlphaTab.Platform.CSharp +{ + internal class ManagedThreadAlphaSynthWorkerApi : AlphaSynthWorkerApiBase + { + private readonly Action _uiInvoke; + private readonly Thread _workerThread; + private BlockingCollection _workerQueue; + private CancellationTokenSource _workerCancellationToken; + private readonly ManualResetEventSlim? _threadStartedEvent; + + public ManagedThreadAlphaSynthWorkerApi(ISynthOutput output, LogLevel logLevel, Action uiInvoke) + : base(output, logLevel) + { + _uiInvoke = uiInvoke; + + _threadStartedEvent = new ManualResetEventSlim(false); + _workerQueue = new BlockingCollection(); + _workerCancellationToken = new CancellationTokenSource(); + + _workerThread = new Thread(DoWork); + _workerThread.IsBackground = true; + _workerThread.Start(); + + _threadStartedEvent.Wait(); + _workerQueue.Add(Initialize); + _threadStartedEvent.Dispose(); + _threadStartedEvent = null; + } + + public override void Destroy() + { + _workerCancellationToken.Cancel(); + _workerThread.Join(); + } + + protected override void DispatchOnUiThread(Action action) + { + _uiInvoke(action); + } + + private bool CheckAccess() + { + return Thread.CurrentThread == _workerThread; + } + + protected override void DispatchOnWorkerThread(Action action) + { + if (CheckAccess()) + { + action(); + } + else + { + _workerQueue.Add(action); + } + } + + private void DoWork() + { + _threadStartedEvent.Set(); + while (_workerQueue.TryTake(out var action, Timeout.Infinite, _workerCancellationToken.Token)) + { + if (_workerCancellationToken.IsCancellationRequested) + { + break; + } + + action(); + } + } + } +} diff --git a/src.csharp/AlphaTab/Platform/CSharp/ManagedThreadScoreRenderer.cs b/src.csharp/AlphaTab/Platform/CSharp/ManagedThreadScoreRenderer.cs new file mode 100644 index 000000000..4d4eb2100 --- /dev/null +++ b/src.csharp/AlphaTab/Platform/CSharp/ManagedThreadScoreRenderer.cs @@ -0,0 +1,192 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Threading; +using AlphaTab.Core; +using AlphaTab.Core.EcmaScript; +using AlphaTab.Model; +using AlphaTab.Rendering; +using AlphaTab.Rendering.Utils; + +namespace AlphaTab.Platform.CSharp +{ + internal class ManagedThreadScoreRenderer : IScoreRenderer + { + private readonly Action _uiInvoke; + + private readonly Thread _workerThread; + private BlockingCollection _workerQueue; + private ManualResetEventSlim? _threadStartedEvent; + private CancellationTokenSource _workerCancellationToken; + private ScoreRenderer _renderer; + private double _width; + + public BoundsLookup? BoundsLookup { get; private set; } + + public ManagedThreadScoreRenderer(Settings settings, Action uiInvoke) + { + _uiInvoke = uiInvoke; + _renderer = null!; + _threadStartedEvent = new ManualResetEventSlim(false); + _workerQueue = new BlockingCollection(); + _workerCancellationToken = new CancellationTokenSource(); + + _workerThread = new Thread(DoWork); + _workerThread.IsBackground = true; + _workerThread.Start(); + + _threadStartedEvent.Wait(); + + _workerQueue.Add(() => Initialize(settings)); + _threadStartedEvent.Dispose(); + _threadStartedEvent = null; + } + + + private void DoWork() + { + _threadStartedEvent.Set(); + while (_workerQueue.TryTake(out var action, Timeout.Infinite, + _workerCancellationToken.Token)) + { + if (_workerCancellationToken.IsCancellationRequested) + { + break; + } + + action(); + } + } + + private void Initialize(Settings settings) + { + _renderer = new ScoreRenderer(settings); + _renderer.PartialRenderFinished.On(result => + _uiInvoke(() => OnPartialRenderFinished(result))); + _renderer.RenderFinished.On(result => _uiInvoke(() => OnRenderFinished(result))); + _renderer.PostRenderFinished.On(() => + _uiInvoke(() => OnPostFinished(_renderer.BoundsLookup))); + _renderer.PreRender.On(resize => _uiInvoke(() => OnPreRender(resize))); + _renderer.Error.On(e => _uiInvoke(() => OnError(e))); + } + + private void OnPostFinished(BoundsLookup boundsLookup) + { + BoundsLookup = boundsLookup; + OnPostRenderFinished(); + } + + public void Destroy() + { + _workerCancellationToken.Cancel(); + _workerThread.Join(); + } + + public void UpdateSettings(Settings settings) + { + if (CheckAccess()) + { + _renderer.UpdateSettings(settings); + } + else + { + _workerQueue.Add(() => UpdateSettings(settings)); + } + } + + private bool CheckAccess() + { + return Thread.CurrentThread == _workerThread; + } + + public void Render() + { + if (CheckAccess()) + { + _renderer.Render(); + } + else + { + _workerQueue.Add(Render); + } + } + + public double Width + { + get => _width; + set + { + _width = value; + if (CheckAccess()) + { + _renderer.Width = value; + } + else + { + _workerQueue.Add(() => _renderer.Width = value); + } + } + } + + public void ResizeRender() + { + if (CheckAccess()) + { + _renderer.ResizeRender(); + } + else + { + _workerQueue.Add(ResizeRender); + } + } + + public void RenderScore(Score score, IList trackIndexes) + { + if (CheckAccess()) + { + _renderer.RenderScore(score, trackIndexes); + } + else + { + _workerQueue.Add(() => RenderScore(score, trackIndexes)); + } + } + + public IEventEmitterOfT PreRender { get; } = new EventEmitterOfT(); + + protected virtual void OnPreRender(bool isResize) + { + ((EventEmitterOfT) PreRender).Trigger(isResize); + } + + public IEventEmitterOfT PartialRenderFinished { get; } = + new EventEmitterOfT(); + + protected virtual void OnPartialRenderFinished(RenderFinishedEventArgs obj) + { + ((EventEmitterOfT) PartialRenderFinished).Trigger(obj); + } + + public IEventEmitterOfT RenderFinished { get; } = + new EventEmitterOfT(); + + protected virtual void OnRenderFinished(RenderFinishedEventArgs obj) + { + ((EventEmitterOfT) RenderFinished).Trigger(obj); + } + + public IEventEmitterOfT Error { get; } = new EventEmitterOfT(); + + protected virtual void OnError(Error details) + { + ((EventEmitterOfT) Error).Trigger(details); + } + + public IEventEmitter PostRenderFinished { get; } = new EventEmitter(); + + protected virtual void OnPostRenderFinished() + { + ((EventEmitter) PostRenderFinished).Trigger(); + } + } +} diff --git a/src.csharp/AlphaTab/Platform/CSharp/ManagedUiFacade.cs b/src.csharp/AlphaTab/Platform/CSharp/ManagedUiFacade.cs new file mode 100644 index 000000000..30725a2ef --- /dev/null +++ b/src.csharp/AlphaTab/Platform/CSharp/ManagedUiFacade.cs @@ -0,0 +1,149 @@ +using System; +using System.Collections.Concurrent; +using System.IO; +using AlphaTab.Synth; +using AlphaTab.Core.EcmaScript; +using AlphaTab.Importer; +using AlphaTab.Model; +using AlphaTab.Rendering; +using AlphaTab.Rendering.Utils; + +namespace AlphaTab.Platform.CSharp +{ + public abstract class ManagedUiFacade : IUiFacade + where TSettings : class + { + protected ConcurrentQueue TotalResultCount { get; private set; } = null!; + + protected class Counter + { + public int Count; + } + + public double ResizeThrottle => 25; + public bool AreWorkersSupported => true; + public bool CanRender => true; + + protected AlphaTabApiBase Api { get; private set; } = null!; + protected TSettings SettingsContainer { get; private set; } = null!; + + public virtual void Initialize(AlphaTabApiBase api, TSettings settings) + { + Api = api; + SettingsContainer = settings; + TotalResultCount = new ConcurrentQueue(); + } + + public IScoreRenderer CreateWorkerRenderer() + { + return new ManagedThreadScoreRenderer(Api.Settings, BeginInvoke); + } + + protected abstract Stream OpenDefaultSoundFont(); + + public IAlphaSynth CreateWorkerPlayer() + { + var player = new ManagedThreadAlphaSynthWorkerApi(CreateSynthOutput(), + Api.Settings.Core.LogLevel, BeginInvoke); + player.Ready.On(() => + { + using (var sf = OpenDefaultSoundFont()) + using (var ms = new MemoryStream()) + { + sf.CopyTo(ms); + player.LoadSoundFont(new Uint8Array(ms.ToArray())); + } + }); + return player; + } + + protected abstract ISynthOutput CreateSynthOutput(); + + public abstract IContainer RootContainer { get; } + public IEventEmitter CanRenderChanged { get; } = new EventEmitter(); + public abstract IEventEmitter RootContainerBecameVisible { get; } + public abstract void Destroy(); + public abstract IContainer CreateCanvasElement(); + + public abstract void TriggerEvent(IContainer container, string eventName, + object? details = null, IMouseEventArgs? originalEvent = null); + + public virtual void InitialRender() + { + Api.Renderer.PreRender.On(resize => { TotalResultCount.Enqueue(new Counter()); }); + + + RootContainerBecameVisible.On(() => + { + // rendering was possibly delayed due to invisible element + // in this case we need the correct width for autosize + Api.Renderer.Width = (int) RootContainer.Width; + Api.Renderer.UpdateSettings(Api.Settings); + + RenderTracks(); + }); + } + + protected abstract void RenderTracks(); + + public abstract void BeginAppendRenderResults(RenderFinishedEventArgs? renderResults); + public abstract void DestroyCursors(); + public abstract Cursors? CreateCursors(); + public abstract void BeginInvoke(Action action); + public abstract void RemoveHighlights(); + public abstract void HighlightElements(string groupId); + public abstract IContainer? CreateSelectionElement(); + public abstract IContainer GetScrollContainer(); + public abstract Bounds GetOffset(IContainer? scrollElement, IContainer container); + public abstract void ScrollToY(IContainer scrollElement, double offset, double speed); + public abstract void ScrollToX(IContainer scrollElement, double offset, double speed); + + public bool Load(object? data, Action success, Action error) + { + switch (data) + { + case Score score: + success(score); + return true; + case byte[] b: + success(ScoreLoader.LoadScoreFromBytes(new Uint8Array(b), Api.Settings)); + return true; + case Stream s: + { + using (var ms = new MemoryStream()) + { + s.CopyTo(ms); + success(ScoreLoader.LoadScoreFromBytes(new Uint8Array(ms.ToArray()), + Api.Settings)); + } + + return true; + } + default: + return false; + } + } + + public bool LoadSoundFont(object? data) + { + switch (data) + { + case byte[] bytes: + Api.Player.LoadSoundFont(new Uint8Array(bytes)); + return true; + case Stream stream: + { + using (var ms = new MemoryStream()) + { + stream.CopyTo(ms); + Api.Player.LoadSoundFont(new Uint8Array(ms.ToArray())); + } + + return true; + } + default: + return false; + } + } + } +} diff --git a/src.csharp/AlphaTab/Platform/CSharp/SkiaCanvas.cs b/src.csharp/AlphaTab/Platform/CSharp/SkiaCanvas.cs new file mode 100644 index 000000000..6a250e7f6 --- /dev/null +++ b/src.csharp/AlphaTab/Platform/CSharp/SkiaCanvas.cs @@ -0,0 +1,385 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; +using AlphaTab.Model; +using AlphaTab.Rendering.Glyphs; +using SkiaSharp; +using String = AlphaTab.Core.EcmaScript.String; + +namespace AlphaTab.Platform.CSharp +{ + internal class SkiaCanvas : ICanvas + { + private static readonly SKTypeface MusicFont; + private static readonly int MusicFontSize = 34; + + [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] + private static extern IntPtr LoadLibrary(string libname); + + static SkiaCanvas() + { + // https://github.com/mono/SkiaSharp/issues/713 + // https://github.com/mono/SkiaSharp/issues/572 + // manually load skia lib + switch (System.Environment.OSVersion.Platform) + { + case PlatformID.MacOSX: + case PlatformID.Unix: + // I think unix platforms should be fine, to be tested + break; + default: + var libSkiaSharpPath = Path.GetDirectoryName(typeof(SkiaCanvas).Assembly.Location); + if (IntPtr.Size == 4) + { + libSkiaSharpPath = Path.Combine(libSkiaSharpPath, "runtimes", "win-x86", "native", "libSkiaSharp.dll"); + } + else + { + libSkiaSharpPath = Path.Combine(libSkiaSharpPath, "runtimes", "win-x64", "native", "libSkiaSharp.dll"); + } + + Logger.Debug("Skia", "Loading native lib from '" + libSkiaSharpPath + "'"); + var lib = LoadLibrary(libSkiaSharpPath); + if (lib == IntPtr.Zero) + { + Logger.Warning("Skia", "Loading native lib from '" + libSkiaSharpPath + "' failed"); + } + else + { + Logger.Debug("Skia", "Loading native lib from '" + libSkiaSharpPath + "' successful"); + } + + break; + } + + // attempt to load correct skia native lib + var type = typeof(SkiaCanvas).GetTypeInfo(); + using var bravura = type.Assembly.GetManifestResourceStream(type.Namespace + ".Bravura.ttf"); + MusicFont = SKTypeface.FromStream(bravura); + } + + private SKSurface _surface = null!; + private SKPath _path = null!; + private string _typeFaceCache = ""; + private SKTypeface _typeFace = null!; + + public Color Color { get; set; } + public double LineWidth { get; set; } + public Font Font { get; set; } + + public SKTypeface TypeFace + { + get + { + if (_typeFaceCache != Font.ToCssString(Settings.Display.Scale)) + { + if (_typeFace != null) + { + _typeFace.Dispose(); + } + + _typeFaceCache = Font.ToCssString(Settings.Display.Scale); + _typeFace = SKTypeface.FromFamilyName(Font.Family, + Font.IsBold ? SKFontStyleWeight.Bold : SKFontStyleWeight.Normal, + SKFontStyleWidth.Normal, + Font.IsItalic ? SKFontStyleSlant.Italic : SKFontStyleSlant.Upright + ); + } + + return _typeFace; + } + } + + public TextAlign TextAlign { get; set; } + + public TextBaseline TextBaseline { get; set; } + public Settings Settings { get; set; } + + public SkiaCanvas() + { + Color = new Color(255, 255, 255); + LineWidth = 1; + Font = new Font("Arial", 10); + TextAlign = TextAlign.Left; + TextBaseline = TextBaseline.Top; + Settings = null!; + } + + public void BeginRender(double width, double height) + { + var newImage = SKSurface.Create(new SKImageInfo((int)width, + (int)height, + SKImageInfo.PlatformColorType, + SKAlphaType.Premul)); + _surface = newImage; + + if (_path != null) + { + _path.Dispose(); + } + + _path = new SKPath(); + _path.FillType = SKPathFillType.Winding; + } + + public object EndRender() + { + var image = _surface.Snapshot(); + _surface.Dispose(); + return image; + } + + public virtual object OnRenderFinished() + { + // nothing to do + return null; + } + + public void FillRect(double x, double y, double w, double h) + { + using (var paint = CreatePaint()) + { + paint.BlendMode = SKBlendMode.SrcOver; + _surface.Canvas.DrawRect(SKRect.Create((float)x, (float)y, (float)w, (float)h), paint); + } + } + + private SKPaint CreatePaint() + { + var paint = new SKPaint(); + paint.IsAntialias = true; + paint.SubpixelText = true; + paint.DeviceKerningEnabled = true; + paint.Color = new SKColor((byte) Color.R, (byte) Color.G, (byte) Color.B, + (byte) Color.A); + return paint; + } + + public void StrokeRect(double x, double y, double w, double h) + { + using (var paint = CreatePaint()) + { + paint.BlendMode = SKBlendMode.SrcOver; + paint.StrokeWidth = (float)LineWidth; + paint.IsStroke = true; + _surface.Canvas.DrawRect(SKRect.Create((float)x, (float)y, (float)w, (float)h), paint); + } + } + + public void BeginPath() + { + _path.Reset(); + } + + public void ClosePath() + { + _path.Close(); + } + + public void MoveTo(double x, double y) + { + _path.MoveTo((float)x, (float)y); + } + + public void LineTo(double x, double y) + { + _path.LineTo((float)x, (float)y); + } + + public void QuadraticCurveTo(double cpx, double cpy, double x, double y) + { + _path.QuadTo((float)cpx, (float)cpy, (float)x, (float)y); + } + + public void BezierCurveTo(double cp1X, double cp1Y, double cp2X, double cp2Y, double x, double y) + { + _path.CubicTo((float)cp1X, (float)cp1Y, (float)cp2X, (float)cp2Y, (float)x, (float)y); + } + + public void FillCircle(double x, double y, double radius) + { + BeginPath(); + _path.AddCircle((float)x, (float)y, (float)radius); + ClosePath(); + Fill(); + } + + public void StrokeCircle(double x, double y, double radius) + { + BeginPath(); + _path.AddCircle((float)x, (float)y, (float)radius); + ClosePath(); + Stroke(); + } + + public void Fill() + { + using (var paint = CreatePaint()) + { + _surface.Canvas.DrawPath(_path, paint); + } + + _path.Reset(); + } + + public void Stroke() + { + using (var paint = CreatePaint()) + { + paint.StrokeWidth = (float)LineWidth; + paint.IsStroke = true; + _surface.Canvas.DrawPath(_path, paint); + } + + _path.Reset(); + } + + public void BeginGroup(string identifier) + { + } + + public void EndGroup() + { + } + + public void FillText(string text, double x, double y) + { + using (var paint = CreatePaint()) + { + paint.Typeface = TypeFace; + paint.TextSize = (float)(Font.Size * Settings.Display.Scale); + switch (TextAlign) + { + case TextAlign.Left: + paint.TextAlign = SKTextAlign.Left; + break; + case TextAlign.Center: + paint.TextAlign = SKTextAlign.Center; + break; + case TextAlign.Right: + paint.TextAlign = SKTextAlign.Right; + break; + } + + _surface.Canvas.DrawText(text, (int)x, (int)y + GetFontBaseline(TextBaseline, paint), paint); + } + } + + private float GetFontBaseline(TextBaseline baseline, SKPaint paint) + { + switch (baseline) + { + case TextBaseline.Top: // TopTextBaseline + // https://chromium.googlesource.com/chromium/blink/+/master/Source/modules/canvas2d/CanvasRenderingContext2D.cpp#2056 + // According to http://wiki.apache.org/xmlgraphics-fop/LineLayout/AlignmentHandling + // "FOP (Formatting Objects Processor) puts the hanging baseline at 80% of the ascender height" + return (-paint.FontMetrics.Ascent * 4) / 5; + case TextBaseline.Middle: // MiddleTextBaseline + return -paint.FontMetrics.Descent + paint.TextSize / 2; + case TextBaseline.Bottom: // BottomTextBaseline + return -paint.FontMetrics.Descent; + default: + break; + } + + return 0; + } + + public double MeasureText(string text) + { + if (string.IsNullOrEmpty(text)) + { + return 0; + } + + using (var paint = CreatePaint()) + { + paint.Typeface = TypeFace; + paint.TextSize = (float)Font.Size; + switch (TextAlign) + { + case TextAlign.Left: + paint.TextAlign = SKTextAlign.Left; + break; + case TextAlign.Center: + paint.TextAlign = SKTextAlign.Center; + break; + case TextAlign.Right: + paint.TextAlign = SKTextAlign.Right; + break; + } + + return paint.MeasureText(text); + } + } + + public void FillMusicFontSymbol( + double x, + double y, + double scale, + MusicFontSymbol symbol, + bool centerAtPosition = false) + { + if (symbol == MusicFontSymbol.None) + { + return; + } + + using (var paint = CreatePaint()) + { + paint.Typeface = MusicFont; + paint.TextSize = (float)(MusicFontSize * scale); + if (centerAtPosition) + { + paint.TextAlign = SKTextAlign.Center; + } + + _surface.Canvas.DrawText(String.FromCharCode((double)symbol), (float)x, (float)y, paint); + } + } + + public void FillMusicFontSymbols( + double x, + double y, + double scale, + IList symbols, + bool centerAtPosition = false) + { + var s = ""; + foreach (var symbol in symbols) + { + if (symbol != MusicFontSymbol.None) + { + s += String.FromCharCode((double)symbol); + } + } + + using (var paint = CreatePaint()) + { + paint.Typeface = MusicFont; + paint.TextSize = (float)(MusicFontSize * scale); + if (centerAtPosition) + { + paint.TextAlign = SKTextAlign.Center; + } + + + _surface.Canvas.DrawText(s, (float)x, (float)y, paint); + } + } + + public void BeginRotate(double centerX, double centerY, double angle) + { + _surface.Canvas.Save(); + _surface.Canvas.Translate((float)centerX, (float)centerY); + _surface.Canvas.RotateDegrees((float)angle); + } + + public void EndRotate() + { + _surface.Canvas.Restore(); + } + } +} diff --git a/src.csharp/AlphaTab/Platform/Svg/FontSizes.cs b/src.csharp/AlphaTab/Platform/Svg/FontSizes.cs new file mode 100644 index 000000000..1963d453c --- /dev/null +++ b/src.csharp/AlphaTab/Platform/Svg/FontSizes.cs @@ -0,0 +1,18 @@ +using AlphaTab.Core.EcmaScript; + +namespace AlphaTab.Platform.Svg +{ + partial class FontSizes + { + public static void GenerateFontLookup(string family) + { + if (FontSizeLookupTables.Has(family)) + { + return; + } + + // TODO: maybe allow fallback to GDI/Skia based on availability? + FontSizeLookupTables.Set(family, new Uint8Array(new[] {8})); + } + } +} diff --git a/src.csharp/AlphaTab/Properties/AssemblyInfo.cs b/src.csharp/AlphaTab/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..672055c9b --- /dev/null +++ b/src.csharp/AlphaTab/Properties/AssemblyInfo.cs @@ -0,0 +1,4 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("AlphaTab.Test")] +[assembly: InternalsVisibleTo("AlphaTab.Windows")] diff --git a/src.csharp/Directory.Build.props b/src.csharp/Directory.Build.props new file mode 100644 index 000000000..520762682 --- /dev/null +++ b/src.csharp/Directory.Build.props @@ -0,0 +1,22 @@ + + + portable + true + 1.0.0 + 1.0.0.0 + $(AssemblyVersion) + Danielku15 + CoderLine + AlphaTab + en + alphaTab is a cross platform music notation and guitar tablature rendering library. + Copyright © 2020, Daniel Kuschny and Contributors + MPL-2.0 + https://www.alphatab.net + https://github.com/CoderLine/alphaTab + git + music,notation,engraver,renderer + true + snupkg + + diff --git a/src.csharp/Samples/AlphaTab.Samples.Player/AlphaTab.Samples.Player.csproj b/src.csharp/Samples/AlphaTab.Samples.Player/AlphaTab.Samples.Player.csproj new file mode 100644 index 000000000..68e4330bd --- /dev/null +++ b/src.csharp/Samples/AlphaTab.Samples.Player/AlphaTab.Samples.Player.csproj @@ -0,0 +1,9 @@ + + + netcoreapp3.1 + Exe + + + + + diff --git a/src.csharp/Samples/AlphaTab.Samples.Player/Program.cs b/src.csharp/Samples/AlphaTab.Samples.Player/Program.cs new file mode 100644 index 000000000..8d0b93aab --- /dev/null +++ b/src.csharp/Samples/AlphaTab.Samples.Player/Program.cs @@ -0,0 +1,61 @@ +using System; +using System.IO; +using AlphaTab.Midi; +using AlphaTab.Synth; +using AlphaTab.Importer; + +namespace AlphaTab.Samples.Player +{ + public static class Program + { + private static void Main(string[] args) + { + if (args.Length != 2) + { + Console.WriteLine("Usage AlphaTab.Samples.Player.exe PathToFile PathToSoundFont"); + return; + } + + // load score + var score = ScoreLoader.LoadScoreFromBytes(File.ReadAllBytes(args[0])); + + // generate midi + var midiFile = new MidiFile(); + var handler = new AlphaSynthMidiFileHandler(midiFile); + var generator = new MidiFileGenerator(score, null, handler); + generator.Generate(); + + var player = new AlphaSynth(new NAudioSynthOutput()); + player.MidiLoaded.On(() => { Console.WriteLine("Midi loaded"); }); + player.SoundFontLoaded.On(() => { Console.WriteLine("SoundFont loaded"); }); + player.MidiLoadFailed.On(e => { Console.WriteLine("Midi load failed"); }); + player.SoundFontLoadFailed.On(e => { Console.WriteLine("SoundFont load failed"); }); + player.Finished.On(() => + { + Console.WriteLine("Playback finished"); + ((NAudioSynthOutput) player.Output).Close(); + }); + player.PositionChanged.On(e => + { + var currentTime = TimeSpan.FromMilliseconds(e.CurrentTime); + var endTime = TimeSpan.FromMilliseconds(e.EndTime); + + Console.Write("\r{0:mm\\:ss\\:fff} ({1}) of {2:mm\\:ss\\:fff} ({3})", + currentTime, e.CurrentTick, endTime, e.EndTick); + }); + player.ReadyForPlayback.On(() => { Console.WriteLine("Ready for playback"); }); + player.LoadSoundFont(File.ReadAllBytes(args[1])); + player.LoadMidiFile(midiFile); + + Console.WriteLine("Start playing"); + player.Play(); + + Console.WriteLine("Press enter to exit"); + Console.ReadLine(); + + player.Pause(); + + Console.ReadLine(); + } + } +} diff --git a/src.csharp/Samples/AlphaTab.Samples.Player/Properties/launchSettings.json b/src.csharp/Samples/AlphaTab.Samples.Player/Properties/launchSettings.json new file mode 100644 index 000000000..7e54006ee --- /dev/null +++ b/src.csharp/Samples/AlphaTab.Samples.Player/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "AlphaTab.Samples.Player": { + "commandName": "Project", + "commandLineArgs": "$(SolutionDir)..\\test-data\\audio\\full-song.gp5 $(SolutionDir)..\\font\\sonivox\\sonivox.sf2" + } + } +} diff --git a/Samples/CSharp/AlphaTab.Samples.Player/Readme.md b/src.csharp/Samples/AlphaTab.Samples.Player/Readme.md similarity index 100% rename from Samples/CSharp/AlphaTab.Samples.Player/Readme.md rename to src.csharp/Samples/AlphaTab.Samples.Player/Readme.md diff --git a/src.csharp/Samples/AlphaTab.Samples.PngDump/AlphaTab.Samples.PngDump.csproj b/src.csharp/Samples/AlphaTab.Samples.PngDump/AlphaTab.Samples.PngDump.csproj new file mode 100644 index 000000000..dcc78b990 --- /dev/null +++ b/src.csharp/Samples/AlphaTab.Samples.PngDump/AlphaTab.Samples.PngDump.csproj @@ -0,0 +1,9 @@ + + + netcoreapp3.1 + Exe + + + + + diff --git a/src.csharp/Samples/AlphaTab.Samples.PngDump/Program.cs b/src.csharp/Samples/AlphaTab.Samples.PngDump/Program.cs new file mode 100644 index 000000000..f3e1a8dbb --- /dev/null +++ b/src.csharp/Samples/AlphaTab.Samples.PngDump/Program.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.IO; +using AlphaTab.Importer; +using AlphaTab.Rendering; +using SkiaSharp; + +namespace AlphaTab.Samples.PngDump +{ + public static class Program + { + private static void Main(string[] args) + { + if (args.Length != 1) + { + Console.WriteLine("Usage AlphaTab.Samples.PngDump.exe Path"); + return; + } + + // load score + var score = ScoreLoader.LoadScoreFromBytes(File.ReadAllBytes(args[0])); + + // render score with svg engine and desired rendering width + var settings = new Settings(); + settings.Core.Engine = "skia"; + var renderer = new ScoreRenderer(settings) + { + Width = 970 + }; + + // iterate tracks + for (var i = 0; i < score.Tracks.Count; i++) + { + var track = score.Tracks[i]; + + // render track + Console.WriteLine("Rendering track {0} - {1}", i + 1, track.Name); + var images = new List(); + var totalWidth = 0; + var totalHeight = 0; + renderer.PartialRenderFinished.On(r => { images.Add((SKImage) r.RenderResult); }); + renderer.RenderFinished.On(r => + { + totalWidth = (int) r.TotalWidth; + totalHeight = (int) r.TotalHeight; + }); + renderer.RenderScore(score, new List {track.Index}); + + // write png + var info = new FileInfo(args[0]); + var path = Path.Combine(info.DirectoryName, + Path.GetFileNameWithoutExtension(info.Name) + "-" + i + ".png"); + + using var full = SKSurface.Create(new SKImageInfo(totalWidth, totalHeight, + SKImageInfo.PlatformColorType, SKAlphaType.Premul)); + + var y = 0; + foreach (var image in images) + { + full.Canvas.DrawImage(image, new SKRect(0, 0, image.Width, image.Height), + new SKRect(0, y, image.Width, y + image.Height)); + y += image.Height; + } + + using var fullImage = full.Snapshot(); + using var data = fullImage.Encode(SKEncodedImageFormat.Png, 100) + .AsStream(true); + using var fileStream = + new FileStream(path, FileMode.Create, FileAccess.Write); + data.CopyTo(fileStream); + } + } + } +} diff --git a/src.csharp/Samples/AlphaTab.Samples.PngDump/Properties/launchSettings.json b/src.csharp/Samples/AlphaTab.Samples.PngDump/Properties/launchSettings.json new file mode 100644 index 000000000..90cb30469 --- /dev/null +++ b/src.csharp/Samples/AlphaTab.Samples.PngDump/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "AlphaTab.Samples.PngDump": { + "commandName": "Project", + "commandLineArgs": "$(SolutionDir)..\\test-data\\audio\\full-song.gp5" + } + } +} diff --git a/Samples/CSharp/AlphaTab.Samples.PngDump/Readme.md b/src.csharp/Samples/AlphaTab.Samples.PngDump/Readme.md similarity index 100% rename from Samples/CSharp/AlphaTab.Samples.PngDump/Readme.md rename to src.csharp/Samples/AlphaTab.Samples.PngDump/Readme.md diff --git a/src.csharp/Samples/AlphaTab.Samples.ScoreDump/AlphaTab.Samples.ScoreDump.csproj b/src.csharp/Samples/AlphaTab.Samples.ScoreDump/AlphaTab.Samples.ScoreDump.csproj new file mode 100644 index 000000000..dcc78b990 --- /dev/null +++ b/src.csharp/Samples/AlphaTab.Samples.ScoreDump/AlphaTab.Samples.ScoreDump.csproj @@ -0,0 +1,9 @@ + + + netcoreapp3.1 + Exe + + + + + diff --git a/src.csharp/Samples/AlphaTab.Samples.ScoreDump/Program.cs b/src.csharp/Samples/AlphaTab.Samples.ScoreDump/Program.cs new file mode 100644 index 000000000..8f4d1436c --- /dev/null +++ b/src.csharp/Samples/AlphaTab.Samples.ScoreDump/Program.cs @@ -0,0 +1,41 @@ +using System; +using System.IO; +using System.Linq; +using AlphaTab.Importer; +using AlphaTab.Model; + +namespace AlphaTab.Samples.ScoreDump +{ + public static class Program + { + private static void Main(string[] args) + { + if (args.Length != 1) + { + Console.WriteLine("Usage AlphaTab.Samples.ScoreDump.exe Path"); + return; + } + + var score = ScoreLoader.LoadScoreFromBytes(File.ReadAllBytes(args[0])); + + // score info + Console.WriteLine("Title: {0}", score.Title); + Console.WriteLine("Subtitle: {0}", score.SubTitle); + Console.WriteLine("Artist: {0}", score.Artist); + Console.WriteLine("Tempo: {0}", score.Tempo); + Console.WriteLine("Bars: {0}", score.MasterBars.Count); + Console.WriteLine("Time Signature: {0}/{1}", score.MasterBars[0].TimeSignatureNumerator, + score.MasterBars[0].TimeSignatureDenominator); + // tracks + Console.WriteLine("Tracks: "); + for (var i = 0; i < score.Tracks.Count; i++) + { + var track = score.Tracks[i]; + Console.WriteLine(" {0} - {1} - {2}", i + 1, track.Name, + track.Staves.Any(s => s.IsPercussion) + ? "Percussion" + : "Midi Instrument: " + track.PlaybackInfo.Program); + } + } + } +} diff --git a/src.csharp/Samples/AlphaTab.Samples.ScoreDump/Properties/launchSettings.json b/src.csharp/Samples/AlphaTab.Samples.ScoreDump/Properties/launchSettings.json new file mode 100644 index 000000000..9ff5dda4f --- /dev/null +++ b/src.csharp/Samples/AlphaTab.Samples.ScoreDump/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "AlphaTab.Samples.ScoreDump": { + "commandName": "Project", + "commandLineArgs": "$(SolutionDir)..\\test-data\\audio\\full-song.gp5" + } + } +} diff --git a/Samples/CSharp/AlphaTab.Samples.ScoreDump/Readme.md b/src.csharp/Samples/AlphaTab.Samples.ScoreDump/Readme.md similarity index 100% rename from Samples/CSharp/AlphaTab.Samples.ScoreDump/Readme.md rename to src.csharp/Samples/AlphaTab.Samples.ScoreDump/Readme.md diff --git a/src.csharp/Samples/AlphaTab.Samples.WinForms/AlphaTab.Samples.WinForms.csproj b/src.csharp/Samples/AlphaTab.Samples.WinForms/AlphaTab.Samples.WinForms.csproj new file mode 100644 index 000000000..c8b14b639 --- /dev/null +++ b/src.csharp/Samples/AlphaTab.Samples.WinForms/AlphaTab.Samples.WinForms.csproj @@ -0,0 +1,10 @@ + + + netcoreapp3.1 + true + WinExe + + + + + diff --git a/Samples/CSharp/AlphaTab.Samples.WinForms/ColorTools.cs b/src.csharp/Samples/AlphaTab.Samples.WinForms/ColorTools.cs similarity index 79% rename from Samples/CSharp/AlphaTab.Samples.WinForms/ColorTools.cs rename to src.csharp/Samples/AlphaTab.Samples.WinForms/ColorTools.cs index a647cb93a..12f04d500 100644 --- a/Samples/CSharp/AlphaTab.Samples.WinForms/ColorTools.cs +++ b/src.csharp/Samples/AlphaTab.Samples.WinForms/ColorTools.cs @@ -1,22 +1,4 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using System; +using System; using System.Drawing; namespace AlphaTab.Samples.WinForms @@ -93,9 +75,9 @@ public static Color HSL2RGB(double h, double sl, double l) // Return H,S,L in range of 0-1 public static void RGB2HSL(Color rgb, out double h, out double s, out double l) { - double r = rgb.R / 255.0; - double g = rgb.G / 255.0; - double b = rgb.B / 255.0; + var r = rgb.R / 255.0; + var g = rgb.G / 255.0; + var b = rgb.B / 255.0; double v; double m; double vm; diff --git a/Samples/CSharp/AlphaTab.Samples.WinForms/MainWindow.Designer.cs b/src.csharp/Samples/AlphaTab.Samples.WinForms/MainWindow.Designer.cs similarity index 86% rename from Samples/CSharp/AlphaTab.Samples.WinForms/MainWindow.Designer.cs rename to src.csharp/Samples/AlphaTab.Samples.WinForms/MainWindow.Designer.cs index af5fa1d7c..4a104b5c3 100644 --- a/Samples/CSharp/AlphaTab.Samples.WinForms/MainWindow.Designer.cs +++ b/src.csharp/Samples/AlphaTab.Samples.WinForms/MainWindow.Designer.cs @@ -1,22 +1,4 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ - -using AlphaTab.Platform.CSharp.WinForms; +using AlphaTab.WinForms; namespace AlphaTab.Samples.WinForms { @@ -49,11 +31,11 @@ protected override void Dispose(bool disposing) private void InitializeComponent() { this.toolStripContainer1 = new System.Windows.Forms.ToolStripContainer(); - this.alphaTabControl1 = new AlphaTab.Platform.CSharp.WinForms.AlphaTabControl(); + this.alphaTabControl1 = new AlphaTab.WinForms.AlphaTabControl(); this.mainToolBar = new System.Windows.Forms.ToolStrip(); this.openFileButton = new System.Windows.Forms.ToolStripButton(); + this.playPauseButton = new System.Windows.Forms.ToolStripButton(); this.showScoreInfo = new System.Windows.Forms.ToolStripButton(); - this.cmbRenderEngine = new System.Windows.Forms.ToolStripComboBox(); this.splitContainer1 = new System.Windows.Forms.SplitContainer(); this.panel2 = new System.Windows.Forms.Panel(); this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); @@ -73,12 +55,12 @@ private void InitializeComponent() this.tableLayoutPanel2.SuspendLayout(); this.panel3.SuspendLayout(); this.SuspendLayout(); - // + // // toolStripContainer1 - // - // + // + // // toolStripContainer1.ContentPanel - // + // this.toolStripContainer1.ContentPanel.Controls.Add(this.alphaTabControl1); this.toolStripContainer1.ContentPanel.Size = new System.Drawing.Size(891, 318); this.toolStripContainer1.Dock = System.Windows.Forms.DockStyle.Fill; @@ -88,15 +70,15 @@ private void InitializeComponent() this.toolStripContainer1.RightToolStripPanelVisible = false; this.toolStripContainer1.Size = new System.Drawing.Size(891, 359); this.toolStripContainer1.TabIndex = 0; - // + // // toolStripContainer1.TopToolStripPanel - // + // this.toolStripContainer1.TopToolStripPanel.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(76)))), ((int)(((byte)(76)))), ((int)(((byte)(76))))); this.toolStripContainer1.TopToolStripPanel.Controls.Add(this.mainToolBar); this.toolStripContainer1.TopToolStripPanel.RenderMode = System.Windows.Forms.ToolStripRenderMode.System; - // + // // alphaTabControl1 - // + // this.alphaTabControl1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(97)))), ((int)(((byte)(99)))), ((int)(((byte)(98))))); this.alphaTabControl1.Dock = System.Windows.Forms.DockStyle.Fill; this.alphaTabControl1.ForeColor = System.Drawing.Color.WhiteSmoke; @@ -106,16 +88,16 @@ private void InitializeComponent() this.alphaTabControl1.Size = new System.Drawing.Size(891, 318); this.alphaTabControl1.TabIndex = 0; this.alphaTabControl1.Text = "alphaTabControl1"; - // + // // mainToolBar - // + // this.mainToolBar.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(76)))), ((int)(((byte)(76)))), ((int)(((byte)(76))))); this.mainToolBar.Dock = System.Windows.Forms.DockStyle.None; this.mainToolBar.ImageScalingSize = new System.Drawing.Size(24, 24); this.mainToolBar.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.openFileButton, this.showScoreInfo, - this.cmbRenderEngine}); + this.playPauseButton}); this.mainToolBar.LayoutStyle = System.Windows.Forms.ToolStripLayoutStyle.Flow; this.mainToolBar.Location = new System.Drawing.Point(3, 0); this.mainToolBar.Name = "mainToolBar"; @@ -123,9 +105,9 @@ private void InitializeComponent() this.mainToolBar.RenderMode = System.Windows.Forms.ToolStripRenderMode.System; this.mainToolBar.Size = new System.Drawing.Size(189, 41); this.mainToolBar.TabIndex = 0; - // + // // openFileButton - // + // this.openFileButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; this.openFileButton.Image = global::AlphaTab.Samples.WinForms.Properties.Resources.folder_page_white; this.openFileButton.ImageTransparentColor = System.Drawing.Color.Magenta; @@ -133,9 +115,18 @@ private void InitializeComponent() this.openFileButton.Size = new System.Drawing.Size(28, 28); this.openFileButton.Text = "Open File"; this.openFileButton.Click += new System.EventHandler(this.openFileButton_Click); - // + // + // playPauseButton + // + this.playPauseButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + this.playPauseButton.Image = global::AlphaTab.Samples.WinForms.Properties.Resources.control_play; + this.playPauseButton.Name = "playPauseButton"; + this.playPauseButton.Size = new System.Drawing.Size(28, 28); + this.playPauseButton.Text = "Play/Pause"; + this.playPauseButton.Click += new System.EventHandler(this.playPauseButton_Click); + // // showScoreInfo - // + // this.showScoreInfo.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; this.showScoreInfo.Enabled = false; this.showScoreInfo.Image = global::AlphaTab.Samples.WinForms.Properties.Resources.information; @@ -144,39 +135,29 @@ private void InitializeComponent() this.showScoreInfo.Size = new System.Drawing.Size(28, 28); this.showScoreInfo.Text = "Show Score Info"; this.showScoreInfo.Click += new System.EventHandler(this.showScoreInfo_Click); - // - // cmbRenderEngine - // - this.cmbRenderEngine.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.cmbRenderEngine.Items.AddRange(new object[] { - "gdi", - "skia"}); - this.cmbRenderEngine.Name = "cmbRenderEngine"; - this.cmbRenderEngine.Size = new System.Drawing.Size(121, 23); - this.cmbRenderEngine.SelectedIndexChanged += new System.EventHandler(this.cmbRenderEngine_SelectedIndexChanged); - // + // // splitContainer1 - // + // this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; this.splitContainer1.FixedPanel = System.Windows.Forms.FixedPanel.Panel2; this.splitContainer1.Location = new System.Drawing.Point(0, 0); this.splitContainer1.Name = "splitContainer1"; this.splitContainer1.Orientation = System.Windows.Forms.Orientation.Horizontal; - // + // // splitContainer1.Panel1 - // + // this.splitContainer1.Panel1.Controls.Add(this.toolStripContainer1); - // + // // splitContainer1.Panel2 - // + // this.splitContainer1.Panel2.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(93)))), ((int)(((byte)(95)))), ((int)(((byte)(94))))); this.splitContainer1.Panel2.Controls.Add(this.panel2); this.splitContainer1.Size = new System.Drawing.Size(891, 590); this.splitContainer1.SplitterDistance = 359; this.splitContainer1.TabIndex = 2; - // + // // panel2 - // + // this.panel2.AutoScroll = true; this.panel2.Controls.Add(this.tableLayoutPanel2); this.panel2.Dock = System.Windows.Forms.DockStyle.Fill; @@ -184,9 +165,9 @@ private void InitializeComponent() this.panel2.Name = "panel2"; this.panel2.Size = new System.Drawing.Size(891, 227); this.panel2.TabIndex = 1; - // + // // tableLayoutPanel2 - // + // this.tableLayoutPanel2.AutoSize = true; this.tableLayoutPanel2.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; this.tableLayoutPanel2.ColumnCount = 2; @@ -201,9 +182,9 @@ private void InitializeComponent() this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tableLayoutPanel2.Size = new System.Drawing.Size(891, 39); this.tableLayoutPanel2.TabIndex = 0; - // + // // panel3 - // + // this.panel3.AutoSize = true; this.panel3.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; this.panel3.Controls.Add(this.trackDetails); @@ -214,9 +195,9 @@ private void InitializeComponent() this.panel3.Name = "panel3"; this.panel3.Size = new System.Drawing.Size(420, 39); this.panel3.TabIndex = 1; - // + // // trackDetails - // + // this.trackDetails.AutoSize = true; this.trackDetails.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; this.trackDetails.Dock = System.Windows.Forms.DockStyle.Fill; @@ -225,18 +206,18 @@ private void InitializeComponent() this.trackDetails.Name = "trackDetails"; this.trackDetails.Size = new System.Drawing.Size(420, 0); this.trackDetails.TabIndex = 0; - // + // // trackHeaderControl1 - // + // this.trackHeaderControl1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(117)))), ((int)(((byte)(118)))), ((int)(((byte)(119))))); this.trackHeaderControl1.Dock = System.Windows.Forms.DockStyle.Top; this.trackHeaderControl1.Location = new System.Drawing.Point(0, 0); this.trackHeaderControl1.Name = "trackHeaderControl1"; this.trackHeaderControl1.Size = new System.Drawing.Size(420, 39); this.trackHeaderControl1.TabIndex = 0; - // + // // trackBars - // + // this.trackBars.AutoSize = true; this.trackBars.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; this.trackBars.Dock = System.Windows.Forms.DockStyle.Top; @@ -246,9 +227,9 @@ private void InitializeComponent() this.trackBars.Padding = new System.Windows.Forms.Padding(0, 39, 0, 0); this.trackBars.Size = new System.Drawing.Size(471, 39); this.trackBars.TabIndex = 2; - // + // // MainWindow - // + // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(891, 590); @@ -291,7 +272,7 @@ private void InitializeComponent() private TrackHeaderControl trackHeaderControl1; private System.Windows.Forms.Panel trackBars; private System.Windows.Forms.ToolStripButton openFileButton; - private System.Windows.Forms.ToolStripComboBox cmbRenderEngine; + private System.Windows.Forms.ToolStripButton playPauseButton; private AlphaTabControl alphaTabControl1; } } diff --git a/src.csharp/Samples/AlphaTab.Samples.WinForms/MainWindow.cs b/src.csharp/Samples/AlphaTab.Samples.WinForms/MainWindow.cs new file mode 100644 index 000000000..4eef66c50 --- /dev/null +++ b/src.csharp/Samples/AlphaTab.Samples.WinForms/MainWindow.cs @@ -0,0 +1,182 @@ +using System; +using System.IO; +using System.Windows.Forms; +using AlphaTab.Synth; +using AlphaTab.Importer; +using AlphaTab.Model; +using AlphaTab.Samples.WinForms.Properties; +using Track = AlphaTab.Model.Track; + +namespace AlphaTab.Samples.WinForms +{ + public partial class MainWindow : Form + { + private Score _score; + private int _currentTrackIndex; + + public MainWindow() + { + InitializeComponent(); + alphaTabControl1.HandleCreated += (sender, args) => + { + alphaTabControl1.Api.Player.StateChanged.On(OnPlayerStateChanged); + }; + } + + + private void OnPlayerStateChanged(PlayerStateChangedEventArgs e) + { + switch (e.State) + { + case PlayerState.Paused: + playPauseButton.Image = Resources.control_play; + break; + case PlayerState.Playing: + playPauseButton.Image = Resources.control_pause; + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + + private void openFileButton_Click(object sender, EventArgs e) + { + OpenFile(); + } + + #region Score Data + + public Score Score + { + get => _score; + set + { + _score = value; + showScoreInfo.Enabled = value != null; + Text = "AlphaTab - " + (value == null ? "No File Opened" : value.Title); + CurrentTrackIndex = 0; + } + } + + public int CurrentTrackIndex + { + get => _currentTrackIndex; + set + { + _currentTrackIndex = value; + UpdateSelectedTrack(); + var track = CurrentTrack; + if (track != null) + { + alphaTabControl1.Tracks = new[] {track}; + } + } + } + + public Track CurrentTrack + { + get + { + if (Score == null || CurrentTrackIndex < 0 || + CurrentTrackIndex >= _score.Tracks.Count) + { + return null; + } + + return _score.Tracks[_currentTrackIndex]; + } + } + + #endregion + + #region Score Loading + + private void OpenFile() + { + using (var dialog = new OpenFileDialog()) + { + dialog.Filter = + "Supported Files (*.gp3, *.gp4, *.gp5, *.gpx, *.gp)|*.gp3;*.gp4;*.gp5;*.gpx;*.gp"; + if (dialog.ShowDialog(this) == DialogResult.OK) + { + OpenFile(dialog.FileName); + } + } + } + + private void OpenFile(string file) + { + if (!string.IsNullOrWhiteSpace(file) && File.Exists(file)) + { + InternalOpenFile(file); + } + } + + private void InternalOpenFile(string file) + { + try + { + // load the score from the filesystem + Score = ScoreLoader.LoadScoreFromBytes(File.ReadAllBytes(file)); + + trackDetails.Controls.Clear(); + trackBars.Controls.Clear(); + for (var i = Score.Tracks.Count - 1; i >= 0; i--) + { + var details = new TrackDetailsControl(Score.Tracks[i]); + details.Dock = DockStyle.Top; + details.Height = 25; + trackDetails.Controls.Add(details); + details.Selected += details_Click; + + var bars = new TrackBarsControl(Score.Tracks[i]); + bars.Dock = DockStyle.Top; + trackBars.Controls.Add(bars); + } + + UpdateSelectedTrack(); + } + catch (Exception e) + { + MessageBox.Show(this, e.Message, "An error during opening the file occured", + MessageBoxButtons.OK, + MessageBoxIcon.Error); + } + } + + private void details_Click(object sender, EventArgs e) + { + var details = (TrackDetailsControl) sender; + CurrentTrackIndex = _score.Tracks.IndexOf(details.Track); + } + + private void UpdateSelectedTrack() + { + var currentTrack = CurrentTrack; + foreach (TrackDetailsControl trackViewModel in trackDetails.Controls) + { + trackViewModel.IsSelected = currentTrack == trackViewModel.Track; + } + } + + #endregion + + private void showScoreInfo_Click(object sender, EventArgs e) + { + if (_score == null) + { + return; + } + + using (var window = new ScoreInfoWindow(_score)) + { + window.ShowDialog(this); + } + } + + private void playPauseButton_Click(object sender, EventArgs e) + { + alphaTabControl1.Api.PlayPause(); + } + } +} diff --git a/Samples/CSharp/AlphaTab.Samples.WinForms/MainWindow.resx b/src.csharp/Samples/AlphaTab.Samples.WinForms/MainWindow.resx similarity index 100% rename from Samples/CSharp/AlphaTab.Samples.WinForms/MainWindow.resx rename to src.csharp/Samples/AlphaTab.Samples.WinForms/MainWindow.resx diff --git a/src.csharp/Samples/AlphaTab.Samples.WinForms/Program.cs b/src.csharp/Samples/AlphaTab.Samples.WinForms/Program.cs new file mode 100644 index 000000000..1eaadc10b --- /dev/null +++ b/src.csharp/Samples/AlphaTab.Samples.WinForms/Program.cs @@ -0,0 +1,19 @@ +using System; +using System.Windows.Forms; + +namespace AlphaTab.Samples.WinForms +{ + internal static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + private static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new MainWindow()); + } + } +} diff --git a/Samples/CSharp/AlphaTab.Samples.WinForms/Properties/Resources.Designer.cs b/src.csharp/Samples/AlphaTab.Samples.WinForms/Properties/Resources.Designer.cs similarity index 100% rename from Samples/CSharp/AlphaTab.Samples.WinForms/Properties/Resources.Designer.cs rename to src.csharp/Samples/AlphaTab.Samples.WinForms/Properties/Resources.Designer.cs diff --git a/Samples/CSharp/AlphaTab.Samples.WinForms/Properties/Resources.resx b/src.csharp/Samples/AlphaTab.Samples.WinForms/Properties/Resources.resx similarity index 100% rename from Samples/CSharp/AlphaTab.Samples.WinForms/Properties/Resources.resx rename to src.csharp/Samples/AlphaTab.Samples.WinForms/Properties/Resources.resx diff --git a/Samples/CSharp/AlphaTab.Samples.WinForms/Properties/Settings.Designer.cs b/src.csharp/Samples/AlphaTab.Samples.WinForms/Properties/Settings.Designer.cs similarity index 100% rename from Samples/CSharp/AlphaTab.Samples.WinForms/Properties/Settings.Designer.cs rename to src.csharp/Samples/AlphaTab.Samples.WinForms/Properties/Settings.Designer.cs diff --git a/Samples/CSharp/AlphaTab.Samples.WinForms/Properties/Settings.settings b/src.csharp/Samples/AlphaTab.Samples.WinForms/Properties/Settings.settings similarity index 100% rename from Samples/CSharp/AlphaTab.Samples.WinForms/Properties/Settings.settings rename to src.csharp/Samples/AlphaTab.Samples.WinForms/Properties/Settings.settings diff --git a/Samples/CSharp/AlphaTab.Samples.WinForms/Readme.md b/src.csharp/Samples/AlphaTab.Samples.WinForms/Readme.md similarity index 100% rename from Samples/CSharp/AlphaTab.Samples.WinForms/Readme.md rename to src.csharp/Samples/AlphaTab.Samples.WinForms/Readme.md diff --git a/Samples/CSharp/AlphaTab.Samples.WinForms/Resources/control_pause.png b/src.csharp/Samples/AlphaTab.Samples.WinForms/Resources/control_pause.png similarity index 100% rename from Samples/CSharp/AlphaTab.Samples.WinForms/Resources/control_pause.png rename to src.csharp/Samples/AlphaTab.Samples.WinForms/Resources/control_pause.png diff --git a/Samples/CSharp/AlphaTab.Samples.WinForms/Resources/control_play.png b/src.csharp/Samples/AlphaTab.Samples.WinForms/Resources/control_play.png similarity index 100% rename from Samples/CSharp/AlphaTab.Samples.WinForms/Resources/control_play.png rename to src.csharp/Samples/AlphaTab.Samples.WinForms/Resources/control_play.png diff --git a/Samples/CSharp/AlphaTab.Samples.WinForms/Resources/control_stop.png b/src.csharp/Samples/AlphaTab.Samples.WinForms/Resources/control_stop.png similarity index 100% rename from Samples/CSharp/AlphaTab.Samples.WinForms/Resources/control_stop.png rename to src.csharp/Samples/AlphaTab.Samples.WinForms/Resources/control_stop.png diff --git a/Samples/CSharp/AlphaTab.Samples.WinForms/Resources/folder_page_white.png b/src.csharp/Samples/AlphaTab.Samples.WinForms/Resources/folder_page_white.png similarity index 100% rename from Samples/CSharp/AlphaTab.Samples.WinForms/Resources/folder_page_white.png rename to src.csharp/Samples/AlphaTab.Samples.WinForms/Resources/folder_page_white.png diff --git a/Samples/CSharp/AlphaTab.Samples.WinForms/Resources/information.png b/src.csharp/Samples/AlphaTab.Samples.WinForms/Resources/information.png similarity index 100% rename from Samples/CSharp/AlphaTab.Samples.WinForms/Resources/information.png rename to src.csharp/Samples/AlphaTab.Samples.WinForms/Resources/information.png diff --git a/Samples/CSharp/AlphaTab.Samples.WinForms/ScoreInfoWindow.Designer.cs b/src.csharp/Samples/AlphaTab.Samples.WinForms/ScoreInfoWindow.Designer.cs similarity index 93% rename from Samples/CSharp/AlphaTab.Samples.WinForms/ScoreInfoWindow.Designer.cs rename to src.csharp/Samples/AlphaTab.Samples.WinForms/ScoreInfoWindow.Designer.cs index 34b1b91d3..470aaa0e1 100644 --- a/Samples/CSharp/AlphaTab.Samples.WinForms/ScoreInfoWindow.Designer.cs +++ b/src.csharp/Samples/AlphaTab.Samples.WinForms/ScoreInfoWindow.Designer.cs @@ -1,21 +1,4 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Samples.WinForms +namespace AlphaTab.Samples.WinForms { partial class ScoreInfoWindow { diff --git a/src.csharp/Samples/AlphaTab.Samples.WinForms/ScoreInfoWindow.cs b/src.csharp/Samples/AlphaTab.Samples.WinForms/ScoreInfoWindow.cs new file mode 100644 index 000000000..02cbc4790 --- /dev/null +++ b/src.csharp/Samples/AlphaTab.Samples.WinForms/ScoreInfoWindow.cs @@ -0,0 +1,27 @@ +using System.Windows.Forms; +using AlphaTab.Model; + +namespace AlphaTab.Samples.WinForms +{ + public partial class ScoreInfoWindow : Form + { + public ScoreInfoWindow() + { + InitializeComponent(); + } + + public ScoreInfoWindow(Score score) + { + InitializeComponent(); + txtAlbum.Text = score.Album; + txtArtist.Text = score.Artist; + txtCopyright.Text = score.Copyright; + txtLyrics.Text = score.Words; + txtMusic.Text = score.Music; + txtNotes.Text = score.Notices; + txtSubTitle.Text = score.SubTitle; + txtTab.Text = score.Tab; + txtTitle.Text = score.Title; + } + } +} diff --git a/Samples/CSharp/AlphaTab.Samples.WinForms/ScoreInfoWindow.resx b/src.csharp/Samples/AlphaTab.Samples.WinForms/ScoreInfoWindow.resx similarity index 100% rename from Samples/CSharp/AlphaTab.Samples.WinForms/ScoreInfoWindow.resx rename to src.csharp/Samples/AlphaTab.Samples.WinForms/ScoreInfoWindow.resx diff --git a/src.csharp/Samples/AlphaTab.Samples.WinForms/TrackBarsControl.cs b/src.csharp/Samples/AlphaTab.Samples.WinForms/TrackBarsControl.cs new file mode 100644 index 000000000..b083ab046 --- /dev/null +++ b/src.csharp/Samples/AlphaTab.Samples.WinForms/TrackBarsControl.cs @@ -0,0 +1,99 @@ +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Windows.Forms; +using AlphaTab.Model; +using Color = System.Drawing.Color; + +namespace AlphaTab.Samples.WinForms +{ + internal class TrackBarsControl : Control + { + private static readonly Size BlockSize = new Size(25, 25); + private readonly bool[] _usedBars; + private Color _startColor; + private Color _endColor; + + public TrackBarsControl(Track track) + { + SetStyle(ControlStyles.FixedHeight, true); + SetStyle(ControlStyles.DoubleBuffer, true); + SetStyle(ControlStyles.OptimizedDoubleBuffer, true); + SetStyle(ControlStyles.ResizeRedraw, true); + SetStyle(ControlStyles.UserPaint, true); + base.DoubleBuffered = true; + base.BackColor = Color.FromArgb(93, 95, 94); + + _usedBars = new bool[(int) track.Score.MasterBars.Count]; + for (var s = 0; s < track.Staves.Count; s++) + { + var staff = track.Staves[s]; + for (var barI = 0; barI < staff.Bars.Count; barI++) + { + var bar = staff.Bars[barI]; + _usedBars[barI] = false; + + for (var voiceI = 0; voiceI < bar.Voices.Count && (!_usedBars[barI]); voiceI++) + { + var voice = bar.Voices[voiceI]; + for (var i = 0; i < voice.Beats.Count; i++) + { + var b = voice.Beats[i]; + if (!b.IsRest) + { + _usedBars[barI] = true; + } + } + } + } + } + + PerformLayout(); + Width = BlockSize.Width * _usedBars.Length; + Height = BlockSize.Height; + MinimumSize = BlockSize; + + SetColor(track.Color); + } + + private void SetColor(Model.Color color) + { + var baseColor = Color.FromArgb((byte) color.R, (byte) color.G, (byte) color.B); + double h, s, l; + ColorTools.RGB2HSL(baseColor, out h, out s, out l); + + _startColor = ColorTools.HSL2RGB(h, System.Math.Max(0, System.Math.Min(1, s - 0.2)), + System.Math.Max(0, System.Math.Min(1, l + 0.2))); + _endColor = ColorTools.HSL2RGB(h, System.Math.Max(0, System.Math.Min(1, s - 0.2)), + System.Math.Max(0, System.Math.Min(1, l - 0.2))); + } + + protected override void OnPaint(PaintEventArgs e) + { + base.OnPaint(e); + if (_usedBars == null) + { + return; + } + + using (var brush = new LinearGradientBrush(DisplayRectangle, _startColor, _endColor, + LinearGradientMode.Vertical)) + { + e.Graphics.FillRectangle(brush, + new Rectangle(0, 0, _usedBars.Length * BlockSize.Width, BlockSize.Height)); + } + + using (var pen = new Pen(Color.FromArgb(75, 255, 255, 255))) + { + for (var i = 0; i < _usedBars.Length; i++) + { + e.Graphics.DrawLine(pen, (i + 1) * BlockSize.Width, 0, + (i + 1) * BlockSize.Width, BlockSize.Height); + } + + pen.Color = Color.FromArgb(51, 51, 51); + e.Graphics.DrawLine(pen, 0, Height - 1, _usedBars.Length * BlockSize.Width, + Height - 1); + } + } + } +} diff --git a/Samples/CSharp/AlphaTab.Samples.WinForms/TrackDetailsControl.Designer.cs b/src.csharp/Samples/AlphaTab.Samples.WinForms/TrackDetailsControl.Designer.cs similarity index 92% rename from Samples/CSharp/AlphaTab.Samples.WinForms/TrackDetailsControl.Designer.cs rename to src.csharp/Samples/AlphaTab.Samples.WinForms/TrackDetailsControl.Designer.cs index 0c5cf75ed..758d322db 100644 --- a/Samples/CSharp/AlphaTab.Samples.WinForms/TrackDetailsControl.Designer.cs +++ b/src.csharp/Samples/AlphaTab.Samples.WinForms/TrackDetailsControl.Designer.cs @@ -1,21 +1,4 @@ -/* - * This file is part of alphaTab. - * Copyright © 2018, Daniel Kuschny and Contributors, All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or at your option any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. - */ -namespace AlphaTab.Samples.WinForms +namespace AlphaTab.Samples.WinForms { partial class TrackDetailsControl { diff --git a/src.csharp/Samples/AlphaTab.Samples.WinForms/TrackDetailsControl.cs b/src.csharp/Samples/AlphaTab.Samples.WinForms/TrackDetailsControl.cs new file mode 100644 index 000000000..674984286 --- /dev/null +++ b/src.csharp/Samples/AlphaTab.Samples.WinForms/TrackDetailsControl.cs @@ -0,0 +1,86 @@ +using System; +using System.Windows.Forms; +using AlphaTab.Model; +using Color = System.Drawing.Color; + +namespace AlphaTab.Samples.WinForms +{ + public partial class TrackDetailsControl : UserControl + { + private bool _isSelected; + private Track _track; + + public bool IsSelected + { + get => _isSelected; + set + { + _isSelected = value; + BackColor = value ? Color.FromArgb(116, 118, 117) : Color.FromArgb(93, 94, 95); + } + } + + public Track Track + { + get => _track; + set + { + _track = value; + TrackName = value.Name; + Volume = (int)value.PlaybackInfo.Volume; + IsSolo = value.PlaybackInfo.IsSolo; + IsMute = value.PlaybackInfo.IsMute; + } + } + + public string TrackName + { + get => lblName.Text; + set => lblName.Text = value; + } + + public int Volume + { + get => volumeTrack.Value; + set => volumeTrack.Value = value; + } + + public bool IsSolo + { + get => isSoloCheck.Checked; + set => isSoloCheck.Checked = value; + } + + public bool IsMute + { + get => isMuteCheck.Checked; + set => isMuteCheck.Checked = value; + } + + public TrackDetailsControl() + { + InitializeComponent(); + } + + public TrackDetailsControl(Track track) + { + InitializeComponent(); + Track = track; + } + + public event EventHandler Selected; + protected virtual void OnSelected() + { + var handler = Selected; + if (handler != null) + { + handler(this, EventArgs.Empty); + } + } + + private void lblName_Click(object sender, EventArgs e) + { + OnSelected(); + } + } +} diff --git a/Samples/CSharp/AlphaTab.Samples.WinForms/TrackDetailsControl.resx b/src.csharp/Samples/AlphaTab.Samples.WinForms/TrackDetailsControl.resx similarity index 100% rename from Samples/CSharp/AlphaTab.Samples.WinForms/TrackDetailsControl.resx rename to src.csharp/Samples/AlphaTab.Samples.WinForms/TrackDetailsControl.resx diff --git a/Samples/CSharp/AlphaTab.Samples.WinForms/TrackHeaderControl.Designer.cs b/src.csharp/Samples/AlphaTab.Samples.WinForms/TrackHeaderControl.Designer.cs similarity index 100% rename from Samples/CSharp/AlphaTab.Samples.WinForms/TrackHeaderControl.Designer.cs rename to src.csharp/Samples/AlphaTab.Samples.WinForms/TrackHeaderControl.Designer.cs diff --git a/src.csharp/Samples/AlphaTab.Samples.WinForms/TrackHeaderControl.cs b/src.csharp/Samples/AlphaTab.Samples.WinForms/TrackHeaderControl.cs new file mode 100644 index 000000000..ef8ceca8b --- /dev/null +++ b/src.csharp/Samples/AlphaTab.Samples.WinForms/TrackHeaderControl.cs @@ -0,0 +1,12 @@ +using System.Windows.Forms; + +namespace AlphaTab.Samples.WinForms +{ + public partial class TrackHeaderControl : UserControl + { + public TrackHeaderControl() + { + InitializeComponent(); + } + } +} diff --git a/Samples/CSharp/AlphaTab.Samples.WinForms/TrackHeaderControl.resx b/src.csharp/Samples/AlphaTab.Samples.WinForms/TrackHeaderControl.resx similarity index 100% rename from Samples/CSharp/AlphaTab.Samples.WinForms/TrackHeaderControl.resx rename to src.csharp/Samples/AlphaTab.Samples.WinForms/TrackHeaderControl.resx diff --git a/src.csharp/Samples/AlphaTab.Samples.Wpf/AlphaTab.Samples.Wpf.csproj b/src.csharp/Samples/AlphaTab.Samples.Wpf/AlphaTab.Samples.Wpf.csproj new file mode 100644 index 000000000..aa70e6e91 --- /dev/null +++ b/src.csharp/Samples/AlphaTab.Samples.Wpf/AlphaTab.Samples.Wpf.csproj @@ -0,0 +1,28 @@ + + + netcoreapp3.1 + true + WinExe + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src.csharp/Samples/AlphaTab.Samples.Wpf/App.xaml b/src.csharp/Samples/AlphaTab.Samples.Wpf/App.xaml new file mode 100644 index 000000000..85e9fd4d0 --- /dev/null +++ b/src.csharp/Samples/AlphaTab.Samples.Wpf/App.xaml @@ -0,0 +1,21 @@ + + + + + + + + + + + + diff --git a/src.csharp/Samples/AlphaTab.Samples.Wpf/App.xaml.cs b/src.csharp/Samples/AlphaTab.Samples.Wpf/App.xaml.cs new file mode 100644 index 000000000..2a75a1c26 --- /dev/null +++ b/src.csharp/Samples/AlphaTab.Samples.Wpf/App.xaml.cs @@ -0,0 +1,29 @@ +using System.Globalization; +using System.Threading; +using System.Windows; +using System.Windows.Markup; +using AlphaTab.Samples.Wpf.ViewModel; + +namespace AlphaTab.Samples.Wpf +{ + /// + /// Interaction logic for App.xaml + /// + public partial class App + { + public static void InitializeCultures() + { + Thread.CurrentThread.CurrentCulture = new CultureInfo("en-us"); + Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-us"); + + FrameworkElement.LanguageProperty.OverrideMetadata(typeof(FrameworkElement), new FrameworkPropertyMetadata( + XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag))); + } + + protected override void OnStartup(StartupEventArgs e) + { + base.OnStartup(e); + ViewModelBase.Initialize(); + } + } +} diff --git a/src.csharp/Samples/AlphaTab.Samples.Wpf/Controls/ToolBarCustom.cs b/src.csharp/Samples/AlphaTab.Samples.Wpf/Controls/ToolBarCustom.cs new file mode 100644 index 000000000..f38e891c8 --- /dev/null +++ b/src.csharp/Samples/AlphaTab.Samples.Wpf/Controls/ToolBarCustom.cs @@ -0,0 +1,25 @@ +using System.Windows; +using System.Windows.Controls; + +namespace AlphaTab.Samples.Wpf.Controls +{ + /// + /// A custom toolbar with hidden overflow button + /// + public class ToolBarCustom : ToolBar + { + public ToolBarCustom() + { + Loaded += OnLoaded; + } + + private void OnLoaded(object sender, RoutedEventArgs e) + { + var overflowGrid = Template.FindName("OverflowGrid", this) as FrameworkElement; + if (overflowGrid != null) + { + overflowGrid.Visibility = Visibility.Collapsed; + } + } + } +} diff --git a/src.csharp/Samples/AlphaTab.Samples.Wpf/Converter/BoolToBrushConverter.cs b/src.csharp/Samples/AlphaTab.Samples.Wpf/Converter/BoolToBrushConverter.cs new file mode 100644 index 000000000..610a5f975 --- /dev/null +++ b/src.csharp/Samples/AlphaTab.Samples.Wpf/Converter/BoolToBrushConverter.cs @@ -0,0 +1,26 @@ +using System; +using System.Globalization; +using System.Windows.Data; +using System.Windows.Media; + +namespace AlphaTab.Samples.Wpf.Converter +{ + /// + /// A simple ValueConverter which converts a bool to a associated brush. + /// + public class BoolToBrushConverter : IValueConverter + { + public Brush TrueBrush { get; set; } + public Brush FalseBrush { get; set; } + + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + return value is bool && (bool) value ? TrueBrush : FalseBrush; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/src.csharp/Samples/AlphaTab.Samples.Wpf/Converter/PlayerStateToImageSourceConverter.cs b/src.csharp/Samples/AlphaTab.Samples.Wpf/Converter/PlayerStateToImageSourceConverter.cs new file mode 100644 index 000000000..0f9b7fb0a --- /dev/null +++ b/src.csharp/Samples/AlphaTab.Samples.Wpf/Converter/PlayerStateToImageSourceConverter.cs @@ -0,0 +1,36 @@ +using System; +using System.Globalization; +using System.Windows.Data; +using System.Windows.Media; +using AlphaTab.Synth; + +namespace AlphaTab.Samples.Wpf.Converter +{ + internal class PlayerStateToImageSourceConverter : IValueConverter + { + public ImageSource PlayImage { get; set; } + public ImageSource PauseImage { get; set; } + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is PlayerState state) + { + switch (state) + { + case PlayerState.Paused: + return PlayImage; + case PlayerState.Playing: + return PauseImage; + default: + throw new ArgumentOutOfRangeException(); + } + } + + return null; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/src.csharp/Samples/AlphaTab.Samples.Wpf/Data/DialogService.cs b/src.csharp/Samples/AlphaTab.Samples.Wpf/Data/DialogService.cs new file mode 100644 index 000000000..5320b2648 --- /dev/null +++ b/src.csharp/Samples/AlphaTab.Samples.Wpf/Data/DialogService.cs @@ -0,0 +1,34 @@ +using System.Windows; +using AlphaTab.Model; +using AlphaTab.Samples.Wpf.ViewModel; +using Microsoft.Win32; + +namespace AlphaTab.Samples.Wpf.Data +{ + /// + /// This DialogService implementation opens uses WPF dialogs + /// + public class DialogService : IDialogService + { + public string OpenFile() + { + var dialog = new OpenFileDialog + { + Filter = "Supported Files (*.gp3, *.gp4, *.gp5, *.gpx, *.gp)|*.gp3;*.gp4;*.gp5;*.gpx;*.gp" + }; + if (dialog.ShowDialog(Application.Current.MainWindow).GetValueOrDefault()) + { + return dialog.FileName; + } + return null; + } + + public void ShowScoreInfo(Score score) + { + var viewModel = new ScoreInfoViewModel(score); + var window = new ScoreInfoWindow(); + window.DataContext = viewModel; + window.ShowDialog(); + } + } +} diff --git a/src.csharp/Samples/AlphaTab.Samples.Wpf/Data/ErrorService.cs b/src.csharp/Samples/AlphaTab.Samples.Wpf/Data/ErrorService.cs new file mode 100644 index 000000000..f0fbb6946 --- /dev/null +++ b/src.csharp/Samples/AlphaTab.Samples.Wpf/Data/ErrorService.cs @@ -0,0 +1,21 @@ +using System; +using System.Windows; +using AlphaTab.Samples.Wpf.ViewModel; + +namespace AlphaTab.Samples.Wpf.Data +{ + /// + /// This error service implementation shows errors as messageboxes on the main window + /// + public class ErrorService : IErrorService + { + public void OpenFailed(Exception e) + { + ViewModelBase.InvokeOnUi(() => + { + MessageBox.Show(Application.Current.MainWindow, e.Message, "An error during opening the file occured", + MessageBoxButton.OK, MessageBoxImage.Error); + }); + } + } +} \ No newline at end of file diff --git a/src.csharp/Samples/AlphaTab.Samples.Wpf/Data/IDialogService.cs b/src.csharp/Samples/AlphaTab.Samples.Wpf/Data/IDialogService.cs new file mode 100644 index 000000000..98b1a9ca3 --- /dev/null +++ b/src.csharp/Samples/AlphaTab.Samples.Wpf/Data/IDialogService.cs @@ -0,0 +1,19 @@ +using AlphaTab.Model; + +namespace AlphaTab.Samples.Wpf.Data +{ + public interface IDialogService + { + /// + /// Called when the application wants to open a new file. + /// Implementation should ask the user to select a file from the filesystem. + /// + /// The path to the file which should be opened or null if no file should be opened + string OpenFile(); + + /// + /// Called when the application wants to show a score information. + /// + void ShowScoreInfo(Score score); + } +} diff --git a/src.csharp/Samples/AlphaTab.Samples.Wpf/Data/IErrorService.cs b/src.csharp/Samples/AlphaTab.Samples.Wpf/Data/IErrorService.cs new file mode 100644 index 000000000..4dfe10bab --- /dev/null +++ b/src.csharp/Samples/AlphaTab.Samples.Wpf/Data/IErrorService.cs @@ -0,0 +1,16 @@ +using System; + +namespace AlphaTab.Samples.Wpf.Data +{ + /// + /// Implement this interface to handle application errors. + /// + public interface IErrorService + { + /// + /// Called when an error during opening a file occurs. + /// + /// The exception occured during opening the file + void OpenFailed(Exception e); + } +} diff --git a/src.csharp/Samples/AlphaTab.Samples.Wpf/Images/32/control_pause.png b/src.csharp/Samples/AlphaTab.Samples.Wpf/Images/32/control_pause.png new file mode 100644 index 000000000..186f361f0 Binary files /dev/null and b/src.csharp/Samples/AlphaTab.Samples.Wpf/Images/32/control_pause.png differ diff --git a/src.csharp/Samples/AlphaTab.Samples.Wpf/Images/32/control_play.png b/src.csharp/Samples/AlphaTab.Samples.Wpf/Images/32/control_play.png new file mode 100644 index 000000000..3b64790b9 Binary files /dev/null and b/src.csharp/Samples/AlphaTab.Samples.Wpf/Images/32/control_play.png differ diff --git a/Samples/CSharp/AlphaTab.Samples.Wpf/Images/32/folder_page_white.png b/src.csharp/Samples/AlphaTab.Samples.Wpf/Images/32/folder_page_white.png similarity index 100% rename from Samples/CSharp/AlphaTab.Samples.Wpf/Images/32/folder_page_white.png rename to src.csharp/Samples/AlphaTab.Samples.Wpf/Images/32/folder_page_white.png diff --git a/Samples/CSharp/AlphaTab.Samples.Wpf/Images/32/information.png b/src.csharp/Samples/AlphaTab.Samples.Wpf/Images/32/information.png similarity index 100% rename from Samples/CSharp/AlphaTab.Samples.Wpf/Images/32/information.png rename to src.csharp/Samples/AlphaTab.Samples.Wpf/Images/32/information.png diff --git a/Samples/CSharp/AlphaTab.Samples.Wpf/MainWindow.xaml b/src.csharp/Samples/AlphaTab.Samples.Wpf/MainWindow.xaml similarity index 86% rename from Samples/CSharp/AlphaTab.Samples.Wpf/MainWindow.xaml rename to src.csharp/Samples/AlphaTab.Samples.Wpf/MainWindow.xaml index c6583febc..ffc787eca 100644 --- a/Samples/CSharp/AlphaTab.Samples.Wpf/MainWindow.xaml +++ b/src.csharp/Samples/AlphaTab.Samples.Wpf/MainWindow.xaml @@ -1,10 +1,16 @@  + + + @@ -21,28 +27,20 @@ - + - - - - - - - - + + + @@ -108,7 +106,7 @@ - + @@ -118,7 +116,7 @@ - + @@ -127,14 +125,14 @@ - + - + @@ -146,7 +144,7 @@ - + diff --git a/src.csharp/Samples/AlphaTab.Samples.Wpf/MainWindow.xaml.cs b/src.csharp/Samples/AlphaTab.Samples.Wpf/MainWindow.xaml.cs new file mode 100644 index 000000000..c3371659e --- /dev/null +++ b/src.csharp/Samples/AlphaTab.Samples.Wpf/MainWindow.xaml.cs @@ -0,0 +1,31 @@ +using System.Windows; +using AlphaTab.Samples.Wpf.Data; +using AlphaTab.Samples.Wpf.ViewModel; + +namespace AlphaTab.Samples.Wpf +{ + /// + /// Interaction logic for MainWindow.xaml + /// + public partial class MainWindow + { + public MainWindow() + { + InitializeComponent(); + + // ensure the UI is using en-us as culture + // we need this culture for correct WPF path string generation + // in a German culture it could happen that we have a , as a decimal separator. + App.InitializeCultures(); + + // create a our viewmodel for databinding + var viewModel = new MainViewModel(new DialogService(), new ErrorService()); + DataContext = viewModel; + } + + private void OnPlayPauseClick(object sender, RoutedEventArgs e) + { + TablatureControl.Api.PlayPause(); + } + } +} diff --git a/Samples/CSharp/AlphaTab.Samples.Wpf/Properties/Resources.Designer.cs b/src.csharp/Samples/AlphaTab.Samples.Wpf/Properties/Resources.Designer.cs similarity index 100% rename from Samples/CSharp/AlphaTab.Samples.Wpf/Properties/Resources.Designer.cs rename to src.csharp/Samples/AlphaTab.Samples.Wpf/Properties/Resources.Designer.cs diff --git a/Samples/CSharp/AlphaTab.Samples.Wpf/Properties/Resources.resx b/src.csharp/Samples/AlphaTab.Samples.Wpf/Properties/Resources.resx similarity index 100% rename from Samples/CSharp/AlphaTab.Samples.Wpf/Properties/Resources.resx rename to src.csharp/Samples/AlphaTab.Samples.Wpf/Properties/Resources.resx diff --git a/Samples/CSharp/AlphaTab.Samples.Wpf/Properties/Settings.Designer.cs b/src.csharp/Samples/AlphaTab.Samples.Wpf/Properties/Settings.Designer.cs similarity index 100% rename from Samples/CSharp/AlphaTab.Samples.Wpf/Properties/Settings.Designer.cs rename to src.csharp/Samples/AlphaTab.Samples.Wpf/Properties/Settings.Designer.cs diff --git a/Samples/CSharp/AlphaTab.Samples.Wpf/Properties/Settings.settings b/src.csharp/Samples/AlphaTab.Samples.Wpf/Properties/Settings.settings similarity index 100% rename from Samples/CSharp/AlphaTab.Samples.Wpf/Properties/Settings.settings rename to src.csharp/Samples/AlphaTab.Samples.Wpf/Properties/Settings.settings diff --git a/Samples/CSharp/AlphaTab.Samples.Wpf/Readme.md b/src.csharp/Samples/AlphaTab.Samples.Wpf/Readme.md similarity index 100% rename from Samples/CSharp/AlphaTab.Samples.Wpf/Readme.md rename to src.csharp/Samples/AlphaTab.Samples.Wpf/Readme.md diff --git a/Samples/CSharp/AlphaTab.Samples.Wpf/ScoreInfoWindow.xaml b/src.csharp/Samples/AlphaTab.Samples.Wpf/ScoreInfoWindow.xaml similarity index 100% rename from Samples/CSharp/AlphaTab.Samples.Wpf/ScoreInfoWindow.xaml rename to src.csharp/Samples/AlphaTab.Samples.Wpf/ScoreInfoWindow.xaml diff --git a/src.csharp/Samples/AlphaTab.Samples.Wpf/ScoreInfoWindow.xaml.cs b/src.csharp/Samples/AlphaTab.Samples.Wpf/ScoreInfoWindow.xaml.cs new file mode 100644 index 000000000..2abd95f45 --- /dev/null +++ b/src.csharp/Samples/AlphaTab.Samples.Wpf/ScoreInfoWindow.xaml.cs @@ -0,0 +1,32 @@ +using System.Windows; +using System.Windows.Input; + +namespace AlphaTab.Samples.Wpf +{ + /// + /// Interaction logic for ScoreInfoWindow.xaml + /// + public partial class ScoreInfoWindow + { + public ScoreInfoWindow() + { + InitializeComponent(); + } + + private void OnOkClick(object sender, RoutedEventArgs e) + { + DialogResult = true; + } + + protected override void OnPreviewKeyDown(KeyEventArgs e) + { + base.OnPreviewKeyDown(e); + if (e.Key == Key.Escape) + { + DialogResult = false; + e.Handled = true; + } + } + + } +} diff --git a/src.csharp/Samples/AlphaTab.Samples.Wpf/Utils/RelayCommand.cs b/src.csharp/Samples/AlphaTab.Samples.Wpf/Utils/RelayCommand.cs new file mode 100644 index 000000000..4fce26ab0 --- /dev/null +++ b/src.csharp/Samples/AlphaTab.Samples.Wpf/Utils/RelayCommand.cs @@ -0,0 +1,44 @@ +using System; +using System.Windows.Input; +using AlphaTab.Samples.Wpf.ViewModel; + +namespace AlphaTab.Samples.Wpf.Utils +{ + /// + /// This command implementation delegates the execution to a referenced method. + /// + public class RelayCommand : ICommand + { + private readonly Action _action; + private readonly Func _canExecute; + + public RelayCommand(Action action, Func canExecute = null) + { + _action = action; + _canExecute = canExecute; + } + + public bool CanExecute(object parameter) + { + return _canExecute == null || _canExecute(); + } + + public void Execute(object parameter) + { + _action(); + } + + public event EventHandler CanExecuteChanged; + public virtual void RaiseCanExecuteChanged() + { + var handler = CanExecuteChanged; + if (handler != null) + { + ViewModelBase.InvokeOnUi(() => + { + handler(this, EventArgs.Empty); + }); + } + } + } +} diff --git a/src.csharp/Samples/AlphaTab.Samples.Wpf/ViewModel/MainViewModel.cs b/src.csharp/Samples/AlphaTab.Samples.Wpf/ViewModel/MainViewModel.cs new file mode 100644 index 000000000..0a31121c8 --- /dev/null +++ b/src.csharp/Samples/AlphaTab.Samples.Wpf/ViewModel/MainViewModel.cs @@ -0,0 +1,222 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using System.Windows.Input; +using AlphaTab.Importer; +using AlphaTab.Model; +using AlphaTab.Samples.Wpf.Data; +using AlphaTab.Samples.Wpf.Utils; + +namespace AlphaTab.Samples.Wpf.ViewModel +{ + /// + /// This viewmodel contains the data and logic for the main application window. + /// + public class MainViewModel : ViewModelBase + { + // references to the services we want to use + private readonly IDialogService _dialogService; + private readonly IErrorService _errorService; + + #region Score Data + + // those properties store the score information + + private Score _score; + private int _currentTrackIndex; + private IEnumerable _trackInfos; + private TrackViewModel _selectedTrackInfo; + private readonly RelayCommand _showScoreInfoCommand; + + /// + /// A command which raises the method + /// + public ICommand ShowScoreInfoCommand => _showScoreInfoCommand; + + /// + /// Gets or sets the currently opened score. + /// If a new score is selected, the first track gets loaded. + /// + public Score Score + { + get => _score; + set + { + _score = value; + OnPropertyChanged(); + OnPropertyChanged("ScoreTitle"); + _showScoreInfoCommand.RaiseCanExecuteChanged(); + + // select the first track + CurrentTrackIndex = 0; + } + } + + /// + /// Gets or sets the title of the currently loaded sore. + /// + public string ScoreTitle => _score == null ? "No File Opened" : _score.Title; + + /// + /// Gets or sets the index of the track which should be currently displayed. + /// + public int CurrentTrackIndex + { + get => _currentTrackIndex; + set + { + _currentTrackIndex = value; + + // update the visual track selection if a new track is selected + UpdateSelectedViewModel(); + + // notify the ui + OnPropertyChanged(); + OnPropertyChanged(nameof(CurrentTracks)); + } + } + + /// + /// Gets the currently selected track. + /// + public IEnumerable CurrentTracks + { + get + { + if (Score == null || CurrentTrackIndex < 0 || CurrentTrackIndex >= _score.Tracks.Count) + { + return null; + } + + return new[] { _score.Tracks[_currentTrackIndex] }; + } + } + + /// + /// Gets the information about the currently loaded tracks + /// + public IEnumerable TrackInfos + { + get => _trackInfos; + private set + { + _trackInfos = value; + + // ensure correct selection + UpdateSelectedViewModel(); + OnPropertyChanged(); + } + } + + /// + /// Gets or sets the currently selected track. + /// + public TrackViewModel SelectedTrackInfo + { + get => _selectedTrackInfo; + set + { + _selectedTrackInfo = value; + // select the new track + if (_score != null) + { + CurrentTrackIndex = _score.Tracks.IndexOf(_selectedTrackInfo.Track); + } + OnPropertyChanged(); + } + } + + /// + /// Opens a score info dialog for the current score. + /// + public void ShowScoreInfo() + { + if (_score != null) + { + _dialogService.ShowScoreInfo(_score); + } + } + + + + #endregion + + #region Score Loading + + /// + /// A command which raises a file opening + /// + public ICommand OpenFileCommand { get; private set; } + + /// + /// Opens a new file by loading the file path using the IO service. + /// + public void OpenFile() + { + OpenFile(_dialogService.OpenFile()); + } + + /// + /// Opens a new file from the specified file path. + /// + /// the path to the file to load + private void OpenFile(string file) + { + if (!string.IsNullOrWhiteSpace(file) && File.Exists(file)) + { + InternalOpenFile(file); + } + } + + private void InternalOpenFile(string file) + { + Task.Factory.StartNew(() => + { + try + { + // load the score from the filesystem + Score = ScoreLoader.LoadScoreFromBytes(File.ReadAllBytes(file)); + + // build the track info objects for the ui + var trackInfos = new TrackViewModel[(int)Score.Tracks.Count]; + for (var i = 0; i < trackInfos.Length; i++) + { + trackInfos[i] = new TrackViewModel(Score.Tracks[i]); + } + TrackInfos = trackInfos; + } + catch (Exception e) + { + _errorService.OpenFailed(e); + } + }); + } + + /// + /// Updates the currently selected viewmodel + /// + private void UpdateSelectedViewModel() + { + if (_trackInfos != null) + { + var currentTrackLookup = CurrentTracks.ToLookup(t => t.Index); + foreach (var trackViewModel in _trackInfos) + { + trackViewModel.IsSelected = currentTrackLookup.Contains(trackViewModel.Track.Index); + } + } + } + + #endregion + + public MainViewModel(IDialogService dialogService, IErrorService errorService) + { + _dialogService = dialogService; + _errorService = errorService; + OpenFileCommand = new RelayCommand(OpenFile); + _showScoreInfoCommand = new RelayCommand(ShowScoreInfo, () => _score != null); + } + } +} diff --git a/src.csharp/Samples/AlphaTab.Samples.Wpf/ViewModel/ScoreInfoViewModel.cs b/src.csharp/Samples/AlphaTab.Samples.Wpf/ViewModel/ScoreInfoViewModel.cs new file mode 100644 index 000000000..9dd8211e8 --- /dev/null +++ b/src.csharp/Samples/AlphaTab.Samples.Wpf/ViewModel/ScoreInfoViewModel.cs @@ -0,0 +1,132 @@ +using AlphaTab.Model; + +namespace AlphaTab.Samples.Wpf.ViewModel +{ + public class ScoreInfoViewModel : ViewModelBase + { + private string _tab; + private string _words; + private string _title; + private string _subTitle; + private string _notices; + private string _music; + private string _instructions; + private string _copyright; + private string _artist; + private string _album; + + public ScoreInfoViewModel(Score score) + { + _tab = score.Tab; + _words = score.Words; + _title = score.Title; + _subTitle = score.SubTitle; + _notices = score.Notices; + _music = score.Music; + _instructions = score.Instructions; + _copyright = score.Copyright; + _artist = score.Artist; + _album = score.Album; + } + + public string Album + { + get => _album; + set + { + _album = value; + OnPropertyChanged(); + } + } + + public string Artist + { + get => _artist; + set + { + _artist = value; + OnPropertyChanged(); + } + } + + public string Copyright + { + get => _copyright; + set + { + _copyright = value; + OnPropertyChanged(); + } + } + + public string Instructions + { + get => _instructions; + set + { + _instructions = value; + OnPropertyChanged(); + } + } + + public string Music + { + get => _music; + set + { + _music = value; + OnPropertyChanged(); + } + } + + public string Notices + { + get => _notices; + set + { + _notices = value; + OnPropertyChanged(); + } + } + + public string SubTitle + { + get => _subTitle; + set + { + _subTitle = value; + OnPropertyChanged(); + } + } + + public string Title + { + get => _title; + set + { + _title = value; + OnPropertyChanged(); + } + } + + public string Words + { + get => _words; + set + { + _words = value; + OnPropertyChanged(); + } + } + + public string Tab + { + get => _tab; + set + { + _tab = value; + OnPropertyChanged(); + } + } + } +} diff --git a/src.csharp/Samples/AlphaTab.Samples.Wpf/ViewModel/TrackViewModel.cs b/src.csharp/Samples/AlphaTab.Samples.Wpf/ViewModel/TrackViewModel.cs new file mode 100644 index 000000000..166b5fec6 --- /dev/null +++ b/src.csharp/Samples/AlphaTab.Samples.Wpf/ViewModel/TrackViewModel.cs @@ -0,0 +1,157 @@ +using System.Linq; +using AlphaTab.Model; + +namespace AlphaTab.Samples.Wpf.ViewModel +{ + /// + /// A viewmodel for displaying track information in the UI + /// + public class TrackViewModel : ViewModelBase + { + private TrackType _trackType; + private bool[] _usedBars; + private bool _isSelected; + private Track _track; + + public bool IsSelected + { + get => _isSelected; + set + { + _isSelected = value; + OnPropertyChanged(); + } + } + + public TrackType TrackType + { + get => _trackType; + set + { + _trackType = value; + OnPropertyChanged(); + } + } + + public string Name + { + get => _track.Name; + set + { + _track.Name = value; + OnPropertyChanged(); + } + } + + public int Volume + { + get => (int)_track.PlaybackInfo.Volume; + set + { + _track.PlaybackInfo.Volume = value; + OnPropertyChanged(); + } + } + + public bool IsSolo + { + get => _track.PlaybackInfo.IsSolo; + set + { + _track.PlaybackInfo.IsSolo = value; + OnPropertyChanged(); + } + } + + public bool IsMute + { + get => _track.PlaybackInfo.IsMute; + set + { + _track.PlaybackInfo.IsMute = value; + OnPropertyChanged(); + } + } + + public bool[] UsedBars + { + get => _usedBars; + set + { + _usedBars = value; + OnPropertyChanged(); + } + } + + public Track Track + { + get => _track; + set + { + _track = value; + OnPropertyChanged(); + } + } + + public TrackViewModel(Track track) + { + _track = track; + + // general midi Programs + if (track.Staves.Any(s=>s.IsPercussion)) + { + TrackType = TrackType.Drums; + } + else if (track.PlaybackInfo.Program >= 0 && track.PlaybackInfo.Program <= 6) + { + TrackType = TrackType.Piano; + } + else if (track.PlaybackInfo.Program >= 26 && track.PlaybackInfo.Program <= 31) + { + TrackType = TrackType.ElectricGuitar; + } + else if (track.PlaybackInfo.Program >= 32 && track.PlaybackInfo.Program <= 39) + { + TrackType = TrackType.BassGuitar; + } + else + { + TrackType = TrackType.Default; + } + + // scan all bars if they have any note + _usedBars = new bool[(int)track.Score.MasterBars.Count]; + for (var s = 0; s < track.Staves.Count; s++) + { + var staff = track.Staves[s]; + for (var barI = 0; barI < staff.Bars.Count; barI++) + { + var bar = staff.Bars[barI]; + _usedBars[barI] = false; + + for (var voiceI = 0; voiceI < bar.Voices.Count && (!_usedBars[barI]); voiceI++) + { + var voice = bar.Voices[voiceI]; + for (var i = 0; i < voice.Beats.Count; i++) + { + var b = voice.Beats[i]; + if (!b.IsRest) + { + _usedBars[barI] = true; + } + } + } + } + } + } + } + + public enum TrackType + { + Default, + ElectricGuitar, + BassGuitar, + Drums, + Piano + } +} diff --git a/Samples/CSharp/AlphaTab.Samples.Wpf/ViewModel/ViewModelBase.cs b/src.csharp/Samples/AlphaTab.Samples.Wpf/ViewModel/ViewModelBase.cs similarity index 100% rename from Samples/CSharp/AlphaTab.Samples.Wpf/ViewModel/ViewModelBase.cs rename to src.csharp/Samples/AlphaTab.Samples.Wpf/ViewModel/ViewModelBase.cs diff --git a/src/AlphaTabApiBase.ts b/src/AlphaTabApiBase.ts new file mode 100644 index 000000000..6ce7690c7 --- /dev/null +++ b/src/AlphaTabApiBase.ts @@ -0,0 +1,1157 @@ +import { AlphaSynthMidiFileHandler } from '@src/midi/AlphaSynthMidiFileHandler'; +import { MidiFileGenerator } from '@src/midi/MidiFileGenerator'; + +import { MidiFile } from '@src/midi/MidiFile'; +import { MidiTickLookup, MidiTickLookupFindBeatResult } from '@src/midi/MidiTickLookup'; +import { IAlphaSynth } from '@src/synth/IAlphaSynth'; +import { PlaybackRange } from '@src/synth/PlaybackRange'; +import { PlayerState } from '@src/synth/PlayerState'; +import { PlayerStateChangedEventArgs } from '@src/synth/PlayerStateChangedEventArgs'; +import { PositionChangedEventArgs } from '@src/synth/PositionChangedEventArgs'; +import { Environment } from '@src/Environment'; +import { EventEmitter, IEventEmitter, IEventEmitterOfT, EventEmitterOfT } from '@src/EventEmitter'; + +import { AlphaTexImporter } from '@src/importer/AlphaTexImporter'; + +import { ByteBuffer } from '@src/io/ByteBuffer'; +import { Beat } from '@src/model/Beat'; +import { Score } from '@src/model/Score'; +import { Track } from '@src/model/Track'; + +import { IContainer } from '@src/platform/IContainer'; +import { IMouseEventArgs } from '@src/platform/IMouseEventArgs'; +import { IUiFacade } from '@src/platform/IUiFacade'; +import { ScrollMode } from '@src/PlayerSettings'; +import { BeatContainerGlyph } from '@src/rendering/glyphs/BeatContainerGlyph'; + +import { IScoreRenderer } from '@src/rendering/IScoreRenderer'; + +import { RenderFinishedEventArgs } from '@src/rendering/RenderFinishedEventArgs'; +import { ScoreRenderer } from '@src/rendering/ScoreRenderer'; +import { BeatBounds } from '@src/rendering/utils/BeatBounds'; + +import { Bounds } from '@src/rendering/utils/Bounds'; +import { BoundsLookup } from '@src/rendering/utils/BoundsLookup'; +import { MasterBarBounds } from '@src/rendering/utils/MasterBarBounds'; +import { StaveGroupBounds } from '@src/rendering/utils/StaveGroupBounds'; +import { ResizeEventArgs } from '@src/ResizeEventArgs'; +import { Settings } from '@src/Settings'; + +import { Logger } from '@src/Logger'; +import { ModelUtils } from '@src/model/ModelUtils'; +import { AlphaTabError, AlphaTabErrorType } from '@src/AlphaTabError'; + +class SelectionInfo { + public beat: Beat; + public bounds: BeatBounds | null = null; + + public constructor(beat: Beat) { + this.beat = beat; + } +} + +/** + * This class represents the public API of alphaTab and provides all logic to display + * a music sheet in any UI using the given {@link IUiFacade} + * @param The UI object holding the settings. + * @csharp_public + */ +export class AlphaTabApiBase { + private _startTime: number = 0; + private _trackIndexes: number[] | null = null; + /** + * Gets the UI facade to use for interacting with the user interface. + */ + public readonly uiFacade: IUiFacade; + + /** + * Gets the UI container that holds the whole alphaTab control. + */ + public readonly container: IContainer; + + /** + * Gets the score renderer used for rendering the music sheet. This is the low-level API responsible for the actual rendering chain. + */ + public readonly renderer: IScoreRenderer; + + /** + * Gets the score holding all information about the song being rendered. + */ + public score: Score | null = null; + + /** + * Gets the settings that are used for rendering the music notation. + */ + public settings!: Settings; + + /** + * Gets a list of the tracks that are currently rendered; + */ + public tracks: Track[] = []; + + /** + * Gets the UI container that will hold all rendered results. + */ + public readonly canvasElement: IContainer; + + /** + * Initializes a new instance of the {@link AlphaTabApiBase} class. + * @param uiFacade The UI facade to use for interacting with the user interface. + * @param settings The UI settings object to use for loading the settings. + */ + public constructor(uiFacade: IUiFacade, settings: TSettings) { + this.uiFacade = uiFacade; + this.container = uiFacade.rootContainer; + + uiFacade.initialize(this, settings); + Logger.logLevel = this.settings.core.logLevel; + + this.canvasElement = uiFacade.createCanvasElement(); + this.container.appendChild(this.canvasElement); + this.container.resize.on( + Environment.throttle(() => { + if (this.container.width !== this.renderer.width) { + this.triggerResize(); + } + }, uiFacade.resizeThrottle) + ); + if ( + this.settings.core.useWorkers && + this.uiFacade.areWorkersSupported && + Environment.getRenderEngineFactory(this.settings).supportsWorkers + ) { + this.renderer = this.uiFacade.createWorkerRenderer(); + } else { + this.renderer = new ScoreRenderer(this.settings); + } + + let initialResizeEventInfo: ResizeEventArgs = new ResizeEventArgs(); + initialResizeEventInfo.oldWidth = this.renderer.width; + initialResizeEventInfo.newWidth = this.container.width | 0; + initialResizeEventInfo.settings = this.settings; + this.onResize(initialResizeEventInfo); + this.renderer.preRender.on(this.onRenderStarted.bind(this)); + this.renderer.renderFinished.on(renderingResult => { + this.onRenderFinished(renderingResult); + }); + this.renderer.postRenderFinished.on(() => { + let duration: number = Date.now() - this._startTime; + Logger.debug('rendering', 'Rendering completed in ' + duration + 'ms'); + this.onPostRenderFinished(); + }); + this.renderer.preRender.on(_ => { + this._startTime = Date.now(); + }); + this.renderer.partialRenderFinished.on(this.appendRenderResult.bind(this)); + this.renderer.renderFinished.on(r => { + this.appendRenderResult(r); + this.appendRenderResult(null); // marks last element + }); + this.renderer.error.on(this.onError.bind(this)); + if (this.settings.player.enablePlayer) { + this.setupPlayer(); + } + this.setupClickHandling(); + // delay rendering to allow ui to hook up with events first. + this.uiFacade.beginInvoke(() => { + this.uiFacade.initialRender(); + }); + } + + /** + * Destroys the alphaTab control and restores the initial state of the UI. + */ + public destroy(): void { + if (this.player) { + this.player.destroy(); + } + this.uiFacade.destroy(); + this.renderer.destroy(); + } + + /** + * Applies any changes that were done to the settings object and informs the {@link renderer} about any new values to consider. + */ + public updateSettings(): void { + this.renderer.updateSettings(this.settings); + // enable/disable player if needed + if (this.settings.player.enablePlayer) { + this.setupPlayer(); + } else { + this.destroyPlayer(); + } + } + + /** + * Attempts a load of the score represented by the given data object. + * @param scoreData The data container supported by {@link IUiFacade} + * @param trackIndexes The indexes of the tracks from the song that should be rendered. If not provided, the first track of the + * song will be shown. + * @returns true if the data object is supported and a load was initiated, otherwise false + */ + public load(scoreData: unknown, trackIndexes?: number[]): boolean { + try { + return this.uiFacade.load( + scoreData, + score => { + this.renderScore(score, trackIndexes); + }, + error => { + this.onError(error); + } + ); + } catch (e) { + this.onError(e); + return false; + } + } + + /** + * Initiates a rendering of the given score. + * @param score The score containing the tracks to be rendered. + * @param trackIndexes The indexes of the tracks from the song that should be rendered. If not provided, the first track of the + * song will be shown. + */ + public renderScore(score: Score, trackIndexes?: number[]): void { + let tracks: Track[] = []; + if (!trackIndexes) { + if (score.tracks.length > 0) { + tracks.push(score.tracks[0]); + } + } else { + if (trackIndexes.length === 0) { + if (score.tracks.length > 0) { + tracks.push(score.tracks[0]); + } + } else if (trackIndexes.length === 1 && trackIndexes[0] === -1) { + for (let track of score.tracks) { + tracks.push(track); + } + } else { + for (let index of trackIndexes) { + if (index >= 0 && index <= score.tracks.length) { + tracks.push(score.tracks[index]); + } + } + } + } + this.internalRenderTracks(score, tracks); + } + + /** + * Renders the given list of tracks. + * @param tracks The tracks to render. They must all belong to the same score. + */ + public renderTracks(tracks: Track[]): void { + if (tracks.length > 0) { + let score: Score = tracks[0].score; + for (let track of tracks) { + if (track.score !== score) { + this.onError(new AlphaTabError(AlphaTabErrorType.General, 'All rendered tracks must belong to the same score.')); + return; + } + } + this.internalRenderTracks(score, tracks); + } + } + + private internalRenderTracks(score: Score, tracks: Track[]): void { + if (score !== this.score) { + ModelUtils.applyPitchOffsets(this.settings, score); + this.score = score; + this.tracks = tracks; + this._trackIndexes = []; + for (let track of tracks) { + this._trackIndexes.push(track.index); + } + this.onScoreLoaded(score); + this.loadMidiForScore(); + this.render(); + } else { + this.tracks = tracks; + this._trackIndexes = []; + for (let track of tracks) { + this._trackIndexes.push(track.index); + } + this.render(); + } + } + + private triggerResize(): void { + if (!this.container.isVisible) { + Logger.warning( + 'Rendering', + 'AlphaTab container was invisible while autosizing, waiting for element to become visible', + null + ); + this.uiFacade.rootContainerBecameVisible.on(() => { + Logger.debug('Rendering', 'AlphaTab container became visible, doing autosizing', null); + this.triggerResize(); + }); + } else { + let resizeEventInfo: ResizeEventArgs = new ResizeEventArgs(); + resizeEventInfo.oldWidth = this.renderer.width; + resizeEventInfo.newWidth = this.container.width; + resizeEventInfo.settings = this.settings; + this.onResize(resizeEventInfo); + this.renderer.updateSettings(this.settings); + this.renderer.width = this.container.width; + this.renderer.resizeRender(); + } + } + + private appendRenderResult(result: RenderFinishedEventArgs | null): void { + if (result) { + this.canvasElement.width = result.totalWidth; + this.canvasElement.height = result.totalHeight; + if (this._cursorWrapper) { + this._cursorWrapper.width = result.totalWidth; + this._cursorWrapper.height = result.totalHeight; + } + } + if (!result || result.renderResult) { + this.uiFacade.beginAppendRenderResults(result); + } + } + + /** + * Tells alphaTab to render the given alphaTex. + * @param tex The alphaTex code to render. + * @param tracks If set, the given tracks will be rendered, otherwise the first track only will be rendered. + */ + public tex(tex: string, tracks?: number[]): void { + try { + let parser: AlphaTexImporter = new AlphaTexImporter(); + let data: ByteBuffer = ByteBuffer.fromString(tex); + parser.init(data, this.settings); + let score: Score = parser.readScore(); + this.renderScore(score, tracks); + } catch (e) { + this.onError(e); + } + } + + /** + * Attempts a load of the score represented by the given data object. + * @param data The data object to decode + * @returns true if the data object is supported and a load was initiated, otherwise false + */ + public loadSoundFont(data: unknown): boolean { + if (!this.player) { + return false; + } + return this.uiFacade.loadSoundFont(data); + } + + /** + * Initiates a re-rendering of the current setup. If rendering is not yet possible, it will be deferred until the UI changes to be ready for rendering. + */ + public render(): void { + if (!this.renderer) { + return; + } + if (this.uiFacade.canRender) { + // when font is finally loaded, start rendering + this.renderer.width = this.container.width; + this.renderer.renderScore(this.score!, this._trackIndexes as any); + } else { + this.uiFacade.canRenderChanged.on(() => this.render()); + } + } + + private _tickCache: MidiTickLookup | null = null; + /** + * Gets the alphaSynth player used for playback. This is the low-level API to the Midi synthesizer used for playback. + */ + public player: IAlphaSynth | null = null; + + public get isReadyForPlayback(): boolean { + if (!this.player) { + return false; + } + return this.player.isReadyForPlayback; + } + + public get playerState(): PlayerState { + if (!this.player) { + return PlayerState.Paused; + } + return this.player.state; + } + + public get masterVolume(): number { + if (!this.player) { + return 0; + } + return this.player.masterVolume; + } + + public set masterVolume(value: number) { + if (this.player) { + this.player.masterVolume = value; + } + } + + public get metronomeVolume(): number { + if (!this.player) { + return 0; + } + return this.player.metronomeVolume; + } + + public set metronomeVolume(value: number) { + if (this.player) { + this.player.metronomeVolume = value; + } + } + + public get tickPosition(): number { + if (!this.player) { + return 0; + } + return this.player.tickPosition; + } + + public set tickPosition(value: number) { + if (this.player) { + this.player.tickPosition = value; + } + } + + public get timePosition(): number { + if (!this.player) { + return 0; + } + return this.player.timePosition; + } + + public set timePosition(value: number) { + if (this.player) { + this.player.timePosition = value; + } + } + + public get playbackRange(): PlaybackRange | null { + if (!this.player) { + return null; + } + return this.player.playbackRange; + } + + public set playbackRange(value: PlaybackRange | null) { + if (this.player) { + this.player.playbackRange = value; + } + } + + public get playbackSpeed(): number { + if (!this.player) { + return 0; + } + return this.player.playbackSpeed; + } + + public set playbackSpeed(value: number) { + if (this.player) { + this.player.playbackSpeed = value; + } + } + + public get isLooping(): boolean { + if (!this.player) { + return false; + } + return this.player.isLooping; + } + + public set isLooping(value: boolean) { + if (this.player) { + this.player.isLooping = value; + } + } + + private destroyPlayer(): void { + if (!this.player) { + return; + } + this.player.destroy(); + this.player = null; + this.destroyCursors(); + } + + private setupPlayer(): void { + if (this.player) { + return; + } + this.player = this.uiFacade.createWorkerPlayer(); + if (!this.player) { + return; + } + this.player.ready.on(() => { + this.loadMidiForScore(); + }); + this.player.readyForPlayback.on(() => { + this.onPlayerReady(); + if (this.tracks) { + for (let track of this.tracks) { + let volume: number = track.playbackInfo.volume / 16; + this.player!.setChannelVolume(track.playbackInfo.primaryChannel, volume); + this.player!.setChannelVolume(track.playbackInfo.secondaryChannel, volume); + } + } + }); + this.player.soundFontLoaded.on(this.onSoundFontLoaded.bind(this)); + this.player.soundFontLoadFailed.on(e => { + this.onError(e); + }); + this.player.midiLoaded.on(this.onMidiLoaded.bind(this)); + this.player.midiLoadFailed.on(e => { + this.onError(e); + }); + this.player.stateChanged.on(this.onPlayerStateChanged.bind(this)); + this.player.positionChanged.on(this.onPlayerPositionChanged.bind(this)); + this.player.finished.on(this.onPlayerFinished.bind(this)); + if (this.settings.player.enableCursor) { + this.setupCursors(); + } else { + this.destroyCursors(); + } + } + + private loadMidiForScore(): void { + if (!this.player || !this.score || !this.player.isReady) { + return; + } + Logger.debug('AlphaTab', 'Generating Midi'); + let midiFile: MidiFile = new MidiFile(); + let handler: AlphaSynthMidiFileHandler = new AlphaSynthMidiFileHandler(midiFile); + let generator: MidiFileGenerator = new MidiFileGenerator(this.score, this.settings, handler); + generator.generate(); + this._tickCache = generator.tickLookup; + this.player.loadMidiFile(midiFile); + } + + /** + * Changes the volume of the given tracks. + * @param tracks The tracks for which the volume should be changed. + * @param volume The volume to set for all tracks in percent (0-1) + */ + public changeTrackVolume(tracks: Track[], volume: number): void { + if (!this.player) { + return; + } + for (let track of tracks) { + this.player.setChannelVolume(track.playbackInfo.primaryChannel, volume); + this.player.setChannelVolume(track.playbackInfo.secondaryChannel, volume); + } + } + + /** + * Changes the given tracks to be played solo or not. + * If one or more tracks are set to solo, only those tracks are hearable. + * @param tracks The list of tracks to play solo or not. + * @param solo If set to true, the tracks will be added to the solo list. If false, they are removed. + */ + public changeTrackSolo(tracks: Track[], solo: boolean): void { + if (!this.player) { + return; + } + for (let track of tracks) { + this.player.setChannelSolo(track.playbackInfo.primaryChannel, solo); + this.player.setChannelSolo(track.playbackInfo.secondaryChannel, solo); + } + } + + /** + * Changes the given tracks to be muted or not. + * @param tracks The list of track to mute or unmute. + * @param mute If set to true, the tracks will be muted. If false they are unmuted. + */ + public changeTrackMute(tracks: Track[], mute: boolean): void { + if (!this.player) { + return; + } + for (let track of tracks) { + this.player.setChannelMute(track.playbackInfo.primaryChannel, mute); + this.player.setChannelMute(track.playbackInfo.secondaryChannel, mute); + } + } + + /** + * Starts the playback of the current song. + * @returns true if the playback was started, otherwise false. Reasons for not starting can be that the player is not ready or already playing. + */ + public play(): boolean { + if (!this.player) { + return false; + } + return this.player.play(); + } + + /** + * Pauses the playback of the current song. + */ + public pause(): void { + if (!this.player) { + return; + } + this.player.pause(); + } + + /** + * Toggles between play/pause depending on the current player state. + */ + public playPause(): void { + if (!this.player) { + return; + } + this.player.playPause(); + } + + /** + * Stops the playback of the current song, and moves the playback position back to the start. + */ + public stop(): void { + if (!this.player) { + return; + } + this.player.stop(); + } + + private _cursorWrapper: IContainer | null = null; + private _barCursor: IContainer | null = null; + private _beatCursor: IContainer | null = null; + private _selectionWrapper: IContainer | null = null; + private _previousTick: number = 0; + private _playerState: PlayerState = PlayerState.Paused; + private _currentBeat: Beat | null = null; + private _previousStateForCursor: PlayerState = PlayerState.Paused; + private _previousCursorCache: BoundsLookup | null = null; + private _lastScroll: number = 0; + + private destroyCursors(): void { + if (!this._cursorWrapper) { + return; + } + this.uiFacade.destroyCursors(); + this._cursorWrapper = null; + this._barCursor = null; + this._beatCursor = null; + this._selectionWrapper = null; + this._previousTick = 0; + this._playerState = PlayerState.Paused; + } + + private setupCursors(): void { + // + // Create cursors + let cursors = this.uiFacade.createCursors(); + if (!cursors) { + return; + } + // store options and created elements for fast access + this._cursorWrapper = cursors.cursorWrapper; + this._barCursor = cursors.barCursor; + this._beatCursor = cursors.beatCursor; + this._selectionWrapper = cursors.selectionWrapper; + // + // Hook into events + this._previousTick = 0; + this._playerState = PlayerState.Paused; + // we need to update our position caches if we render a tablature + this.renderer.postRenderFinished.on(() => { + this.cursorUpdateTick(this._previousTick, false); + }); + if (this.player) { + this.player.positionChanged.on(e => { + this._previousTick = e.currentTick; + this.uiFacade.beginInvoke(() => { + this.cursorUpdateTick(e.currentTick, false); + }); + }); + this.player.stateChanged.on(e => { + this._playerState = e.state; + if (!e.stopped && e.state === PlayerState.Paused) { + let currentBeat: Beat | null = this._currentBeat; + let tickCache: MidiTickLookup | null = this._tickCache; + if (currentBeat && tickCache) { + this.player!.tickPosition = + tickCache.getMasterBarStart(currentBeat.voice.bar.masterBar) + currentBeat.playbackStart; + } + } + }); + } + } + + /** + * updates the cursors to highlight the beat at the specified tick position + * @param tick + * @param stop + */ + private cursorUpdateTick(tick: number, stop: boolean = false): void { + this.uiFacade.beginInvoke(() => { + let cache: MidiTickLookup | null = this._tickCache; + if (cache) { + let tracks: Track[] = this.tracks; + if (tracks.length > 0) { + let beat: MidiTickLookupFindBeatResult | null = cache.findBeat(tracks, tick); + if (beat) { + this.cursorUpdateBeat( + beat.currentBeat, + beat.nextBeat, + beat.duration, + stop, + beat.beatsToHighlight + ); + } + } + } + }); + } + + /** + * updates the cursors to highlight the specified beat + */ + private cursorUpdateBeat( + beat: Beat, + nextBeat: Beat | null, + duration: number, + stop: boolean, + beatsToHighlight: Beat[] | null = null + ): void { + if (!beat) { + return; + } + let cache: BoundsLookup | null = this.renderer.boundsLookup; + if (!cache) { + return; + } + let previousBeat: Beat | null = this._currentBeat; + let previousCache: BoundsLookup | null = this._previousCursorCache; + let previousState: PlayerState | null = this._previousStateForCursor; + this._currentBeat = beat; + this._previousCursorCache = cache; + this._previousStateForCursor = this._playerState; + if (beat === previousBeat && cache === previousCache && previousState === this._playerState) { + return; + } + let barCursor: IContainer | null = this._barCursor; + let beatCursor: IContainer | null = this._beatCursor; + let beatBoundings: BeatBounds | null = cache.findBeat(beat); + if (!beatBoundings) { + return; + } + let barBoundings: MasterBarBounds = beatBoundings.barBounds.masterBarBounds; + let barBounds: Bounds = barBoundings.visualBounds; + if (barCursor) { + barCursor.top = barBounds.y; + barCursor.left = barBounds.x; + barCursor.width = barBounds.w; + barCursor.height = barBounds.h; + } + + if (beatCursor) { + // move beat to start position immediately + beatCursor.stopAnimation(); + beatCursor.top = barBounds.y; + beatCursor.left = beatBoundings.visualBounds.x; + beatCursor.height = barBounds.h; + } + + // if playing, animate the cursor to the next beat + this.uiFacade.removeHighlights(); + if (this._playerState === PlayerState.Playing || stop) { + duration /= this.playbackSpeed; + if (!stop) { + if (beatsToHighlight) { + for (let highlight of beatsToHighlight) { + let className: string = BeatContainerGlyph.getGroupId(highlight); + this.uiFacade.highlightElements(className); + } + } + let nextBeatX: number = barBoundings.visualBounds.x + barBoundings.visualBounds.w; + // get position of next beat on same stavegroup + if (nextBeat) { + // if we are moving within the same bar or to the next bar + // transition to the next beat, otherwise transition to the end of the bar. + if ( + nextBeat.voice.bar.index === beat.voice.bar.index || + nextBeat.voice.bar.index === beat.voice.bar.index + 1 + ) { + let nextBeatBoundings: BeatBounds | null = cache.findBeat(nextBeat); + if ( + nextBeatBoundings && + nextBeatBoundings.barBounds.masterBarBounds.staveGroupBounds === + barBoundings.staveGroupBounds + ) { + nextBeatX = nextBeatBoundings.visualBounds.x; + } + } + } + + if (beatCursor) { + this.uiFacade.beginInvoke(() => { + // Logger.Info("Player", + // "Transition from " + beatBoundings.VisualBounds.X + " to " + nextBeatX + " in " + duration + + // "(" + Player.PlaybackRange + ")"); + beatCursor!.transitionToX(duration, nextBeatX); + }); + } + } + if (!this._beatMouseDown && this.settings.player.scrollMode !== ScrollMode.Off) { + let scrollElement: IContainer = this.uiFacade.getScrollContainer(); + let isVertical: boolean = Environment.getLayoutEngineFactory(this.settings).vertical; + let mode: ScrollMode = this.settings.player.scrollMode; + let elementOffset: Bounds = this.uiFacade.getOffset(scrollElement, this.container); + if (isVertical) { + switch (mode) { + case ScrollMode.Continuous: + let y: number = + elementOffset.y + barBoundings.realBounds.y + this.settings.player.scrollOffsetY; + if (y !== this._lastScroll) { + this._lastScroll = y; + this.uiFacade.scrollToY(scrollElement, y, this.settings.player.scrollSpeed); + } + break; + case ScrollMode.OffScreen: + let elementBottom: number = + scrollElement.scrollTop + this.uiFacade.getOffset(null, scrollElement).h; + if ( + barBoundings.visualBounds.y + barBoundings.visualBounds.h >= elementBottom || + barBoundings.visualBounds.y < scrollElement.scrollTop + ) { + let scrollTop: number = barBoundings.realBounds.y + this.settings.player.scrollOffsetY; + this._lastScroll = barBoundings.visualBounds.x; + this.uiFacade.scrollToY(scrollElement, scrollTop, this.settings.player.scrollSpeed); + } + break; + } + } else { + switch (mode) { + case ScrollMode.Continuous: + let x: number = barBoundings.visualBounds.x; + if (x !== this._lastScroll) { + let scrollLeft: number = barBoundings.realBounds.x + this.settings.player.scrollOffsetX; + this._lastScroll = barBoundings.visualBounds.x; + this.uiFacade.scrollToX(scrollElement, scrollLeft, this.settings.player.scrollSpeed); + } + break; + case ScrollMode.OffScreen: + let elementRight: number = + scrollElement.scrollLeft + this.uiFacade.getOffset(null, scrollElement).w; + if ( + barBoundings.visualBounds.x + barBoundings.visualBounds.w >= elementRight || + barBoundings.visualBounds.x < scrollElement.scrollLeft + ) { + let scrollLeft: number = barBoundings.realBounds.x + this.settings.player.scrollOffsetX; + this._lastScroll = barBoundings.visualBounds.x; + this.uiFacade.scrollToX(scrollElement, scrollLeft, this.settings.player.scrollSpeed); + } + break; + } + } + } + // trigger an event for others to indicate which beat/bar is played + this.onPlayedBeatChanged(beat); + } + } + + public playedBeatChanged: IEventEmitterOfT = new EventEmitterOfT(); + private onPlayedBeatChanged(beat: Beat): void { + (this.playedBeatChanged as EventEmitterOfT).trigger(beat); + this.uiFacade.triggerEvent(this.container, 'playedBeatChanged', beat); + } + + private _beatMouseDown: boolean = false; + private _selectionStart: SelectionInfo | null = null; + private _selectionEnd: SelectionInfo | null = null; + + public beatMouseDown: IEventEmitterOfT = new EventEmitterOfT(); + public beatMouseMove: IEventEmitterOfT = new EventEmitterOfT(); + public beatMouseUp: IEventEmitterOfT = new EventEmitterOfT(); + + private onBeatMouseDown(originalEvent: IMouseEventArgs, beat: Beat): void { + if ( + this.settings.player.enablePlayer && + this.settings.player.enableCursor && + this.settings.player.enableUserInteraction + ) { + this._selectionStart = new SelectionInfo(beat); + this._selectionEnd = null; + } + this._beatMouseDown = true; + (this.beatMouseDown as EventEmitterOfT).trigger(beat); + this.uiFacade.triggerEvent(this.container, 'beatMouseDown', beat, originalEvent); + } + + private onBeatMouseMove(originalEvent: IMouseEventArgs, beat: Beat): void { + if (this.settings.player.enableUserInteraction) { + if (!this._selectionEnd || this._selectionEnd.beat !== beat) { + this._selectionEnd = new SelectionInfo(beat); + this.cursorSelectRange(this._selectionStart, this._selectionEnd); + } + } + (this.beatMouseMove as EventEmitterOfT).trigger(beat); + this.uiFacade.triggerEvent(this.container, 'beatMouseMove', beat, originalEvent); + } + + private onBeatMouseUp(originalEvent: IMouseEventArgs, beat: Beat | null): void { + if (this.settings.player.enableUserInteraction) { + // for the selection ensure start < end + if (this._selectionEnd) { + let startTick: number = this._selectionStart!.beat.absoluteDisplayStart; + let endTick: number = this._selectionStart!.beat.absoluteDisplayStart; + if (endTick < startTick) { + let t: SelectionInfo = this._selectionStart!; + this._selectionStart = this._selectionEnd; + this._selectionEnd = t; + } + } + if (this._selectionStart && this._tickCache) { + // get the start and stop ticks (which consider properly repeats) + let tickCache: MidiTickLookup = this._tickCache; + let realMasterBarStart: number = tickCache.getMasterBarStart( + this._selectionStart.beat.voice.bar.masterBar + ); + // move to selection start + this._currentBeat = null; // reset current beat so it is updating the cursor + if(this._playerState === PlayerState.Paused) { + this.cursorUpdateBeat(this._selectionStart.beat, null, 0, false, [this._selectionStart.beat]); + } + this.tickPosition = realMasterBarStart + this._selectionStart.beat.playbackStart; + // set playback range + if (this._selectionEnd && this._selectionStart.beat !== this._selectionEnd.beat) { + let realMasterBarEnd: number = tickCache.getMasterBarStart( + this._selectionEnd.beat.voice.bar.masterBar + ); + + let range = new PlaybackRange(); + range.startTick = realMasterBarStart + this._selectionStart.beat.playbackStart; + range.endTick = + realMasterBarEnd + + this._selectionEnd.beat.playbackStart + + this._selectionEnd.beat.playbackDuration - + 50; + this.playbackRange = range; + } else { + this._selectionStart = null; + this.playbackRange = null; + this.cursorSelectRange(this._selectionStart, this._selectionEnd); + } + } + } + + (this.beatMouseUp as EventEmitterOfT).trigger(beat); + this.uiFacade.triggerEvent(this.container, 'beatMouseUp', beat, originalEvent); + this._beatMouseDown = false; + } + + private setupClickHandling(): void { + this.canvasElement.mouseDown.on(e => { + if (!e.isLeftMouseButton) { + return; + } + if (this.settings.player.enableUserInteraction) { + e.preventDefault(); + } + let relX: number = e.getX(this.canvasElement); + let relY: number = e.getY(this.canvasElement); + let beat: Beat | null = this.renderer.boundsLookup?.getBeatAtPos(relX, relY) ?? null; + if (beat) { + this.onBeatMouseDown(e, beat); + } + }); + this.canvasElement.mouseMove.on(e => { + if (!this._beatMouseDown) { + return; + } + let relX: number = e.getX(this.canvasElement); + let relY: number = e.getY(this.canvasElement); + let beat: Beat | null = this.renderer.boundsLookup?.getBeatAtPos(relX, relY) ?? null; + if (beat) { + this.onBeatMouseMove(e, beat); + } + }); + this.canvasElement.mouseUp.on(e => { + if (!this._beatMouseDown) { + return; + } + if (this.settings.player.enableUserInteraction) { + e.preventDefault(); + } + let relX: number = e.getX(this.canvasElement); + let relY: number = e.getY(this.canvasElement); + let beat: Beat | null = this.renderer.boundsLookup?.getBeatAtPos(relX, relY) ?? null; + this.onBeatMouseUp(e, beat); + }); + this.renderer.postRenderFinished.on(() => { + if ( + !this._selectionStart || + !this.settings.player.enablePlayer || + !this.settings.player.enableCursor || + !this.settings.player.enableUserInteraction + ) { + return; + } + this.cursorSelectRange(this._selectionStart, this._selectionEnd); + }); + } + + private cursorSelectRange(startBeat: SelectionInfo | null, endBeat: SelectionInfo | null): void { + let cache: BoundsLookup | null = this.renderer.boundsLookup; + if (!cache) { + return; + } + let selectionWrapper: IContainer | null = this._selectionWrapper; + if (!selectionWrapper) { + return; + } + + selectionWrapper.clear(); + if (!startBeat || !endBeat || startBeat.beat === endBeat.beat) { + return; + } + + if (!startBeat.bounds) { + startBeat.bounds = cache.findBeat(startBeat.beat); + } + if (!endBeat.bounds) { + endBeat.bounds = cache.findBeat(endBeat.beat); + } + let startTick: number = startBeat.beat.absolutePlaybackStart; + let endTick: number = endBeat.beat.absolutePlaybackStart; + if (endTick < startTick) { + let t: SelectionInfo = startBeat; + startBeat = endBeat; + endBeat = t; + } + let startX: number = startBeat.bounds!.realBounds.x; + let endX: number = endBeat.bounds!.realBounds.x + endBeat.bounds!.realBounds.w; + if (endBeat.beat.index === endBeat.beat.voice.beats.length - 1) { + endX = + endBeat.bounds!.barBounds.masterBarBounds.realBounds.x + + endBeat.bounds!.barBounds.masterBarBounds.realBounds.w; + } + // if the selection goes across multiple staves, we need a special selection highlighting + if ( + startBeat.bounds!.barBounds.masterBarBounds.staveGroupBounds !== + endBeat.bounds!.barBounds.masterBarBounds.staveGroupBounds + ) { + // from the startbeat to the end of the staff, + // then fill all staffs until the end-beat staff + // then from staff-start to the end beat (or to end of bar if it's the last beat) + let staffStartX: number = startBeat.bounds!.barBounds.masterBarBounds.staveGroupBounds.visualBounds.x; + let staffEndX: number = + startBeat.bounds!.barBounds.masterBarBounds.staveGroupBounds.visualBounds.x + + startBeat.bounds!.barBounds.masterBarBounds.staveGroupBounds.visualBounds.w; + let startSelection: IContainer = this.uiFacade.createSelectionElement()!; + startSelection.top = startBeat.bounds!.barBounds.masterBarBounds.visualBounds.y; + startSelection.left = startX; + startSelection.width = staffEndX - startX; + startSelection.height = startBeat.bounds!.barBounds.masterBarBounds.visualBounds.h; + selectionWrapper.appendChild(startSelection); + let staffStartIndex: number = startBeat.bounds!.barBounds.masterBarBounds.staveGroupBounds.index + 1; + let staffEndIndex: number = endBeat.bounds!.barBounds.masterBarBounds.staveGroupBounds.index; + for (let staffIndex: number = staffStartIndex; staffIndex < staffEndIndex; staffIndex++) { + let staffBounds: StaveGroupBounds = cache.staveGroups[staffIndex]; + let middleSelection: IContainer = this.uiFacade.createSelectionElement()!; + middleSelection.top = staffBounds.visualBounds.y; + middleSelection.left = staffStartX; + middleSelection.width = staffEndX - staffStartX; + middleSelection.height = staffBounds.visualBounds.h; + selectionWrapper.appendChild(middleSelection); + } + let endSelection: IContainer = this.uiFacade.createSelectionElement()!; + endSelection.top = endBeat.bounds!.barBounds.masterBarBounds.visualBounds.y; + endSelection.left = staffStartX; + endSelection.width = endX - staffStartX; + endSelection.height = endBeat.bounds!.barBounds.masterBarBounds.visualBounds.h; + selectionWrapper.appendChild(endSelection); + } else { + // if the beats are on the same staff, we simply highlight from the startbeat to endbeat + let selection: IContainer = this.uiFacade.createSelectionElement()!; + selection.top = startBeat.bounds!.barBounds.masterBarBounds.visualBounds.y; + selection.left = startX; + selection.width = endX - startX; + selection.height = startBeat.bounds!.barBounds.masterBarBounds.visualBounds.h; + selectionWrapper.appendChild(selection); + } + } + + public scoreLoaded: IEventEmitterOfT = new EventEmitterOfT(); + private onScoreLoaded(score: Score): void { + (this.scoreLoaded as EventEmitterOfT).trigger(score); + this.uiFacade.triggerEvent(this.container, 'scoreLoaded', score); + } + + public resize: IEventEmitterOfT = new EventEmitterOfT(); + private onResize(e: ResizeEventArgs): void { + (this.resize as EventEmitterOfT).trigger(e); + this.uiFacade.triggerEvent(this.container, 'resize', e); + } + + public renderStarted: IEventEmitterOfT = new EventEmitterOfT(); + private onRenderStarted(resize: boolean): void { + (this.renderStarted as EventEmitterOfT).trigger(resize); + this.uiFacade.triggerEvent(this.container, 'renderStarted', resize); + } + + public renderFinished: IEventEmitterOfT = new EventEmitterOfT(); + private onRenderFinished(renderingResult: RenderFinishedEventArgs): void { + (this.renderFinished as EventEmitterOfT).trigger(renderingResult); + this.uiFacade.triggerEvent(this.container, 'renderFinished', renderingResult); + } + + public postRenderFinished: IEventEmitter = new EventEmitter(); + private onPostRenderFinished(): void { + (this.postRenderFinished as EventEmitter).trigger(); + this.uiFacade.triggerEvent(this.container, 'postRenderFinished', null); + } + + public error: IEventEmitterOfT = new EventEmitterOfT(); + public onError(error: Error): void { + Logger.error('API', 'An unexpected error occurred', error); + (this.error as EventEmitterOfT).trigger(error); + this.uiFacade.triggerEvent(this.container, 'error', error); + } + + public playerReady: IEventEmitter = new EventEmitter(); + private onPlayerReady(): void { + (this.playerReady as EventEmitter).trigger(); + this.uiFacade.triggerEvent(this.container, 'playerReady', null); + } + + public playerFinished: IEventEmitter = new EventEmitter(); + private onPlayerFinished(): void { + (this.playerFinished as EventEmitter).trigger(); + this.uiFacade.triggerEvent(this.container, 'playerFinished', null); + } + + public soundFontLoaded: IEventEmitter = new EventEmitter(); + private onSoundFontLoaded(): void { + (this.soundFontLoaded as EventEmitter).trigger(); + this.uiFacade.triggerEvent(this.container, 'soundFontLoaded', null); + } + + public midiLoaded: IEventEmitter = new EventEmitter(); + private onMidiLoaded(): void { + (this.midiLoaded as EventEmitter).trigger(); + this.uiFacade.triggerEvent(this.container, 'midiFileLoaded', null); + } + + public playerStateChanged: IEventEmitterOfT = new EventEmitterOfT< + PlayerStateChangedEventArgs + >(); + private onPlayerStateChanged(e: PlayerStateChangedEventArgs): void { + (this.playerStateChanged as EventEmitterOfT).trigger(e); + this.uiFacade.triggerEvent(this.container, 'playerStateChanged', e); + } + + public playerPositionChanged: IEventEmitterOfT = new EventEmitterOfT< + PositionChangedEventArgs + >(); + private onPlayerPositionChanged(e: PositionChangedEventArgs): void { + (this.playerPositionChanged as EventEmitterOfT).trigger(e); + this.uiFacade.triggerEvent(this.container, 'playerPositionChanged', e); + } +} diff --git a/src/AlphaTabError.ts b/src/AlphaTabError.ts new file mode 100644 index 000000000..69bd6c477 --- /dev/null +++ b/src/AlphaTabError.ts @@ -0,0 +1,17 @@ +export enum AlphaTabErrorType { + General, + Format, + AlphaTex +} + +export class AlphaTabError extends Error { + public inner: Error | null; + public type: AlphaTabErrorType; + + public constructor(type: AlphaTabErrorType, message: string, inner?: Error) { + super(message); + this.type = type; + this.inner = inner ?? null; + Object.setPrototypeOf(this, AlphaTabError.prototype); + } +} diff --git a/src/CoreSettings.ts b/src/CoreSettings.ts new file mode 100644 index 000000000..a9aa106d8 --- /dev/null +++ b/src/CoreSettings.ts @@ -0,0 +1,152 @@ +import { Environment } from '@src/Environment'; +import { LogLevel } from '@src/LogLevel'; + +/** + * @json + */ +export class CoreSettings { + /** + * Gets or sets the script file url that will be used to spawn the workers. + * @target web + */ + public scriptFile: string | null = null; + + /** + * Gets or sets the url to the fonts that will be used to generate the alphaTab font style. + * @target web + */ + public fontDirectory: string | null = null; + + /** + * Gets or sets the file to load directly after initializing alphaTab. + * @target web + */ + public file: string | null = null; + + /** + * Gets or sets whether the UI element contains alphaTex code that should be + * used to initialize alphaTab. + * @target web + */ + public tex: boolean = false; + + /** + * Gets or sets the initial tracks that should be loaded for the score. + * @target web + */ + public tracks: unknown = null; + + /** + * Gets or sets the interval in which alphaTab should check whether the + * target element for rendering is already visible. + * @target web + */ + public visibilityCheckInterval: number = 500; + + /** + * Gets or sets whether lazy loading for displayed elements is enabled. + */ + public enableLazyLoading: boolean = true; + + /** + * The engine which should be used to render the the tablature. + * + * - **default**- Platform specific default engine + * - **html5**- HTML5 Canvas + * - **svg**- SVG + */ + public engine: string = 'default'; + + /** + * The log level to use within alphaTab + */ + public logLevel: LogLevel = LogLevel.Info; + + /** + * Gets or sets whether the rendering should be done in a worker if possible. + */ + public useWorkers: boolean = true; + + /** + * Gets or sets whether in the {@link BoundsLookup} also the + * position and area of each individual note is provided. + */ + public includeNoteBounds: boolean = false; + + /** + * @target web + */ + public constructor() { + if (!Environment.isRunningInWorker && Environment.globalThis.ALPHATAB_ROOT) { + this.scriptFile = Environment.globalThis.ALPHATAB_ROOT; + this.scriptFile = CoreSettings.ensureFullUrl(this.scriptFile); + this.scriptFile = CoreSettings.appendScriptName(this.scriptFile); + } else { + this.scriptFile = Environment.scriptFile; + } + + if (!Environment.isRunningInWorker && Environment.globalThis.ALPHATAB_FONT) { + this.fontDirectory = Environment.globalThis['ALPHATAB_FONT']; + this.fontDirectory = CoreSettings.ensureFullUrl(this.fontDirectory); + } else { + this.fontDirectory = this.scriptFile; + if (this.fontDirectory) { + let lastSlash: number = this.fontDirectory.lastIndexOf(String.fromCharCode(47)); + if (lastSlash >= 0) { + this.fontDirectory = this.fontDirectory.substr(0, lastSlash) + '/font/'; + } + } + } + } + + /** + * @target web + */ + public static ensureFullUrl(relativeUrl: string | null): string { + if(!relativeUrl) { + return ''; + } + + if (!relativeUrl.startsWith('http') && !relativeUrl.startsWith('https') && !relativeUrl.startsWith('file')) { + let root: string = ''; + let location: Location = Environment.globalThis['location']; + root += location.protocol?.toString(); + root += '//'?.toString(); + if (location.hostname) { + root += location.hostname?.toString(); + } + if (location.port) { + root += ':'?.toString(); + root += location.port?.toString(); + } + // as it is not clearly defined how slashes are treated in the location object + // better be safe than sorry here + if (!relativeUrl.startsWith('/')) { + let directory: string = location.pathname.split('/').slice(0, -1).join('/'); + if (directory.length > 0) { + if (!directory.startsWith('/')) { + root += '/'?.toString(); + } + root += directory?.toString(); + } + } + if (!relativeUrl.startsWith('/')) { + root += '/'?.toString(); + } + root += relativeUrl?.toString(); + return root; + } + return relativeUrl; + } + + private static appendScriptName(url: string): string { + // append script name + if (url && !url.endsWith('.js')) { + if (!url.endsWith('/')) { + url += '/'; + } + url += 'alphaTab.js'; + } + return url; + } +} diff --git a/src/DisplaySettings.ts b/src/DisplaySettings.ts new file mode 100644 index 000000000..c8ee72f9f --- /dev/null +++ b/src/DisplaySettings.ts @@ -0,0 +1,98 @@ +import { RenderingResources } from '@src/RenderingResources'; + +/** + * Lists all stave profiles controlling which staves are shown. + */ +export enum StaveProfile { + /** + * The profile is auto detected by the track configurations. + */ + Default, + /** + * Standard music notation and guitar tablature are rendered. + */ + ScoreTab, + /** + * Only standard music notation is rendered. + */ + Score, + /** + * Only guitar tablature is rendered. + */ + Tab, + /** + * Only guitar tablature is rendered, but also rests and time signatures are not shown. + * This profile is typically used in multi-track scenarios. + */ + TabMixed +} + +/** + * Lists all layout modes that are supported. + */ +export enum LayoutMode { + /** + * Bars are aligned in rows using a fixed width. + */ + Page, + /** + * Bars are aligned horizontally in one row + */ + Horizontal +} + +/** + * The display settings control how the general layout and display of alphaTab is done. + * @json + */ +export class DisplaySettings { + /** + * Sets the zoom level of the rendered notation + */ + public scale: number = 1.0; + + /** + * The default stretch force to use for layouting. + */ + public stretchForce: number = 1.0; + + /** + * The layouting mode used to arrange the the notation. + */ + public layoutMode: LayoutMode = LayoutMode.Page; + + /** + * The stave profile to use. + */ + public staveProfile: StaveProfile = StaveProfile.Default; + + /** + * Limit the displayed bars per row. + */ + public barsPerRow: number = -1; + + /** + * The bar start number to start layouting with. Note that this is the bar number and not an index! + */ + public startBar: number = 1; + + /** + * The amount of bars to render overall. + */ + public barCount: number = -1; + + /** + * The number of bars that should be rendered per partial. This setting is not used by all layouts. + */ + public barCountPerPartial: number = 10; + + /** + * Gets or sets the resources used during rendering. This defines all fonts and colors used. + */ + public readonly resources: RenderingResources = new RenderingResources(); + + /** + * Gets or sets the padding between the music notation and the border. + */ + public padding: Float32Array | null = null; +} diff --git a/src/Environment.ts b/src/Environment.ts new file mode 100644 index 000000000..27a047d34 --- /dev/null +++ b/src/Environment.ts @@ -0,0 +1,564 @@ +import { LayoutMode, StaveProfile } from '@src/DisplaySettings'; +import { AlphaTexImporter } from '@src/importer/AlphaTexImporter'; +import { Gp3To5Importer } from '@src/importer/Gp3To5Importer'; +import { Gp7Importer } from '@src/importer/Gp7Importer'; +import { GpxImporter } from '@src/importer/GpxImporter'; +import { MusicXmlImporter } from '@src/importer/MusicXmlImporter'; +import { ScoreImporter } from '@src/importer/ScoreImporter'; +import { HarmonicType } from '@src/model/HarmonicType'; +import { ICanvas } from '@src/platform/ICanvas'; +import { AlphaSynthWebWorker } from '@src/platform/javascript/AlphaSynthWebWorker'; +import { AlphaTabWebWorker } from '@src/platform/javascript/AlphaTabWebWorker'; +import { Html5Canvas } from '@src/platform/javascript/Html5Canvas'; +import { JQueryAlphaTab } from '@src/platform/javascript/JQueryAlphaTab'; +import { CssFontSvgCanvas } from '@src/platform/svg/CssFontSvgCanvas'; +import { BarRendererFactory } from '@src/rendering/BarRendererFactory'; +import { EffectBarRendererFactory } from '@src/rendering/EffectBarRendererFactory'; +import { AlternateEndingsEffectInfo } from '@src/rendering/effects/AlternateEndingsEffectInfo'; +import { CapoEffectInfo } from '@src/rendering/effects/CapoEffectInfo'; +import { ChordsEffectInfo } from '@src/rendering/effects/ChordsEffectInfo'; +import { CrescendoEffectInfo } from '@src/rendering/effects/CrescendoEffectInfo'; +import { DynamicsEffectInfo } from '@src/rendering/effects/DynamicsEffectInfo'; +import { FadeInEffectInfo } from '@src/rendering/effects/FadeInEffectInfo'; +import { FermataEffectInfo } from '@src/rendering/effects/FermataEffectInfo'; +import { FingeringEffectInfo } from '@src/rendering/effects/FingeringEffectInfo'; +import { HarmonicsEffectInfo } from '@src/rendering/effects/HarmonicsEffectInfo'; +import { LetRingEffectInfo } from '@src/rendering/effects/LetRingEffectInfo'; +import { LyricsEffectInfo } from '@src/rendering/effects/LyricsEffectInfo'; +import { MarkerEffectInfo } from '@src/rendering/effects/MarkerEffectInfo'; +import { OttaviaEffectInfo } from '@src/rendering/effects/OttaviaEffectInfo'; +import { PalmMuteEffectInfo } from '@src/rendering/effects/PalmMuteEffectInfo'; +import { PickSlideEffectInfo } from '@src/rendering/effects/PickSlideEffectInfo'; +import { PickStrokeEffectInfo } from '@src/rendering/effects/PickStrokeEffectInfo'; +import { SlightBeatVibratoEffectInfo } from '@src/rendering/effects/SlightBeatVibratoEffectInfo'; +import { SlightNoteVibratoEffectInfo } from '@src/rendering/effects/SlightNoteVibratoEffectInfo'; +import { TapEffectInfo } from '@src/rendering/effects/TapEffectInfo'; +import { TempoEffectInfo } from '@src/rendering/effects/TempoEffectInfo'; +import { TextEffectInfo } from '@src/rendering/effects/TextEffectInfo'; +import { TrillEffectInfo } from '@src/rendering/effects/TrillEffectInfo'; +import { TripletFeelEffectInfo } from '@src/rendering/effects/TripletFeelEffectInfo'; +import { WhammyBarEffectInfo } from '@src/rendering/effects/WhammyBarEffectInfo'; +import { WideBeatVibratoEffectInfo } from '@src/rendering/effects/WideBeatVibratoEffectInfo'; +import { WideNoteVibratoEffectInfo } from '@src/rendering/effects/WideNoteVibratoEffectInfo'; +import { MusicFontSymbol } from '@src/rendering/glyphs/MusicFontSymbol'; +import { EffectBarRendererInfo } from '@src/rendering/EffectBarRendererInfo'; +import { IScoreRenderer } from '@src/rendering/IScoreRenderer'; +import { HorizontalScreenLayout } from '@src/rendering/layout/HorizontalScreenLayout'; +import { PageViewLayout } from '@src/rendering/layout/PageViewLayout'; +import { ScoreLayout } from '@src/rendering/layout/ScoreLayout'; +import { ScoreBarRendererFactory } from '@src/rendering/ScoreBarRendererFactory'; +import { ScoreRenderer } from '@src/rendering/ScoreRenderer'; +import { TabBarRendererFactory } from '@src/rendering/TabBarRendererFactory'; +import { Settings } from '@src/Settings'; +import { FontLoadingChecker } from '@src/util/FontLoadingChecker'; +import { Logger } from '@src/Logger'; +import { LeftHandTapEffectInfo } from './rendering/effects/LeftHandTapEffectInfo'; + +export class LayoutEngineFactory { + public readonly vertical: boolean; + public readonly createLayout: (renderer: ScoreRenderer) => ScoreLayout; + + public constructor(vertical: boolean, createLayout: (renderer: ScoreRenderer) => ScoreLayout) { + this.vertical = vertical; + this.createLayout = createLayout; + } +} + +export class RenderEngineFactory { + public readonly supportsWorkers: boolean; + public readonly createCanvas: () => ICanvas; + + public constructor(supportsWorkers: boolean, canvas: () => ICanvas) { + this.supportsWorkers = supportsWorkers; + this.createCanvas = canvas; + } +} + +/** + * This public class represents the global alphaTab environment where + * alphaTab looks for information like available layout engines + * staves etc. + * This public class represents the global alphaTab environment where + * alphaTab looks for information like available layout engines + * staves etc. + * @partial + */ +export class Environment { + /** + * @target web + */ + public static createStyleElement(elementDocument: HTMLDocument, fontDirectory: string | null) { + let styleElement: HTMLStyleElement = elementDocument.getElementById('alphaTabStyle') as HTMLStyleElement; + if (!styleElement) { + if (!fontDirectory) { + Logger.error('AlphaTab', 'Font directory could not be detected, cannot create style element'); + return; + } + + styleElement = elementDocument.createElement('style'); + styleElement.id = 'alphaTabStyle'; + let css: string = ` + @font-face { + font-family: 'alphaTab'; + src: url('${fontDirectory}Bravura.eot'); + src: url('${fontDirectory}Bravura.eot?#iefix') format('embedded-opentype') + , url('${fontDirectory}Bravura.woff') format('woff') + , url('${fontDirectory}Bravura.otf') format('opentype') + , url('${fontDirectory}Bravura.svg#Bravura') format('svg'); + font-weight: normal; + font-style: normal; + } + .at-surface * { + cursor: default; + vertical-align: top; + overflow: visible; + } + .at { + font-family: 'alphaTab'; + speak: none; + font-style: normal; + font-weight: normal; + font-variant: normal; + text-transform: none; + line-height: 1; + line-height: 1; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + font-size: 34px; + overflow: visible !important; + }`; + + styleElement.innerHTML = css; + elementDocument.getElementsByTagName('head').item(0)!.appendChild(styleElement); + Environment.bravuraFontChecker.checkForFontAvailability(); + } + } + + /** + * @target web + */ + private static _globalThis: any | undefined = undefined; + + /** + * @target web + */ + public static get globalThis(): any { + if (Environment._globalThis === undefined) { + try { + Environment._globalThis = globalThis; + } catch (e) { + // global this not available + } + + if (typeof Environment._globalThis === 'undefined') { + Environment._globalThis = self; + } + if (typeof Environment._globalThis === 'undefined') { + Environment._globalThis = window; + } + if (typeof Environment._globalThis === 'undefined') { + Environment._globalThis = Function('return this')(); + } + } + + return this._globalThis; + } + + /** + * @target web + */ + public static scriptFile: string | null = Environment.detectScriptFile(); + + /** + * @target web + */ + public static bravuraFontChecker: FontLoadingChecker = new FontLoadingChecker( + 'alphaTab', + `&#${MusicFontSymbol.ClefG};` + ); + + /** + * @target web + */ + public static get isRunningInWorker(): boolean { + return 'WorkerGlobalScope' in Environment.globalThis; + } + + /** + * @target web + */ + public static get supportsFontsApi(): boolean { + return 'fonts' in document && 'load' in (document as any).fonts; + } + + /** + * @target web + */ + public static get supportsTextDecoder(): boolean { + return 'TextDecoder' in Environment.globalThis; + } + + /** + * @target web + */ + public static throttle(action: () => void, delay: number): () => void { + let timeoutId: number = 0; + return () => { + window.clearTimeout(timeoutId); + timeoutId = window.setTimeout(action, delay); + }; + } + + /** + * @target web + */ + private static detectScriptFile(): string | null { + if (Environment.isRunningInWorker) { + return null; + } + // try to build the find the alphaTab script url in case we are not in the webworker already + let scriptElement: HTMLScriptElement = document.currentScript as HTMLScriptElement; + let scriptFile: string | null = null; + + if (!scriptElement) { + // try to get javascript from exception stack + try { + let error: Error = new Error(); + let stack = error.stack; + if (!stack) { + throw error; + } + scriptFile = Environment.scriptFileFromStack(stack); + } catch (e) { + if (e instanceof Error) { + let stack = e.stack; + if (!stack) { + scriptElement = document.querySelector('script[data-alphatab]') as HTMLScriptElement; + } else { + scriptFile = Environment.scriptFileFromStack(stack); + } + } else { + throw e; + } + } + } + + // failed to automatically resolve + if (!scriptFile) { + if (!scriptElement) { + Logger.warning( + 'Environment', + 'Could not automatically find alphaTab script file for worker, please add the data-alphatab attribute to the script tag that includes alphaTab or provide it when initializing alphaTab', + null + ); + } else { + scriptFile = scriptElement.src; + } + } + + return scriptFile; + } + + /** + * @target web + */ + private static registerJQueryPlugin(): void { + if (!Environment.isRunningInWorker && Environment.globalThis && 'jQuery' in Environment.globalThis) { + let jquery: any = Environment.globalThis['jQuery']; + let api: JQueryAlphaTab = new JQueryAlphaTab(); + jquery.fn.alphaTab = function (this: any, method: string) { + const args = Array.prototype.slice.call(arguments, 1); + // if only a single element is affected, we use this + if (this.length === 1) { + return api.exec(this[0], method, args); + } + // if multiple elements are affected we provide chaining + return this.each((_i: number, e: HTMLElement) => { + api.exec(e, method, args); + }); + }; + jquery.alphaTab = { + restore: JQueryAlphaTab.restore + }; + jquery.fn.alphaTab.fn = api; + } + } + + /** + * based on https://github.com/JamesMGreene/currentExecutingScript + * @target web + */ + private static scriptFileFromStack(stack: string): string | null { + let matches = stack.match( + '(data:text\\/javascript(?:;[^,]+)?,.+?|(?:|blob:)(?:http[s]?|file):\\/\\/[\\/]?.+?\\/[^:\\)]*?)(?::\\d+)(?::\\d+)?' + ); + if (!matches) { + matches = stack.match( + '^(?:|[^:@]*@|.+\\)@(?=data:text\\/javascript|blob|http[s]?|file)|.+?\\s+(?: at |@)(?:[^:\\(]+ )*[\\(]?)(data:text\\/javascript(?:;[^,]+)?,.+?|(?:|blob:)(?:http[s]?|file):\\/\\/[\\/]?.+?\\/[^:\\)]*?)(?::\\d+)(?::\\d+)?' + ); + if (!matches) { + matches = stack.match( + '\\)@(data:text\\/javascript(?:;[^,]+)?,.+?|(?:|blob:)(?:http[s]?|file):\\/\\/[\\/]?.+?\\/[^:\\)]*?)(?::\\d+)(?::\\d+)?' + ); + if (!matches) { + return null; + } + } + } + return matches[1]; + } + + public static renderEngines: Map = Environment.createDefaultRenderEngines(); + public static layoutEngines: Map = Environment.createDefaultLayoutEngines(); + public static staveProfiles: Map = Environment.createDefaultStaveProfiles(); + + public static createScoreRenderer(settings: Settings): IScoreRenderer { + return new ScoreRenderer(settings); + } + + public static getRenderEngineFactory(settings: Settings): RenderEngineFactory { + if (!settings.core.engine || !Environment.renderEngines.has(settings.core.engine)) { + return Environment.renderEngines.get('default')!; + } + return Environment.renderEngines.get(settings.core.engine)!; + } + + public static getLayoutEngineFactory(settings: Settings): LayoutEngineFactory { + if (!settings.display.layoutMode || !Environment.layoutEngines.has(settings.display.layoutMode)) { + return Environment.layoutEngines.get(LayoutMode.Page)!; + } + return Environment.layoutEngines.get(settings.display.layoutMode)!; + } + + /** + * Gets all default ScoreImporters + * @returns + */ + public static buildImporters(): ScoreImporter[] { + return [ + new Gp3To5Importer(), + new GpxImporter(), + new Gp7Importer(), + new AlphaTexImporter(), + new MusicXmlImporter() + ]; + } + + private static createDefaultRenderEngines(): Map { + const renderEngines = new Map(); + renderEngines.set( + 'svg', + new RenderEngineFactory(true, () => { + return new CssFontSvgCanvas(); + }) + ); + renderEngines.set('default', renderEngines.get('svg')!); + Environment.createPlatformSpecificRenderEngines(renderEngines); + return renderEngines; + } + + /** + * @target web + */ + private static createPlatformSpecificRenderEngines(renderEngines: Map) { + renderEngines.set( + 'html5', + new RenderEngineFactory(false, () => { + return new Html5Canvas(); + }) + ); + } + + private static createDefaultStaveProfiles(): Map { + const staveProfiles = new Map(); + + // default combinations of stave textprofiles + staveProfiles.set(StaveProfile.ScoreTab, [ + new EffectBarRendererFactory('score-effects', [ + new TempoEffectInfo(), + new TripletFeelEffectInfo(), + new MarkerEffectInfo(), + new TextEffectInfo(), + new ChordsEffectInfo(), + new FermataEffectInfo(), + new WhammyBarEffectInfo(), + new TrillEffectInfo(), + new OttaviaEffectInfo(true), + new WideBeatVibratoEffectInfo(), + new SlightBeatVibratoEffectInfo(), + new WideNoteVibratoEffectInfo(), + new SlightNoteVibratoEffectInfo(), + new LeftHandTapEffectInfo(), + new AlternateEndingsEffectInfo() + ]), + new ScoreBarRendererFactory(), + new EffectBarRendererFactory('tab-effects', [ + new CrescendoEffectInfo(), + new OttaviaEffectInfo(false), + new DynamicsEffectInfo(), + new LyricsEffectInfo(), + new TrillEffectInfo(), + new WideBeatVibratoEffectInfo(), + new SlightBeatVibratoEffectInfo(), + new WideNoteVibratoEffectInfo(), + new SlightNoteVibratoEffectInfo(), + new TapEffectInfo(), + new FadeInEffectInfo(), + new HarmonicsEffectInfo(HarmonicType.Natural), + new HarmonicsEffectInfo(HarmonicType.Artificial), + new HarmonicsEffectInfo(HarmonicType.Pinch), + new HarmonicsEffectInfo(HarmonicType.Tap), + new HarmonicsEffectInfo(HarmonicType.Semi), + new HarmonicsEffectInfo(HarmonicType.Feedback), + new LetRingEffectInfo(), + new CapoEffectInfo(), + new FingeringEffectInfo(), + new PalmMuteEffectInfo(), + new PickStrokeEffectInfo(), + new PickSlideEffectInfo(), + new LeftHandTapEffectInfo() + ]), + new TabBarRendererFactory(false, false, false) + ]); + staveProfiles.set(StaveProfile.Score, [ + new EffectBarRendererFactory('score-effects', [ + new TempoEffectInfo(), + new TripletFeelEffectInfo(), + new MarkerEffectInfo(), + new TextEffectInfo(), + new ChordsEffectInfo(), + new FermataEffectInfo(), + new WhammyBarEffectInfo(), + new TrillEffectInfo(), + new OttaviaEffectInfo(true), + new WideBeatVibratoEffectInfo(), + new SlightBeatVibratoEffectInfo(), + new WideNoteVibratoEffectInfo(), + new SlightNoteVibratoEffectInfo(), + new FadeInEffectInfo(), + new LetRingEffectInfo(), + new PalmMuteEffectInfo(), + new PickStrokeEffectInfo(), + new PickSlideEffectInfo(), + new LeftHandTapEffectInfo(), + new AlternateEndingsEffectInfo() + ]), + new ScoreBarRendererFactory(), + new EffectBarRendererFactory('score-bottom-effects', [ + new CrescendoEffectInfo(), + new OttaviaEffectInfo(false), + new DynamicsEffectInfo(), + new LyricsEffectInfo() + ]) + ]); + let tabEffectInfos: EffectBarRendererInfo[] = [ + new TempoEffectInfo(), + new TripletFeelEffectInfo(), + new MarkerEffectInfo(), + new TextEffectInfo(), + new ChordsEffectInfo(), + new FermataEffectInfo(), + new TrillEffectInfo(), + new WideBeatVibratoEffectInfo(), + new SlightBeatVibratoEffectInfo(), + new WideNoteVibratoEffectInfo(), + new SlightNoteVibratoEffectInfo(), + new TapEffectInfo(), + new FadeInEffectInfo(), + new HarmonicsEffectInfo(HarmonicType.Artificial), + new HarmonicsEffectInfo(HarmonicType.Pinch), + new HarmonicsEffectInfo(HarmonicType.Tap), + new HarmonicsEffectInfo(HarmonicType.Semi), + new HarmonicsEffectInfo(HarmonicType.Feedback), + new LetRingEffectInfo(), + new CapoEffectInfo(), + new FingeringEffectInfo(), + new PalmMuteEffectInfo(), + new PickStrokeEffectInfo(), + new PickSlideEffectInfo(), + new LeftHandTapEffectInfo(), + new AlternateEndingsEffectInfo() + ]; + staveProfiles.set(StaveProfile.Tab, [ + new EffectBarRendererFactory('tab-effects', tabEffectInfos), + new TabBarRendererFactory(true, true, true), + new EffectBarRendererFactory('tab-bottom-effects', [new LyricsEffectInfo()]) + ]); + staveProfiles.set(StaveProfile.TabMixed, [ + new EffectBarRendererFactory('tab-effects', tabEffectInfos), + new TabBarRendererFactory(false, false, false), + new EffectBarRendererFactory('tab-bottom-effects', [new LyricsEffectInfo()]) + ]); + + return staveProfiles; + } + + private static createDefaultLayoutEngines(): Map { + const engines = new Map(); + // default layout engines + engines.set( + LayoutMode.Page, + new LayoutEngineFactory(true, r => { + return new PageViewLayout(r); + }) + ); + engines.set( + LayoutMode.Horizontal, + new LayoutEngineFactory(false, r => { + return new HorizontalScreenLayout(r); + }) + ); + return engines; + } + + /** + * @target web + */ + public static platformInit(): void { + Environment.registerJQueryPlugin(); + // polyfills + Math.log2 = Math.log2 + ? Math.log2 + : function (x) { + return Math.log(x) * Math.LOG2E; + }; + + if (!Environment.isRunningInWorker) { + let vbAjaxLoader: string = ''; + vbAjaxLoader += 'Function VbAjaxLoader(method, fileName)' + '\r\n'; + vbAjaxLoader += ' Dim xhr' + '\r\n'; + vbAjaxLoader += ' Set xhr = CreateObject("Microsoft.XMLHTTP")' + '\r\n'; + vbAjaxLoader += ' xhr.Open method, fileName, False' + '\r\n'; + vbAjaxLoader += ' xhr.setRequestHeader "Accept-Charset", "x-user-defined"' + '\r\n'; + vbAjaxLoader += ' xhr.send' + '\r\n'; + vbAjaxLoader += ' Dim byteArray()' + '\r\n'; + vbAjaxLoader += ' if xhr.Status = 200 Then' + '\r\n'; + vbAjaxLoader += ' Dim byteString' + '\r\n'; + vbAjaxLoader += ' Dim i' + '\r\n'; + vbAjaxLoader += ' byteString=xhr.responseBody' + '\r\n'; + vbAjaxLoader += ' ReDim byteArray(LenB(byteString))' + '\r\n'; + vbAjaxLoader += ' For i = 1 To LenB(byteString)' + '\r\n'; + vbAjaxLoader += ' byteArray(i-1) = AscB(MidB(byteString, i, 1))' + '\r\n'; + vbAjaxLoader += ' Next' + '\r\n'; + vbAjaxLoader += ' End If' + '\r\n'; + vbAjaxLoader += ' VbAjaxLoader=byteArray' + '\r\n'; + vbAjaxLoader += 'End Function' + '\r\n'; + let vbAjaxLoaderScript: HTMLScriptElement = document.createElement('script') as HTMLScriptElement; + vbAjaxLoaderScript.setAttribute('type', 'text/vbscript'); + let inlineScript: Node = document.createTextNode(vbAjaxLoader); + vbAjaxLoaderScript.appendChild(inlineScript); + document.addEventListener( + 'DOMContentLoaded', + () => { + document.body.appendChild(vbAjaxLoaderScript); + }, + false + ); + } else { + AlphaTabWebWorker.init(); + AlphaSynthWebWorker.init(); + } + } +} + +Environment.platformInit(); diff --git a/src/EventEmitter.ts b/src/EventEmitter.ts new file mode 100644 index 000000000..3300d9cb9 --- /dev/null +++ b/src/EventEmitter.ts @@ -0,0 +1,44 @@ +export interface IEventEmitter { + on(value: () => void): void; + off(value: () => void): void; +} +export interface IEventEmitterOfT { + on(value: (arg: T) => void): void; + off(value: (arg: T) => void): void; +} + +export class EventEmitter implements IEventEmitter { + private _listeners: (() => void)[] = []; + + public on(value: () => void): void { + this._listeners.push(value); + } + + public off(value: () => void): void { + this._listeners = this._listeners.filter(l => l !== value); + } + + public trigger(): void { + for (const l of this._listeners) { + l(); + } + } +} + +export class EventEmitterOfT implements IEventEmitterOfT { + private _listeners: ((arg: T) => void)[] = []; + + public on(value: (arg: T) => void): void { + this._listeners.push(value); + } + + public off(value: (arg: T) => void): void { + this._listeners = this._listeners.filter(l => l !== value); + } + + public trigger(arg: T): void { + for (const l of this._listeners) { + l(arg); + } + } +} diff --git a/src/FileLoadError.ts b/src/FileLoadError.ts new file mode 100644 index 000000000..ca1282823 --- /dev/null +++ b/src/FileLoadError.ts @@ -0,0 +1,14 @@ +import { AlphaTabError, AlphaTabErrorType } from "./AlphaTabError"; + +/** + * @target web + */ +export class FileLoadError extends AlphaTabError { + public xhr: XMLHttpRequest; + + public constructor(message: string, xhr: XMLHttpRequest) { + super(AlphaTabErrorType.General, message); + this.xhr = xhr; + Object.setPrototypeOf(this, FileLoadError.prototype); + } +} diff --git a/src/FormatError.ts b/src/FormatError.ts new file mode 100644 index 000000000..8f5802f3f --- /dev/null +++ b/src/FormatError.ts @@ -0,0 +1,11 @@ +import { AlphaTabError, AlphaTabErrorType} from "@src/AlphaTabError"; + +/** + * An invalid input format was detected (e.g. invalid setting values, file formats,...) + */ +export class FormatError extends AlphaTabError { + public constructor(message: string) { + super(AlphaTabErrorType.Format, message); + Object.setPrototypeOf(this, FormatError.prototype); + } +} diff --git a/src/ImporterSettings.ts b/src/ImporterSettings.ts new file mode 100644 index 000000000..85d7b1d3f --- /dev/null +++ b/src/ImporterSettings.ts @@ -0,0 +1,15 @@ +/** + * All settings related to importers that decode file formats. + * @json + */ +export class ImporterSettings { + /** + * The text encoding to use when decoding strings. By default UTF-8 is used. + */ + public encoding: string = 'utf-8'; + + /** + * If part-groups should be merged into a single track. + */ + public mergePartGroupsInMusicXml: boolean = false; +} diff --git a/src/LogLevel.ts b/src/LogLevel.ts new file mode 100644 index 000000000..2200bc17e --- /dev/null +++ b/src/LogLevel.ts @@ -0,0 +1,26 @@ +/** + * Defines all loglevels. + * @json + */ +export enum LogLevel { + /** + * No logging + */ + None, + /** + * Debug level (internal details are displayed). + */ + Debug, + /** + * Info level (only important details are shown) + */ + Info, + /** + * Warning level + */ + Warning, + /** + * Error level. + */ + Error +} \ No newline at end of file diff --git a/src/Logger.ts b/src/Logger.ts new file mode 100644 index 000000000..e80def20f --- /dev/null +++ b/src/Logger.ts @@ -0,0 +1,65 @@ +import { LogLevel } from '@src/LogLevel'; + +export interface ILogger { + debug(category: string, msg: string, ...details: unknown[]): void; + warning(category: string, msg: string, ...details: unknown[]): void; + info(category: string, msg: string, ...details: unknown[]): void; + error(category: string, msg: string, ...details: unknown[]): void; +} + +export class ConsoleLogger implements ILogger { + public static logLevel: LogLevel = LogLevel.Info; + + private static format(category: string, msg: string): string { + return `[AlphaTab][${category}] ${msg}`; + } + + public debug(category: string, msg: string, ...details: unknown[]): void { + console.debug(ConsoleLogger.format(category, msg), ...details); + } + + public warning(category: string, msg: string, ...details: unknown[]): void { + console.warn(ConsoleLogger.format(category, msg), ...details); + } + + public info(category: string, msg: string, ...details: unknown[]): void { + console.info(ConsoleLogger.format(category, msg), ...details); + } + + public error(category: string, msg: string, ...details: unknown[]): void { + console.error(ConsoleLogger.format(category, msg), ...details); + } +} + +export class Logger { + public static logLevel: LogLevel = LogLevel.Info; + public static log:ILogger = new ConsoleLogger(); + + private static shouldLog(level: LogLevel): boolean { + return Logger.logLevel !== LogLevel.None && level >= Logger.logLevel; + } + + public static debug(category: string, msg: string, ...details: unknown[]): void { + if (Logger.shouldLog(LogLevel.Debug)) { + Logger.log.debug(category, msg, ...details); + } + } + + public static warning(category: string, msg: string, ...details: unknown[]): void { + if (Logger.shouldLog(LogLevel.Warning)) { + Logger.log.warning(category, msg, ...details); + } + } + + public static info(category: string, msg: string, ...details: unknown[]): void { + if (Logger.shouldLog(LogLevel.Info)) { + Logger.log.info(category, msg, ...details); + } + } + + public static error(category: string, msg: string, ...details: unknown[]): void { + if (Logger.shouldLog(LogLevel.Error)) { + Logger.log.error(category, msg, ...details); + } + } +} diff --git a/src/NotationSettings.ts b/src/NotationSettings.ts new file mode 100644 index 000000000..f8024c2f1 --- /dev/null +++ b/src/NotationSettings.ts @@ -0,0 +1,382 @@ +/** + * Lists the different modes on how rhythm notation is shown on the tab staff. + */ +export enum TabRhythmMode { + /** + * Rhythm notation is hidden. + */ + Hidden, + /** + * Rhythm notation is shown with individual beams per beat. + */ + ShowWithBeams, + /** + * Rhythm notation is shown and behaves like normal score notation with connected bars. + */ + ShowWithBars +} + +/** + * Lists all modes on how fingerings should be displayed. + */ +export enum FingeringMode { + /** + * Fingerings will be shown in the standard notation staff. + */ + ScoreDefault, + /** + * Fingerings will be shown in the standard notation staff. Piano finger style is enforced, where + * fingers are rendered as 1-5 instead of p,i,m,a,c and T,1,2,3,4. + */ + ScoreForcePiano, + /** + * Fingerings will be shown in a effect band above the tabs in case + * they have only a single note on the beat. + */ + SingleNoteEffectBand, + /** + * Fingerings will be shown in a effect band above the tabs in case + * they have only a single note on the beat. Piano finger style is enforced, where + * fingers are rendered as 1-5 instead of p,i,m,a,c and T,1,2,3,4. + */ + SingleNoteEffectBandForcePiano +} + +/** + * Lists all modes on how alphaTab can handle the display and playback of music notation. + */ +export enum NotationMode { + /** + * Music elements will be displayed and played as in Guitar Pro. + */ + GuitarPro, + + /** + * Music elements will be displayed and played as in traditional songbooks. + * Changes: + * 1. Bends + * For bends additional grace beats are introduced. + * Bends are categorized into gradual and fast bends. + * - Gradual bends are indicated by beat text "grad" or "grad.". Bend will sound along the beat duration. + * - Fast bends are done right before the next note. If the next note is tied even on-beat of the next note. + * 2. Whammy Bars + * Dips are shown as simple annotation over the beats + * Whammy Bars are categorized into gradual and fast. + * - Gradual whammys are indicated by beat text "grad" or "grad.". Whammys will sound along the beat duration. + * - Fast whammys are done right the beat. + * 3. Let Ring + * Tied notes with let ring are not shown in standard notation + * Let ring does not cause a longer playback, duration is defined via tied notes. + */ + SongBook +} + +/** + * Lists all major music notation elements that are part + * of the music sheet and can be dynamically controlled to be shown + * or hidden. + */ +export enum NotationElement { + /** + * The score title shown at the start of the music sheet. + */ + ScoreTitle, + + /** + * The score subtitle shown at the start of the music sheet. + */ + ScoreSubTitle, + + /** + * The score artist shown at the start of the music sheet. + */ + ScoreArtist, + + /** + * The score album shown at the start of the music sheet. + */ + ScoreAlbum, + + /** + * The score words author shown at the start of the music sheet. + */ + ScoreWords, + + /** + * The score music author shown at the start of the music sheet. + */ + ScoreMusic, + + /** + * The score words&music author shown at the start of the music sheet. + */ + ScoreWordsAndMusic, + + /** + * The score copyright owner shown at the start of the music sheet. + */ + ScoreCopyright, + + /** + * The tuning information of the guitar shown + * above the staves. + */ + GuitarTuning, + + /** + * The track names which are shown in the accolade. + */ + TrackNames, + + /** + * The chord diagrams for guitars. Usually shown + * below the score info. + */ + ChordDiagrams, + + /** + * Parenthesis that are shown for tied bends + * if they are preceeded by bends. + */ + ParenthesisOnTiedBends, + + /** + * The tab number for tied notes if the + * bend of a note is increased at that point. + */ + TabNotesOnTiedBends, + + /** + * Zero tab numbers on "dive whammys". + */ + ZerosOnDiveWhammys, + + /** + * The alternate endings information on repeats shown above the staff. + */ + EffectAlternateEndings, + + /** + * The information about the fret on which the capo is placed shown above the staff. + */ + EffectCapo, + + /** + * The chord names shown above beats shown above the staff. + */ + EffectChordNames, + + /** + * The crescendo/decrescendo angle shown above the staff. + */ + EffectCrescendo, + + /** + * The beat dynamics shown above the staff. + */ + EffectDynamics, + + /** + * The curved angle for fade in/out effects shown above the staff. + */ + EffectFadeIn, + + /** + * The fermata symbol shown above the staff. + */ + EffectFermata, + + /** + * The fingering information. + */ + EffectFingering, + + /** + * The harmonics names shown above the staff. + * (does not represent the harmonic note heads) + */ + EffectHarmonics, + + /** + * The let ring name and line above the staff. + */ + EffectLetRing, + + /** + * The lyrics of the track shown above the staff. + */ + EffectLyrics, + + /** + * The section markers shown above the staff. + */ + EffectMarker, + + /** + * The ottava symbol and lines shown above the staff. + */ + EffectOttavia, + + /** + * The palm mute name and line shown above the staff. + */ + EffectPalmMute, + + /** + * The pick slide information shown above the staff. + * (does not control the pick slide lines) + */ + EffectPickSlide, + + /** + * The pick stroke symbols shown above the staff. + */ + EffectPickStroke, + + /** + * The slight beat vibrato waves shown above the staff. + */ + EffectSlightBeatVibrato, + + /** + * The slight note vibrato waves shown above the staff. + */ + EffectSlightNoteVibrato, + + /** + * The tap/slap/pop effect names shown above the staff. + */ + EffectTap, + + /** + * The tempo information shown above the staff. + */ + EffectTempo, + + /** + * The additional beat text shown above the staff. + */ + EffectText, + + /** + * The trill name and waves shown above the staff. + */ + EffectTrill, + + /** + * The triplet feel symbol shown above the staff. + */ + EffectTripletFeel, + + /** + * The whammy bar information shown above the staff. + * (does not control the whammy lines shown within the staff) + */ + EffectWhammyBar, + + /** + * The wide beat vibrato waves shown above the staff. + */ + EffectWideBeatVibrato, + + /** + * The wide note vibrato waves shown above the staff. + */ + EffectWideNoteVibrato, + + /** + * The left hand tap symbol shown above the staff. + */ + EffectLeftHandTap +} + +/** + * The notation settings control how various music notation elements are shown and behaving + * @json + */ +export class NotationSettings { + /** + * Gets or sets the mode to use for display and play music notation elements. + */ + public notationMode: NotationMode = NotationMode.GuitarPro; + + /** + * Gets or sets the fingering mode to use. + */ + public fingeringMode: FingeringMode = FingeringMode.ScoreDefault; + + /** + * Gets or sets the configuration on whether music notation elements are visible or not. + * If notation elements are not specified, the default configuration will be applied. + */ + public elements: Map = new Map(); + + /** + * Gets the default configuration of the {@see notationElements} setting. Do not modify + * this map as it might not result in the expected side effects. + * If items are not listed explicitly in this list, they are considered visible. + */ + public static defaultElements: Map = new Map([ + [NotationElement.ZerosOnDiveWhammys, false] + ]); + + /** + * Whether to show rhythm notation in the guitar tablature. + */ + public rhythmMode: TabRhythmMode = TabRhythmMode.Hidden; + + /** + * The height of the rythm bars. + */ + public rhythmHeight: number = 15; + + /** + * The transposition pitch offsets for the individual tracks. + * They apply to rendering and playback. + */ + public transpositionPitches: number[] = []; + + /** + * The transposition pitch offsets for the individual tracks. + * They apply to rendering only. + */ + public displayTranspositionPitches: number[] = []; + + /** + * If set to true the guitar tabs on grace beats are rendered smaller. + */ + public smallGraceTabNotes: boolean = true; + + /** + * If set to true bend arrows expand to the end of the last tied note + * of the string. Otherwise they end on the next beat. + */ + public extendBendArrowsOnTiedNotes: boolean = true; + + /** + * If set to true, line effects (like w/bar, let-ring etc) + * are drawn until the end of the beat instead of the start. + */ + public extendLineEffectsToBeatEnd: boolean = false; + + /** + * Gets or sets the height for slurs. The factor is multiplied with the a logarithmic distance + * between slur start and end. + */ + public slurHeight: number = 5.0; + + /** + * Gets whether the given music notation element should be shown + * @param element the element to check + * @returns true if the element should be shown, otherwise false. + */ + public isNotationElementVisible(element: NotationElement): boolean { + if (this.elements.has(element)) { + return this.elements.get(element)!; + } + if (NotationSettings.defaultElements.has(element)) { + return NotationSettings.defaultElements.get(element)!; + } + return true; + } +} diff --git a/src/PlayerSettings.ts b/src/PlayerSettings.ts new file mode 100644 index 000000000..7851d250a --- /dev/null +++ b/src/PlayerSettings.ts @@ -0,0 +1,168 @@ +/** + * Lists all modes how alphaTab can scroll the container during playback. + */ +export enum ScrollMode { + /** + * Do not scroll automatically + */ + Off, + /** + * Scrolling happens as soon the offsets of the cursors change. + */ + Continuous, + /** + * Scrolling happens as soon the cursors exceed the displayed range. + */ + OffScreen +} + +/** + * This object defines the details on how to generate the vibrato effects. + * @json + */ +export class VibratoPlaybackSettings { + /** + * Gets or sets the wavelength of the note-wide vibrato in midi ticks. + */ + public noteWideLength: number = 480; + + /** + * Gets or sets the amplitude for the note-wide vibrato in semitones. + */ + public noteWideAmplitude: number = 2; + + /** + * Gets or sets the wavelength of the note-slight vibrato in midi ticks. + */ + public noteSlightLength: number = 480; + + /** + * Gets or sets the amplitude for the note-slight vibrato in semitones. + */ + public noteSlightAmplitude: number = 2; + + /** + * Gets or sets the wavelength of the beat-wide vibrato in midi ticks. + */ + public beatWideLength: number = 240; + + /** + * Gets or sets the amplitude for the beat-wide vibrato in semitones. + */ + public beatWideAmplitude: number = 3; + + /** + * Gets or sets the wavelength of the beat-slight vibrato in midi ticks. + */ + public beatSlightLength: number = 240; + + /** + * Gets or sets the amplitude for the beat-slight vibrato in semitones. + */ + public beatSlightAmplitude: number = 3; +} + +/** + * This object defines the details on how to generate the slide effects. + * @json + */ +export class SlidePlaybackSettings { + /** + * Gets or sets 1/4 tones (bend value) offset that + * simple slides like slide-out-below or slide-in-above use. + */ + public simpleSlidePitchOffset: number = 6; + + /** + * Gets or sets the percentage which the simple slides should take up + * from the whole note. for "slide into" effects the slide will take place + * from time 0 where the note is plucked to 25% of the overall note duration. + * For "slide out" effects the slide will start 75% and finish at 100% of the overall + * note duration. + */ + public simpleSlideDurationRatio: number = 0.25; + + /** + * Gets or sets the percentage which the legato and shift slides should take up + * from the whole note. For a value 0.5 the sliding will start at 50% of the overall note duration + * and finish at 100% + */ + public shiftSlideDurationRatio: number = 0.5; +} + +/** + * The player settings control how the audio playback and UI is behaving. + * @json + */ +export class PlayerSettings { + /** + * Gets or sets the URl of the sound font to be loaded. + */ + public soundFont: string | null = null; + + /** + * Gets or sets the element that should be used for scrolling. + */ + public scrollElement: string = 'html,body'; + + /** + * Gets or sets whether the player should be enabled. + */ + public enablePlayer: boolean = false; + + /** + * Gets or sets whether playback cursors should be displayed. + */ + public enableCursor: boolean = true; + + /** + * Gets or sets alphaTab should provide user interaction features to + * select playback ranges and jump to the playback position by click (aka. seeking). + */ + public enableUserInteraction: boolean = true; + + /** + * Gets or sets the X-offset to add when scrolling. + */ + public scrollOffsetX: number = 0; + + /** + * Gets or sets the Y-offset to add when scrolling + */ + public scrollOffsetY: number = 0; + + /** + * Gets or sets the mode how to scroll. + */ + public scrollMode: ScrollMode = ScrollMode.Continuous; + + /** + * Gets or sets how fast the scrolling to the new position should happen (in milliseconds) + */ + public scrollSpeed: number = 300; + + /** + * Gets or sets the bend duration in milliseconds for songbook bends. + */ + public songBookBendDuration: number = 75; + + /** + * Gets or sets the duration of whammy dips in milliseconds for songbook whammys. + */ + public songBookDipDuration: number = 150; + + /** + * Gets or sets the settings on how the vibrato audio is generated. + */ + public readonly vibrato: VibratoPlaybackSettings = new VibratoPlaybackSettings(); + + /** + * Gets or sets the setitngs on how the slide audio is generated. + */ + public readonly slide: SlidePlaybackSettings = new SlidePlaybackSettings(); + + /** + * Gets or sets whether the triplet feel should be applied/played during audio playback. + */ + public playTripletFeel: boolean = true; +} diff --git a/src/ProgressEventArgs.ts b/src/ProgressEventArgs.ts new file mode 100644 index 000000000..1497f0a47 --- /dev/null +++ b/src/ProgressEventArgs.ts @@ -0,0 +1,24 @@ +/** + * Represents the progress of any data being loaded. + */ +export class ProgressEventArgs { + /** + * Gets the currently loaded bytes. + */ + public readonly loaded: number; + + /** + * Gets the total number of bytes to load. + */ + public readonly total: number; + + /** + * Initializes a new instance of the {@link ProgressEventArgs} class. + * @param loaded + * @param total + */ + public constructor(loaded: number, total: number) { + this.loaded = loaded; + this.total = total; + } +} diff --git a/src/RenderingResources.ts b/src/RenderingResources.ts new file mode 100644 index 000000000..cf1b652f5 --- /dev/null +++ b/src/RenderingResources.ts @@ -0,0 +1,96 @@ +import { Color } from '@src/model/Color'; +import { Font, FontStyle } from '@src/model/Font'; + +/** + * This public class contains central definitions for controlling the visual appearance. + * @json + */ +export class RenderingResources { + private static sansFont: string = 'Arial'; + private static serifFont: string = 'Georgia'; + + /** + * Gets or sets the font to use for displaying the songs copyright information in the header of the music sheet. + */ + public copyrightFont: Font = new Font(RenderingResources.sansFont, 12, FontStyle.Bold); + + /** + * Gets or sets the font to use for displaying the songs title in the header of the music sheet. + */ + public titleFont: Font = new Font(RenderingResources.serifFont, 32, FontStyle.Plain); + + /** + * Gets or sets the font to use for displaying the songs subtitle in the header of the music sheet. + */ + public subTitleFont: Font = new Font(RenderingResources.serifFont, 20, FontStyle.Plain); + + /** + * Gets or sets the font to use for displaying the lyrics information in the header of the music sheet. + */ + public wordsFont: Font = new Font(RenderingResources.serifFont, 15, FontStyle.Plain); + + /** + * Gets or sets the font to use for displaying certain effect related elements in the music sheet. + */ + public effectFont: Font = new Font(RenderingResources.serifFont, 12, FontStyle.Italic); + + /** + * Gets or sets the font to use for displaying the fretboard numbers in chord diagrams. + */ + public fretboardNumberFont: Font = new Font(RenderingResources.sansFont, 11, FontStyle.Plain); + + /** + * Gets or sets the font to use for displaying the guitar tablature numbers in the music sheet. + */ + public tablatureFont: Font = new Font(RenderingResources.sansFont, 13, FontStyle.Plain); + + /** + * Gets or sets the font to use for grace notation related texts in the music sheet. + */ + public graceFont: Font = new Font(RenderingResources.sansFont, 11, FontStyle.Plain); + + /** + * Gets or sets the color to use for rendering the lines of staves. + */ + public staffLineColor: Color = new Color(165, 165, 165, 0xff); + + /** + * Gets or sets the color to use for rendering bar separators, the accolade and repeat signs. + */ + public barSeparatorColor: Color = new Color(34, 34, 17, 0xff); + + /** + * Gets or sets the font to use for displaying the bar numbers above the music sheet. + */ + public barNumberFont: Font = new Font(RenderingResources.sansFont, 11, FontStyle.Plain); + + /** + * Gets or sets the color to use for displaying the bar numbers above the music sheet. + */ + public barNumberColor: Color = new Color(200, 0, 0, 0xff); + + /** + * Gets or sets the font to use for displaying finger information in the music sheet. + */ + public fingeringFont: Font = new Font(RenderingResources.serifFont, 14, FontStyle.Plain); + + /** + * Gets or sets the font to use for section marker labels shown above the music sheet. + */ + public markerFont: Font = new Font(RenderingResources.serifFont, 14, FontStyle.Bold); + + /** + * Gets or sets the color to use for music notation elements of the primary voice. + */ + public mainGlyphColor: Color = new Color(0, 0, 0, 0xff); + + /** + * Gets or sets the color to use for music notation elements of the secondary voices. + */ + public secondaryGlyphColor: Color = new Color(0, 0, 0, 100); + + /** + * Gets or sets the color to use for displaying the song information above the music sheet. + */ + public scoreInfoColor: Color = new Color(0, 0, 0, 0xff); +} diff --git a/src/ResizeEventArgs.ts b/src/ResizeEventArgs.ts new file mode 100644 index 000000000..fafdfa0c2 --- /dev/null +++ b/src/ResizeEventArgs.ts @@ -0,0 +1,34 @@ +import { Settings } from '@src/Settings'; +import { CoreSettings } from '@src/CoreSettings'; + +/** + * Represents the information related to a resize event. + */ +export class ResizeEventArgs { + /** + * Gets the size before the resizing happened. + */ + public oldWidth: number = 0; + + /** + * Gets the size after the resize was complete. + */ + public newWidth: number = 0; + + /** + * Gets the settings currently used for rendering. + */ + public settings: Settings | null = null; + + public core() : CoreSettings { + if(this.settings && this.causeIssue()) { + return this.settings.core; + } + return new CoreSettings(); + } + + private causeIssue() { + this.settings = null; + return true; + } +} diff --git a/src/Settings.ts b/src/Settings.ts new file mode 100644 index 000000000..0d1942ab1 --- /dev/null +++ b/src/Settings.ts @@ -0,0 +1,95 @@ +import { CoreSettings } from '@src/CoreSettings'; +import { DisplaySettings } from '@src/DisplaySettings'; +import { ImporterSettings } from '@src/ImporterSettings'; +import { FingeringMode, NotationMode, NotationSettings, NotationElement } from '@src/NotationSettings'; +import { PlayerSettings } from '@src/PlayerSettings'; + +/** + * This public class contains instance specific settings for alphaTab + * @json + */ +export class Settings { + /** + * @target web + */ + public static fromJson(json: any): Settings { + // dynamically implemented via AST transformer + return new Settings(); + } + + /** + * @target web + */ + public fillFromJson(json: any): void { + // dynamically implemented via AST transformer + } + + /** + * @target web + */ + public static toJson(settings: Settings): unknown { + // dynamically implemented via AST transformer + return null; + } + + /** + * @target web + */ + public setProperty(property: string, value: any): boolean { + // dynamically implemented via macro + return false; + } + + /** + * @target web + */ + public fillFromDataAttributes(dataAttributes: Map): void { + dataAttributes.forEach((v, k) => { + this.setProperty(k.toLowerCase(), v); + }); + } + + /** + * The core settings control the general behavior of alphatab like + * what modules are active. + * @json_on_parent + */ + public readonly core: CoreSettings = new CoreSettings(); + + /** + * The display settings control how the general layout and display of alphaTab is done. + * @json_on_parent + */ + public readonly display: DisplaySettings = new DisplaySettings(); + + /** + * The notation settings control how various music notation elements are shown and behaving. + */ + public readonly notation: NotationSettings = new NotationSettings(); + + /** + * All settings related to importers that decode file formats. + */ + public readonly importer: ImporterSettings = new ImporterSettings(); + + /** + * Contains all player related settings + */ + public readonly player: PlayerSettings = new PlayerSettings(); + + public setSongBookModeSettings(): void { + this.notation.notationMode = NotationMode.SongBook; + this.notation.smallGraceTabNotes = false; + this.notation.fingeringMode = FingeringMode.SingleNoteEffectBand; + this.notation.extendBendArrowsOnTiedNotes = false; + this.notation.elements.set(NotationElement.ParenthesisOnTiedBends, false); + this.notation.elements.set(NotationElement.TabNotesOnTiedBends, false); + this.notation.elements.set(NotationElement.ZerosOnDiveWhammys, true); + } + + public static get songBook(): Settings { + let settings: Settings = new Settings(); + settings.setSongBookModeSettings(); + return settings; + } +} diff --git a/src/alphatab.ts b/src/alphatab.ts new file mode 100644 index 000000000..840701fc9 --- /dev/null +++ b/src/alphatab.ts @@ -0,0 +1,298 @@ +if (!('WorkerGlobalScope' in self)) { + if (!Element.prototype.matches) { + Element.prototype.matches = + (Element.prototype as any).msMatchesSelector || Element.prototype.webkitMatchesSelector; + } + + if (!Element.prototype.closest) { + Element.prototype.closest = function (s: string) { + let el: any = this; + do { + if (Element.prototype.matches.call(el, s)) return el; + el = el.parentElement || el.parentNode; + } while (el !== null && el.nodeType === 1); + return null; + }; + } + + if (window.NodeList && !NodeList.prototype.forEach) { + (NodeList.prototype as any).forEach = Array.prototype.forEach; + } +} + +if (!String.prototype.startsWith) { + String.prototype.startsWith = function (searchString, position) { + position = position || 0; + return this.indexOf(searchString, position) === position; + }; +} + +// https://tc39.github.io/ecma262/#sec-array.prototype.find +if (!Array.prototype.find) { + Object.defineProperty(Array.prototype, 'find', { + value: function (predicate: any) { + // 1. Let O be ? ToObject(this value). + if (this == null) { + throw new TypeError('"this" is null or not defined'); + } + + let o = Object(this); + + // 2. Let len be ? ToLength(? Get(O, "length")). + let len = o.length >>> 0; + + // 3. If IsCallable(predicate) is false, throw a TypeError exception. + if (typeof predicate !== 'function') { + throw new TypeError('predicate must be a function'); + } + + // 4. If thisArg was supplied, let T be thisArg; else let T be undefined. + let thisArg = arguments[1]; + + // 5. Let k be 0. + let k = 0; + + // 6. Repeat, while k < len + while (k < len) { + // a. Let Pk be ! ToString(k). + // b. Let kValue be ? Get(O, Pk). + // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)). + // d. If testResult is true, return kValue. + let kValue = o[k]; + if (predicate.call(thisArg, kValue, k, o)) { + return kValue; + } + // e. Increase k by 1. + k++; + } + + // 7. Return undefined. + return undefined; + }, + configurable: true, + writable: true + }); +} + +import { CoreSettings } from '@src/CoreSettings'; +import { DisplaySettings, LayoutMode, StaveProfile } from '@src/DisplaySettings'; +import { ImporterSettings } from '@src/ImporterSettings'; +import { FingeringMode, NotationMode, NotationSettings, TabRhythmMode } from '@src/NotationSettings'; +import { PlayerSettings, ScrollMode, VibratoPlaybackSettings } from '@src/PlayerSettings'; +import { ProgressEventArgs } from '@src/ProgressEventArgs'; +import { RenderingResources } from '@src/RenderingResources'; +import { ResizeEventArgs } from '@src/ResizeEventArgs'; +import { Settings } from '@src/Settings'; +import { AlphaTabError, AlphaTabErrorType } from '@src/AlphaTabError'; +import { FormatError } from '@src/FormatError'; +import { LogLevel } from '@src/LogLevel'; +import { Logger } from '@src/Logger'; +import { FileLoadError } from '@src/FileLoadError'; + +import { AlphaTabApi } from '@src/platform/javascript/AlphaTabApi'; + +export { + AlphaTabApi, + AlphaTabError, + AlphaTabErrorType, + FileLoadError, + CoreSettings, + StaveProfile, + LayoutMode, + DisplaySettings, + FormatError, + ImporterSettings, + TabRhythmMode, + FingeringMode, + NotationMode, + NotationSettings, + ScrollMode, + VibratoPlaybackSettings, + PlayerSettings, + ProgressEventArgs, + RenderingResources, + ResizeEventArgs, + Settings, + LogLevel, + Logger +}; + +import { ScoreImporter } from '@src/importer/ScoreImporter'; +import { ScoreLoader } from '@src/importer/ScoreLoader'; +import { UnsupportedFormatError } from '@src/importer/UnsupportedFormatError'; + +export const importer = { + ScoreImporter, + ScoreLoader, + UnsupportedFormatError +}; + +import { BeatTickLookup } from '@src/midi/BeatTickLookup'; +import { MasterBarTickLookup } from '@src/midi/MasterBarTickLookup'; +import { MidiTickLookup, MidiTickLookupFindBeatResult } from '@src/midi/MidiTickLookup'; +import { MidiFile } from '@src/midi/MidiFile'; +import { ControllerType } from '@src/midi/ControllerType'; +import { MetaDataEvent } from '@src/midi/MetaDataEvent'; +import { MetaEvent, MetaEventType } from '@src/midi/MetaEvent'; +import { MetaNumberEvent } from '@src/midi/MetaNumberEvent'; +import { MidiEvent, MidiEventType } from '@src/midi/MidiEvent'; +import { SystemCommonEvent, SystemCommonType } from '@src/midi/SystemCommonEvent'; +import { SystemExclusiveEvent } from '@src/midi/SystemExclusiveEvent'; +import { MidiFileGenerator } from '@src/midi/MidiFileGenerator'; +import { AlphaSynthMidiFileHandler } from '@src/midi/AlphaSynthMidiFileHandler'; + +export const midi = { + BeatTickLookup, + MasterBarTickLookup, + MidiTickLookup, + MidiTickLookupFindBeatResult, + MidiFile, + ControllerType, + MetaDataEvent, + MetaEvent, + MetaEventType, + MetaNumberEvent, + MidiEvent, + MidiEventType, + SystemCommonEvent, + SystemCommonType, + SystemExclusiveEvent, + MidiFileGenerator, + AlphaSynthMidiFileHandler +}; + +import { AccentuationType } from '@src/model/AccentuationType'; +import { AccidentalType } from '@src/model/AccidentalType'; +import { AutomationType, Automation } from '@src/model/Automation'; +import { Bar } from '@src/model/Bar'; +import { Beat } from '@src/model/Beat'; +import { BendPoint } from '@src/model/BendPoint'; +import { BendStyle } from '@src/model/BendStyle'; +import { BendType } from '@src/model/BendType'; +import { BrushType } from '@src/model/BrushType'; +import { Chord } from '@src/model/Chord'; +import { Clef } from '@src/model/Clef'; +import { Color } from '@src/model/Color'; +import { CrescendoType } from '@src/model/CrescendoType'; +import { Duration } from '@src/model/Duration'; +import { DynamicValue } from '@src/model/DynamicValue'; +import { FermataType, Fermata } from '@src/model/Fermata'; +import { Fingers } from '@src/model/Fingers'; +import { FontStyle, Font } from '@src/model/Font'; +import { GraceType } from '@src/model/GraceType'; +import { HarmonicType } from '@src/model/HarmonicType'; +import { JsonConverter } from '@src/model/JsonConverter'; +import { KeySignature } from '@src/model/KeySignature'; +import { KeySignatureType } from '@src/model/KeySignatureType'; +import { Lyrics } from '@src/model/Lyrics'; +import { MasterBar } from '@src/model/MasterBar'; +import { Note } from '@src/model/Note'; +import { NoteAccidentalMode } from '@src/model/NoteAccidentalMode'; +import { Ottavia } from '@src/model/Ottavia'; +import { PickStroke } from '@src/model/PickStroke'; +import { PlaybackInformation } from '@src/model/PlaybackInformation'; +import { RenderStylesheet } from '@src/model/RenderStylesheet'; +import { RepeatGroup } from '@src/model/RepeatGroup'; +import { Score } from '@src/model/Score'; +import { Section } from '@src/model/Section'; +import { SimileMark } from '@src/model/SimileMark'; +import { SlideInType } from '@src/model/SlideInType'; +import { SlideOutType } from '@src/model/SlideOutType'; +import { Staff } from '@src/model/Staff'; +import { Track } from '@src/model/Track'; +import { TripletFeel } from '@src/model/TripletFeel'; +import { Tuning } from '@src/model/Tuning'; +import { TupletGroup } from '@src/model/TupletGroup'; +import { VibratoType } from '@src/model/VibratoType'; +import { Voice } from '@src/model/Voice'; +import { WhammyType } from '@src/model/WhammyType'; + +export const model = { + AccentuationType, + AccidentalType, + AutomationType, + Automation, + Bar, + Beat, + BendPoint, + BendStyle, + BendType, + BrushType, + Chord, + Clef, + Color, + CrescendoType, + Duration, + DynamicValue, + FermataType, + Fermata, + Fingers, + FontStyle, + Font, + GraceType, + HarmonicType, + JsonConverter, + KeySignature, + KeySignatureType, + Lyrics, + MasterBar, + Note, + NoteAccidentalMode, + Ottavia, + PickStroke, + PlaybackInformation, + RenderStylesheet, + RepeatGroup, + Score, + Section, + SimileMark, + SlideInType, + SlideOutType, + Staff, + Track, + TripletFeel, + Tuning, + TupletGroup, + VibratoType, + Voice, + WhammyType +}; + +import { RenderFinishedEventArgs } from '@src/rendering/RenderFinishedEventArgs'; +import { ScoreRenderer } from '@src/rendering/ScoreRenderer'; + +import { BarBounds } from '@src/rendering/utils/BarBounds'; +import { BeatBounds } from '@src/rendering/utils/BeatBounds'; +import { Bounds } from '@src/rendering/utils/Bounds'; +import { BoundsLookup } from '@src/rendering/utils/BoundsLookup'; +import { MasterBarBounds } from '@src/rendering/utils/MasterBarBounds'; +import { NoteBounds } from '@src/rendering/utils/NoteBounds'; +import { StaveGroupBounds } from '@src/rendering/utils/StaveGroupBounds'; + +export const rendering = { + ScoreRenderer, + RenderFinishedEventArgs, + BarBounds, + BeatBounds, + Bounds, + BoundsLookup, + MasterBarBounds, + NoteBounds, + StaveGroupBounds +}; + +import { AlphaSynth } from '@src/synth/AlphaSynth'; +import { PlaybackRange } from '@src/synth/PlaybackRange'; +import { PlayerState } from '@src/synth/PlayerState'; +import { PlayerStateChangedEventArgs } from '@src/synth/PlayerStateChangedEventArgs'; +import { PositionChangedEventArgs } from '@src/synth/PositionChangedEventArgs'; +import { AlphaSynthWebWorkerApi } from '@src/platform/javascript/AlphaSynthWebWorkerApi' + +export const synth = { + AlphaSynth, + PlaybackRange, + PlayerState, + PlayerStateChangedEventArgs, + PositionChangedEventArgs, + AlphaSynthWebWorkerApi +}; diff --git a/src/importer/AlphaTexImporter.ts b/src/importer/AlphaTexImporter.ts new file mode 100644 index 000000000..04da79585 --- /dev/null +++ b/src/importer/AlphaTexImporter.ts @@ -0,0 +1,1762 @@ +import { GeneralMidi } from '@src/midi/GeneralMidi'; +import { ScoreImporter } from '@src/importer/ScoreImporter'; +import { UnsupportedFormatError } from '@src/importer/UnsupportedFormatError'; +import { AccentuationType } from '@src/model/AccentuationType'; +import { Automation, AutomationType } from '@src/model/Automation'; +import { Bar } from '@src/model/Bar'; +import { Beat } from '@src/model/Beat'; +import { BendPoint } from '@src/model/BendPoint'; +import { Chord } from '@src/model/Chord'; +import { Clef } from '@src/model/Clef'; +import { CrescendoType } from '@src/model/CrescendoType'; +import { Duration } from '@src/model/Duration'; +import { DynamicValue } from '@src/model/DynamicValue'; +import { Fingers } from '@src/model/Fingers'; +import { GraceType } from '@src/model/GraceType'; +import { HarmonicType } from '@src/model/HarmonicType'; +import { KeySignature } from '@src/model/KeySignature'; +import { Lyrics } from '@src/model/Lyrics'; +import { MasterBar } from '@src/model/MasterBar'; +import { Note } from '@src/model/Note'; +import { PickStroke } from '@src/model/PickStroke'; +import { Score } from '@src/model/Score'; +import { Section } from '@src/model/Section'; +import { SlideInType } from '@src/model/SlideInType'; +import { SlideOutType } from '@src/model/SlideOutType'; +import { Staff } from '@src/model/Staff'; +import { Track } from '@src/model/Track'; +import { TripletFeel } from '@src/model/TripletFeel'; +import { Tuning } from '@src/model/Tuning'; +import { VibratoType } from '@src/model/VibratoType'; +import { Voice } from '@src/model/Voice'; +import { Logger } from '@src/Logger'; +import { ModelUtils, TuningParseResult } from '@src/model/ModelUtils'; +import { AlphaTabError, AlphaTabErrorType } from '@src/AlphaTabError'; + +/** + * A list of terminals recognized by the alphaTex-parser + */ +export enum AlphaTexSymbols { + No, + Eof, + Number, + DoubleDot, + Dot, + String, + Tuning, + LParensis, + RParensis, + LBrace, + RBrace, + Pipe, + MetaCommand, + Multiply, + LowerThan, + Property +} + +export class AlphaTexError extends AlphaTabError { + public position: number = 0; + public nonTerm: string = ''; + public expected: AlphaTexSymbols = AlphaTexSymbols.No; + public symbol: AlphaTexSymbols = AlphaTexSymbols.No; + public symbolData: unknown; + + public constructor(message: string) { + super(AlphaTabErrorType.AlphaTex, message); + Object.setPrototypeOf(this, AlphaTexError.prototype); + } + + public static symbolError( + position: number, + nonTerm: string, + expected: AlphaTexSymbols, + symbol: AlphaTexSymbols, + symbolData: unknown = null + ): AlphaTexError { + let message: string; + if (!symbolData) { + message = `MalFormed AlphaTex: @${position}: Error on block ${nonTerm}, expected a ${AlphaTexSymbols[expected]} found a ${AlphaTexSymbols[symbol]}: '${symbolData}'`; + } else { + message = `MalFormed AlphaTex: @${position}: Error on block ${nonTerm}, invalid value: ${symbolData}`; + } + let exception: AlphaTexError = new AlphaTexError(message); + exception.position = position; + exception.nonTerm = nonTerm; + exception.expected = expected; + exception.symbol = symbol; + exception.symbolData = symbolData; + return exception; + } + + public static errorMessage(position: number, message: string): AlphaTexError { + message = 'MalFormed AlphaTex: @' + position + ': ' + message; + let exception: AlphaTexError = new AlphaTexError(message); + exception.position = position; + return exception; + } +} + +/** + * This importer can parse alphaTex markup into a score structure. + */ +export class AlphaTexImporter extends ScoreImporter { + private static readonly Eof: number = 0; + private _trackChannel: number = 0; + private _score!: Score; + private _currentTrack!: Track; + private _currentStaff!: Staff; + private _ch: number = 0; + private _curChPos: number = 0; + private _sy: AlphaTexSymbols = AlphaTexSymbols.No; + private _syData: unknown; + private _allowNegatives: boolean = false; + private _allowTuning: boolean = false; + private _currentDuration: Duration = Duration.QuadrupleWhole; + private _currentDynamics: DynamicValue = DynamicValue.PPP; + private _currentTuplet: number = 0; + private _lyrics!: Map; + + public constructor() { + super(); + } + + public get name(): string { + return 'AlphaTex'; + } + + public readScore(): Score { + try { + this._allowTuning = true; + this._lyrics = new Map(); + this.createDefaultScore(); + this._curChPos = 0; + this._currentDuration = Duration.Quarter; + this._currentDynamics = DynamicValue.F; + this._currentTuplet = 1; + this._ch = this.nextChar(); + this._sy = this.newSy(); + if (this._sy === AlphaTexSymbols.LowerThan) { + // potential XML, stop parsing (alphaTex never starts with <) + throw new UnsupportedFormatError('Unknown start sign <'); + } + this.score(); + this.consolidate(); + this._score.finish(this.settings); + this._score.rebuildRepeatGroups(); + this._lyrics.forEach((lyrics, track)=>{ + this._score.tracks[track].applyLyrics(lyrics); + }); + return this._score; + } catch (e) { + if (e instanceof AlphaTexError) { + throw new UnsupportedFormatError(e.message); + } else { + throw e; + } + } + } + + private consolidate(): void { + // the number of bars per staff and track could be inconsistent, + // we need to ensure all staffs of all tracks have the correct number of bars + for (let track of this._score.tracks) { + for (let staff of track.staves) { + while (staff.bars.length < this._score.masterBars.length) { + let bar: Bar = this.newBar(staff); + let emptyBeat: Beat = new Beat(); + emptyBeat.isEmpty = true; + bar.voices[0].addBeat(emptyBeat); + } + } + } + } + + private error(nonterm: string, expected: AlphaTexSymbols, symbolError: boolean = true): void { + let e: AlphaTexError; + if (symbolError) { + e = AlphaTexError.symbolError(this._curChPos, nonterm, expected, this._sy, null); + } else { + e = AlphaTexError.symbolError(this._curChPos, nonterm, expected, expected, this._syData); + } + Logger.error(this.name, e.message); + throw e; + } + + private errorMessage(message: string): void { + let e: AlphaTexError = AlphaTexError.errorMessage(this._curChPos, message); + Logger.error(this.name, e.message); + throw e; + } + + /** + * Initializes the song with some required default values. + * @returns + */ + private createDefaultScore(): void { + this._score = new Score(); + this._score.tempo = 120; + this._score.tempoLabel = ''; + this.newTrack(); + } + + private newTrack(): void { + this._currentTrack = new Track(); + this._currentTrack.ensureStaveCount(1); + this._currentTrack.playbackInfo.program = 25; + this._currentTrack.playbackInfo.primaryChannel = this._trackChannel++; + this._currentTrack.playbackInfo.secondaryChannel = this._trackChannel++; + this._currentStaff = this._currentTrack.staves[0]; + this._currentStaff.displayTranspositionPitch = -12; + this._currentStaff.tuning = Tuning.getDefaultTuningFor(6)!.tunings; + this._score.addTrack(this._currentTrack); + this._lyrics.set(this._currentTrack.index, []); + this._currentDynamics = DynamicValue.F; + } + + /** + * Converts a clef string into the clef value. + * @param str the string to convert + * @returns the clef value + */ + private parseClefFromString(str: string): Clef { + switch (str.toLowerCase()) { + case 'g2': + case 'treble': + return Clef.G2; + case 'f4': + case 'bass': + return Clef.F4; + case 'c3': + case 'tenor': + return Clef.C3; + case 'c4': + case 'alto': + return Clef.C4; + case 'n': + case 'neutral': + return Clef.Neutral; + default: + return Clef.G2; + // error("clef-value", AlphaTexSymbols.String, false); + } + } + + /** + * Converts a clef tuning into the clef value. + * @param i the tuning value to convert + * @returns the clef value + */ + private parseClefFromInt(i: number): Clef { + switch (i) { + case 43: + return Clef.G2; + case 65: + return Clef.F4; + case 48: + return Clef.C3; + case 60: + return Clef.C4; + default: + return Clef.G2; + } + } + + private parseTripletFeelFromString(str: string): TripletFeel { + switch (str.toLowerCase()) { + case 'no': + case 'none': + return TripletFeel.NoTripletFeel; + case 't16': + case 'triplet-16th': + return TripletFeel.Triplet16th; + case 't8': + case 'triplet-8th': + return TripletFeel.Triplet8th; + case 'd16': + case 'dotted-16th': + return TripletFeel.Dotted16th; + case 'd8': + case 'dotted-8th': + return TripletFeel.Dotted8th; + case 's16': + case 'scottish-16th': + return TripletFeel.Scottish16th; + case 's8': + case 'scottish-8th': + return TripletFeel.Scottish8th; + default: + return TripletFeel.NoTripletFeel; + } + } + + private parseTripletFeelFromInt(i: number): TripletFeel { + switch (i) { + case 0: + return TripletFeel.NoTripletFeel; + case 1: + return TripletFeel.Triplet16th; + case 2: + return TripletFeel.Triplet8th; + case 3: + return TripletFeel.Dotted16th; + case 4: + return TripletFeel.Dotted8th; + case 5: + return TripletFeel.Scottish16th; + case 6: + return TripletFeel.Scottish8th; + default: + return TripletFeel.NoTripletFeel; + } + } + + /** + * Converts a keysignature string into the assocciated value. + * @param str the string to convert + * @returns the assocciated keysignature value + */ + private parseKeySignature(str: string): number { + switch (str.toLowerCase()) { + case 'cb': + return -7; + case 'gb': + return -6; + case 'db': + return -5; + case 'ab': + return -4; + case 'eb': + return -3; + case 'bb': + return -2; + case 'f': + return -1; + case 'c': + return 0; + case 'g': + return 1; + case 'd': + return 2; + case 'a': + return 3; + case 'e': + return 4; + case 'b': + return 5; + case 'f#': + return 6; + case 'c#': + return 7; + default: + return 0; + // error("keysignature-value", AlphaTexSymbols.String, false); return 0 + } + } + + /** + * Reads the next character of the source stream. + */ + private nextChar(): number { + let b: number = this.data.readByte(); + if (b === -1) { + this._ch = 0; + } else { + this._ch = b; + this._curChPos++; + } + return this._ch; + } + + /** + * Reads the next terminal symbol. + */ + private newSy(): AlphaTexSymbols { + this._sy = AlphaTexSymbols.No; + do { + if (this._ch === AlphaTexImporter.Eof) { + this._sy = AlphaTexSymbols.Eof; + } else if (this._ch === 0x20 || this._ch === 0x0b || this._ch === 0x0d || this._ch === 0x0a || this._ch === 0x09) { + // skip whitespaces + this._ch = this.nextChar(); + } else if (this._ch === 0x2f /* / */) { + this._ch = this.nextChar(); + if (this._ch === 0x2f /* / */) { + // single line comment + while ( + this._ch !== 0x0d /* \r */ && + this._ch !== 0x0a /* \n */ && + this._ch !== AlphaTexImporter.Eof + ) { + this._ch = this.nextChar(); + } + } else if (this._ch === 0x2a /* * */) { + // multiline comment + while (this._ch !== 0) { + if (this._ch === 0x2a /* * */) { + this._ch = this.nextChar(); + if (this._ch === 0x2f /* / */) { + this._ch = this.nextChar(); + break; + } + } else { + this._ch = this.nextChar(); + } + } + } else { + this.error('symbol', AlphaTexSymbols.String, false); + } + } else if (this._ch === 0x22 /* " */ || this._ch === 0x27 /* ' */) { + let startChar: number = this._ch; + this._ch = this.nextChar(); + let s: string = ''; + this._sy = AlphaTexSymbols.String; + while (this._ch !== startChar && this._ch !== AlphaTexImporter.Eof) { + s += String.fromCharCode(this._ch); + this._ch = this.nextChar(); + } + this._syData = s; + this._ch = this.nextChar(); + } else if (this._ch === 0x2d /* - */) { + // negative number + // is number? + if (this._allowNegatives && this.isDigit(this._ch)) { + let num: number = this.readNumber(); + this._sy = AlphaTexSymbols.Number; + this._syData = num; + } else { + this._sy = AlphaTexSymbols.String; + this._syData = this.readName(); + } + } else if (this._ch === 0x2e /* . */) { + this._sy = AlphaTexSymbols.Dot; + this._ch = this.nextChar(); + } else if (this._ch === 0x3a /* : */) { + this._sy = AlphaTexSymbols.DoubleDot; + this._ch = this.nextChar(); + } else if (this._ch === 0x28 /* ( */) { + this._sy = AlphaTexSymbols.LParensis; + this._ch = this.nextChar(); + } else if (this._ch === 0x5c /* \ */) { + this._ch = this.nextChar(); + let name: string = this.readName(); + this._sy = AlphaTexSymbols.MetaCommand; + this._syData = name; + } else if (this._ch === 0x29 /* ) */) { + this._sy = AlphaTexSymbols.RParensis; + this._ch = this.nextChar(); + } else if (this._ch === 0x7b /* { */) { + this._sy = AlphaTexSymbols.LBrace; + this._ch = this.nextChar(); + } else if (this._ch === 0x7d /* } */) { + this._sy = AlphaTexSymbols.RBrace; + this._ch = this.nextChar(); + } else if (this._ch === 0x7c /* | */) { + this._sy = AlphaTexSymbols.Pipe; + this._ch = this.nextChar(); + } else if (this._ch === 0x2a /* * */) { + this._sy = AlphaTexSymbols.Multiply; + this._ch = this.nextChar(); + } else if (this._ch === 0x3c /* < */) { + this._sy = AlphaTexSymbols.LowerThan; + this._ch = this.nextChar(); + } else if (this.isDigit(this._ch)) { + let num: number = this.readNumber(); + this._sy = AlphaTexSymbols.Number; + this._syData = num; + } else if (AlphaTexImporter.isLetter(this._ch)) { + let name: string = this.readName(); + let tuning: TuningParseResult | null = this._allowTuning ? ModelUtils.parseTuning(name) : null; + if (tuning) { + this._sy = AlphaTexSymbols.Tuning; + this._syData = tuning; + } else { + this._sy = AlphaTexSymbols.String; + this._syData = name; + } + } else { + this.error('symbol', AlphaTexSymbols.String, false); + } + } while (this._sy === AlphaTexSymbols.No); + return this._sy; + } + + /** + * Checks if the given character is a letter. + * (no control characters, whitespaces, numbers or dots) + * @param code the character + * @returns true if the given character is a letter, otherwise false. + */ + private static isLetter(code: number): boolean { + // no control characters, whitespaces, numbers or dots + return ( + !AlphaTexImporter.isTerminal(code) && + ((code >= 0x21 && code <= 0x2f) || (code >= 0x3a && code <= 0x7e) || code > 0x80) + ); /* Unicode Symbols */ + } + + /** + * Checks if the given charater is a non terminal. + * @param ch the character + * @returns true if the given character is a terminal, otherwise false. + */ + private static isTerminal(ch: number): boolean { + return ( + ch === 0x2e /* . */ || + ch === 0x7b /* { */ || + ch === 0x7d /* } */ || + ch === 0x5b /* [ */ || + ch === 0x5d /* ] */ || + ch === 0x28 /* ( */ || + ch === 0x29 /* ) */ || + ch === 0x7c /* | */ || + ch === 0x27 /* ' */ || + ch === 0x22 /* " */ || + ch === 0x5c /* \ */ + ); + } + + /** + * Checks if the given character is a digit. + * @param code the character + * @returns true if the given character is a digit, otherwise false. + */ + private isDigit(code: number): boolean { + return (code >= 0x30 && code <= 0x39) /*0-9*/ || (code === 0x2d /* - */ && this._allowNegatives); // allow - if negatives + } + + /** + * Reads a string from the stream. + * @returns the read string. + */ + private readName(): string { + let str: string = ''; + do { + str += String.fromCharCode(this._ch); + this._ch = this.nextChar(); + } while (AlphaTexImporter.isLetter(this._ch) || this.isDigit(this._ch) || this._ch === 0x23); + return str; + } + + /** + * Reads a number from the stream. + * @returns the read number. + */ + private readNumber(): number { + let str: string = ''; + do { + str += String.fromCharCode(this._ch); + this._ch = this.nextChar(); + } while (this.isDigit(this._ch)); + return parseInt(str); + } + + private score(): void { + this.metaData(); + this.bars(); + } + + private metaData(): void { + let anyMeta: boolean = false; + let continueReading: boolean = true; + while (this._sy === AlphaTexSymbols.MetaCommand && continueReading) { + let syData: string = (this._syData as string).toLowerCase(); + switch (syData) { + case 'title': + this._sy = this.newSy(); + if (this._sy === AlphaTexSymbols.String) { + this._score.title = (this._syData as string); + } else { + this.error('title', AlphaTexSymbols.String, true); + } + this._sy = this.newSy(); + anyMeta = true; + break; + case 'subtitle': + this._sy = this.newSy(); + if (this._sy === AlphaTexSymbols.String) { + this._score.subTitle = (this._syData as string); + } else { + this.error('subtitle', AlphaTexSymbols.String, true); + } + this._sy = this.newSy(); + anyMeta = true; + break; + case 'artist': + this._sy = this.newSy(); + if (this._sy === AlphaTexSymbols.String) { + this._score.artist = (this._syData as string); + } else { + this.error('artist', AlphaTexSymbols.String, true); + } + this._sy = this.newSy(); + anyMeta = true; + break; + case 'album': + this._sy = this.newSy(); + if (this._sy === AlphaTexSymbols.String) { + this._score.album = (this._syData as string); + } else { + this.error('album', AlphaTexSymbols.String, true); + } + this._sy = this.newSy(); + anyMeta = true; + break; + case 'words': + this._sy = this.newSy(); + if (this._sy === AlphaTexSymbols.String) { + this._score.words = (this._syData as string); + } else { + this.error('words', AlphaTexSymbols.String, true); + } + this._sy = this.newSy(); + anyMeta = true; + break; + case 'music': + this._sy = this.newSy(); + if (this._sy === AlphaTexSymbols.String) { + this._score.music = (this._syData as string); + } else { + this.error('music', AlphaTexSymbols.String, true); + } + this._sy = this.newSy(); + anyMeta = true; + break; + case 'copyright': + this._sy = this.newSy(); + if (this._sy === AlphaTexSymbols.String) { + this._score.copyright = (this._syData as string); + } else { + this.error('copyright', AlphaTexSymbols.String, true); + } + this._sy = this.newSy(); + anyMeta = true; + break; + case 'tempo': + this._sy = this.newSy(); + if (this._sy === AlphaTexSymbols.Number) { + this._score.tempo = this._syData as number; + } else { + this.error('tempo', AlphaTexSymbols.Number, true); + } + this._sy = this.newSy(); + anyMeta = true; + break; + default: + if (this.handleStaffMeta()) { + anyMeta = true; + } else if (anyMeta) { + // invalid meta encountered + this.error('metaDataTags', AlphaTexSymbols.String, false); + } else { + // fall forward to bar meta if unknown score meta was found + continueReading = false; + } + break; + } + } + if (anyMeta) { + if (this._sy !== AlphaTexSymbols.Dot) { + this.error('song', AlphaTexSymbols.Dot, true); + } + this._sy = this.newSy(); + } else if (this._sy === AlphaTexSymbols.Dot) { + this._sy = this.newSy(); + } + } + + private handleStaffMeta(): boolean { + let syData: string = (this._syData as string).toLowerCase(); + switch (syData) { + case 'capo': + this._sy = this.newSy(); + if (this._sy === AlphaTexSymbols.Number) { + this._currentStaff.capo = this._syData as number; + } else { + this.error('capo', AlphaTexSymbols.Number, true); + } + this._sy = this.newSy(); + return true; + case 'tuning': + this._sy = this.newSy(); + let strings: number = this._currentStaff.tuning.length; + switch (this._sy) { + case AlphaTexSymbols.String: + let text: string = (this._syData as string).toLowerCase(); + if (text === 'piano' || text === 'none' || text === 'voice') { + // clear tuning + this._currentStaff.tuning = []; + } else { + this.error('tuning', AlphaTexSymbols.Tuning, true); + } + this._sy = this.newSy(); + break; + case AlphaTexSymbols.Tuning: + let tuning: number[] = []; + do { + let t: TuningParseResult = this._syData as TuningParseResult; + tuning.push(t.realValue); + this._sy = this.newSy(); + } while (this._sy === AlphaTexSymbols.Tuning); + this._currentStaff.tuning = tuning; + break; + default: + this.error('tuning', AlphaTexSymbols.Tuning, true); + break; + } + if (strings !== this._currentStaff.tuning.length && this._currentStaff.chords.size > 0) { + this.errorMessage('Tuning must be defined before any chord'); + } + return true; + case 'instrument': + this._sy = this.newSy(); + if (this._sy === AlphaTexSymbols.Number) { + let instrument: number = this._syData as number; + if (instrument >= 0 && instrument <= 128) { + this._currentTrack.playbackInfo.program = this._syData as number; + } else { + this.error('instrument', AlphaTexSymbols.Number, false); + } + } else if (this._sy === AlphaTexSymbols.String) { + let instrumentName: string = (this._syData as string).toLowerCase(); + this._currentTrack.playbackInfo.program = GeneralMidi.getValue(instrumentName); + } else { + this.error('instrument', AlphaTexSymbols.Number, true); + } + this._currentStaff.displayTranspositionPitch = GeneralMidi.isGuitar( + this._currentTrack.playbackInfo.program + ) + ? -12 + : 0; + this._sy = this.newSy(); + return true; + case 'lyrics': + this._sy = this.newSy(); + let lyrics: Lyrics = new Lyrics(); + lyrics.startBar = 0; + lyrics.text = ''; + if (this._sy === AlphaTexSymbols.Number) { + lyrics.startBar = this._syData as number; + this._sy = this.newSy(); + } + if (this._sy === AlphaTexSymbols.String) { + lyrics.text = this._syData as string; + this._sy = this.newSy(); + } else { + this.error('lyrics', AlphaTexSymbols.String, true); + } + this._lyrics.get(this._currentTrack.index)!.push(lyrics); + return true; + case 'chord': + this._sy = this.newSy(); + let chord: Chord = new Chord(); + this.chordProperties(chord); + if (this._sy === AlphaTexSymbols.String) { + chord.name = this._syData as string; + this._sy = this.newSy(); + } else { + this.error('chord-name', AlphaTexSymbols.Number, true); + } + for (let i: number = 0; i < this._currentStaff.tuning.length; i++) { + if (this._sy === AlphaTexSymbols.Number) { + chord.strings.push(this._syData as number); + } else if (this._sy === AlphaTexSymbols.String && (this._syData as string).toLowerCase() === 'x') { + chord.strings.push(-1); + } + this._sy = this.newSy(); + } + this._currentStaff.addChord(chord.name.toLowerCase(), chord); + return true; + default: + return false; + } + } + + private chordProperties(chord: Chord): void { + if (this._sy !== AlphaTexSymbols.LBrace) { + return; + } + this._sy = this.newSy(); + while (this._sy === AlphaTexSymbols.String) { + switch ((this._syData as string).toLowerCase()) { + case 'firstfret': + this._sy = this.newSy(); + switch (this._sy) { + case AlphaTexSymbols.Number: + chord.firstFret = this._syData as number; + break; + default: + this.error('chord-firstfret', AlphaTexSymbols.Number, true); + break; + } + this._sy = this.newSy(); + break; + case 'showdiagram': + this._sy = this.newSy(); + switch (this._sy) { + case AlphaTexSymbols.String: + chord.showDiagram = (this._syData as string).toLowerCase() !== 'false'; + break; + case AlphaTexSymbols.Number: + chord.showDiagram = (this._syData as number) !== 0; + break; + default: + this.error('chord-showdiagram', AlphaTexSymbols.String, true); + break; + } + this._sy = this.newSy(); + break; + case 'showfingering': + this._sy = this.newSy(); + switch (this._sy) { + case AlphaTexSymbols.String: + chord.showDiagram = (this._syData as string).toLowerCase() !== 'false'; + break; + case AlphaTexSymbols.Number: + chord.showFingering = (this._syData as number) !== 0; + break; + default: + this.error('chord-showfingering', AlphaTexSymbols.String, true); + break; + } + this._sy = this.newSy(); + break; + case 'showname': + this._sy = this.newSy(); + switch (this._sy) { + case AlphaTexSymbols.String: + chord.showName = (this._syData as string).toLowerCase() !== 'false'; + break; + case AlphaTexSymbols.Number: + chord.showName = (this._syData as number) !== 0; + break; + default: + this.error('chord-showname', AlphaTexSymbols.String, true); + break; + } + this._sy = this.newSy(); + break; + case 'barre': + this._sy = this.newSy(); + while (this._sy === AlphaTexSymbols.Number) { + chord.barreFrets.push(this._syData as number); + this._sy = this.newSy(); + } + break; + default: + this.error('chord-properties', AlphaTexSymbols.String, false); + break; + } + } + if (this._sy !== AlphaTexSymbols.RBrace) { + this.error('chord-properties', AlphaTexSymbols.RBrace, true); + } + this._sy = this.newSy(); + } + + private bars(): void { + this.bar(); + while (this._sy !== AlphaTexSymbols.Eof) { + // read pipe from last bar + if (this._sy === AlphaTexSymbols.Pipe) { + this._sy = this.newSy(); + this.bar(); + } else if (this._sy === AlphaTexSymbols.MetaCommand) { + this.bar(); + } else { + break; + } + } + } + + private trackStaffMeta(): void { + if (this._sy === AlphaTexSymbols.MetaCommand) { + let syData: string = (this._syData as string).toLowerCase(); + if (syData === 'track') { + this._sy = this.newSy(); + // new track starting? - if no masterbars it's the \track of the initial track. + if (this._score.masterBars.length > 0) { + this.newTrack(); + } + // name + if (this._sy === AlphaTexSymbols.String) { + this._currentTrack.name = (this._syData as string); + this._sy = this.newSy(); + } + // short name + if (this._sy === AlphaTexSymbols.String) { + this._currentTrack.shortName = (this._syData as string); + this._sy = this.newSy(); + } + } + if(this._sy === AlphaTexSymbols.MetaCommand) { + syData = (this._syData as string).toLowerCase(); + if (syData === 'staff') { + this._sy = this.newSy(); + if (this._currentTrack.staves[0].bars.length > 0) { + this._currentTrack.ensureStaveCount(this._currentTrack.staves.length + 1); + this._currentStaff = this._currentTrack.staves[this._currentTrack.staves.length - 1]; + this._currentDynamics = DynamicValue.F; + } + this.staffProperties(); + } + } + } + } + + private staffProperties(): void { + if (this._sy !== AlphaTexSymbols.LBrace) { + return; + } + this._sy = this.newSy(); + let showStandardNotation: boolean = false; + let showTabs: boolean = false; + while (this._sy === AlphaTexSymbols.String) { + switch ((this._syData as string).toLowerCase()) { + case 'score': + showStandardNotation = true; + this._sy = this.newSy(); + break; + case 'tabs': + showTabs = true; + this._sy = this.newSy(); + break; + default: + this.error('staff-properties', AlphaTexSymbols.String, false); + break; + } + } + if (showStandardNotation || showTabs) { + this._currentStaff.showStandardNotation = showStandardNotation; + this._currentStaff.showTablature = showTabs; + } + if (this._sy !== AlphaTexSymbols.RBrace) { + this.error('staff-properties', AlphaTexSymbols.RBrace, true); + } + this._sy = this.newSy(); + } + + private bar(): void { + this.trackStaffMeta(); + let bar: Bar = this.newBar(this._currentStaff); + if (this._currentStaff.bars.length > this._score.masterBars.length) { + let master: MasterBar = new MasterBar(); + this._score.addMasterBar(master); + if (master.index > 0) { + master.keySignature = master.previousMasterBar!.keySignature; + master.keySignatureType = master.previousMasterBar!.keySignatureType; + master.timeSignatureDenominator = master.previousMasterBar!.timeSignatureDenominator; + master.timeSignatureNumerator = master.previousMasterBar!.timeSignatureNumerator; + master.tripletFeel = master.previousMasterBar!.tripletFeel; + } + } + this.barMeta(bar); + let voice: Voice = bar.voices[0]; + while (this._sy !== AlphaTexSymbols.Pipe && this._sy !== AlphaTexSymbols.Eof) { + if (!this.beat(voice)) { + break; + } + } + if (voice.beats.length === 0) { + let emptyBeat: Beat = new Beat(); + emptyBeat.isEmpty = true; + voice.addBeat(emptyBeat); + } + } + + private newBar(staff: Staff): Bar { + let bar: Bar = new Bar(); + staff.addBar(bar); + if (bar.index > 0) { + bar.clef = bar.previousBar!.clef; + } + let voice: Voice = new Voice(); + bar.addVoice(voice); + return bar; + } + + private beat(voice: Voice): boolean { + // duration specifier? + this.beatDuration(); + let beat: Beat = new Beat(); + voice.addBeat(beat); + if (voice.bar.masterBar.tempoAutomation && voice.beats.length === 1) { + beat.automations.push(voice.bar.masterBar.tempoAutomation); + } + // notes + if (this._sy === AlphaTexSymbols.LParensis) { + this._sy = this.newSy(); + this.note(beat); + while (this._sy !== AlphaTexSymbols.RParensis && this._sy !== AlphaTexSymbols.Eof) { + if (!this.note(beat)) { + break; + } + } + if (this._sy !== AlphaTexSymbols.RParensis) { + this.error('note-list', AlphaTexSymbols.RParensis, true); + } + this._sy = this.newSy(); + } else if (this._sy === AlphaTexSymbols.String && (this._syData as string).toLowerCase() === 'r') { + // rest voice -> no notes + this._sy = this.newSy(); + } else { + if (!this.note(beat)) { + voice.beats.splice(voice.beats.length - 1, 1); + return false; + } + } + // new duration + if (this._sy === AlphaTexSymbols.Dot) { + this._allowNegatives = true; + this._sy = this.newSy(); + this._allowNegatives = false; + if (this._sy !== AlphaTexSymbols.Number) { + this.error('duration', AlphaTexSymbols.Number, true); + } + this._currentDuration = this.parseDuration(this._syData as number); + this._sy = this.newSy(); + } + beat.duration = this._currentDuration; + beat.dynamics = this._currentDynamics; + if (this._currentTuplet !== 1 && !beat.hasTuplet) { + this.applyTuplet(beat, this._currentTuplet); + } + // beat multiplier (repeat beat n times) + let beatRepeat: number = 1; + if (this._sy === AlphaTexSymbols.Multiply) { + this._sy = this.newSy(); + // multiplier count + if (this._sy !== AlphaTexSymbols.Number) { + this.error('multiplier', AlphaTexSymbols.Number, true); + } else { + beatRepeat = this._syData as number; + } + this._sy = this.newSy(); + } + this.beatEffects(beat); + for (let i: number = 0; i < beatRepeat - 1; i++) { + voice.addBeat(beat.clone()); + } + return true; + } + + private beatDuration(): void { + if (this._sy !== AlphaTexSymbols.DoubleDot) { + return; + } + this._allowNegatives = true; + this._sy = this.newSy(); + this._allowNegatives = false; + if (this._sy !== AlphaTexSymbols.Number) { + this.error('duration', AlphaTexSymbols.Number, true); + } + this._currentDuration = this.parseDuration(this._syData as number); + this._currentTuplet = 1; + this._sy = this.newSy(); + if (this._sy !== AlphaTexSymbols.LBrace) { + return; + } + this._sy = this.newSy(); + while (this._sy === AlphaTexSymbols.String) { + let effect: string = (this._syData as string).toLowerCase(); + switch (effect) { + case 'tu': + this._sy = this.newSy(); + if (this._sy !== AlphaTexSymbols.Number) { + this.error('duration-tuplet', AlphaTexSymbols.Number, true); + } + this._currentTuplet = this._syData as number; + this._sy = this.newSy(); + break; + default: + this.error('beat-duration', AlphaTexSymbols.String, false); + break; + } + } + if (this._sy !== AlphaTexSymbols.RBrace) { + this.error('beat-duration', AlphaTexSymbols.RBrace, true); + } + this._sy = this.newSy(); + } + + private beatEffects(beat: Beat): void { + if (this._sy !== AlphaTexSymbols.LBrace) { + return; + } + this._sy = this.newSy(); + while (this._sy === AlphaTexSymbols.String) { + this._syData = (this._syData as string).toLowerCase(); + if (!this.applyBeatEffect(beat)) { + this.error('beat-effects', AlphaTexSymbols.String, false); + } + } + if (this._sy !== AlphaTexSymbols.RBrace) { + this.error('beat-effects', AlphaTexSymbols.RBrace, true); + } + this._sy = this.newSy(); + } + + /** + * Tries to apply a beat effect to the given beat. + * @returns true if a effect could be applied, otherwise false + */ + private applyBeatEffect(beat: Beat): boolean { + let syData: string = (this._syData as string).toLowerCase(); + if (syData === 'f') { + beat.fadeIn = true; + this._sy = this.newSy(); + return true; + } + if (syData === 'v') { + beat.vibrato = VibratoType.Slight; + this._sy = this.newSy(); + return true; + } + if (syData === 's') { + beat.slap = true; + this._sy = this.newSy(); + return true; + } + if (syData === 'p') { + beat.pop = true; + this._sy = this.newSy(); + return true; + } + if (syData === 'tt') { + beat.tap = true; + this._sy = this.newSy(); + return true; + } + if (syData === 'dd') { + beat.dots = 2; + this._sy = this.newSy(); + return true; + } + if (syData === 'd') { + beat.dots = 1; + this._sy = this.newSy(); + return true; + } + if (syData === 'su') { + beat.pickStroke = PickStroke.Up; + this._sy = this.newSy(); + return true; + } + if (syData === 'sd') { + beat.pickStroke = PickStroke.Down; + this._sy = this.newSy(); + return true; + } + if (syData === 'tu') { + this._sy = this.newSy(); + if (this._sy !== AlphaTexSymbols.Number) { + this.error('tuplet', AlphaTexSymbols.Number, true); + return false; + } + this.applyTuplet(beat, this._syData as number); + this._sy = this.newSy(); + return true; + } + if (syData === 'tb' || syData === 'tbe') { + let exact: boolean = syData === 'tbe'; + // read points + this._sy = this.newSy(); + if (this._sy !== AlphaTexSymbols.LParensis) { + this.error('tremolobar-effect', AlphaTexSymbols.LParensis, true); + return false; + } + this._allowNegatives = true; + this._sy = this.newSy(); + while (this._sy !== AlphaTexSymbols.RParensis && this._sy !== AlphaTexSymbols.Eof) { + let offset: number = 0; + let value: number = 0; + if (exact) { + if (this._sy !== AlphaTexSymbols.Number) { + this.error('tremolobar-effect', AlphaTexSymbols.Number, true); + return false; + } + offset = this._syData as number; + this._sy = this.newSy(); + if (this._sy !== AlphaTexSymbols.Number) { + this.error('tremolobar-effect', AlphaTexSymbols.Number, true); + return false; + } + value = this._syData as number; + } else { + if (this._sy !== AlphaTexSymbols.Number) { + this.error('tremolobar-effect', AlphaTexSymbols.Number, true); + return false; + } + offset = 0; + value = this._syData as number; + } + beat.addWhammyBarPoint(new BendPoint(offset, value)); + this._sy = this.newSy(); + } + while (beat.whammyBarPoints.length > 60) { + beat.removeWhammyBarPoint(beat.whammyBarPoints.length - 1); + } + // set positions + if (!exact) { + let count: number = beat.whammyBarPoints.length; + let step: number = (60 / count) | 0; + let i: number = 0; + while (i < count) { + beat.whammyBarPoints[i].offset = Math.min(60, i * step); + i++; + } + } else { + beat.whammyBarPoints.sort((a, b) => { + return a.offset - b.offset; + }); + } + this._allowNegatives = false; + if (this._sy !== AlphaTexSymbols.RParensis) { + this.error('tremolobar-effect', AlphaTexSymbols.RParensis, true); + return false; + } + this._sy = this.newSy(); + return true; + } + if (syData === 'ch') { + this._sy = this.newSy(); + let chordName: string = (this._syData as string); + let chordId: string = chordName.toLowerCase(); + if (!this._currentStaff.chords.has(chordId)) { + let chord: Chord = new Chord(); + chord.showDiagram = false; + chord.name = chordName; + this._currentStaff.addChord(chordId, chord); + } + beat.chordId = chordId; + this._sy = this.newSy(); + return true; + } + if (syData === 'gr') { + this._sy = this.newSy(); + if ((this._syData as string).toLowerCase() === 'ob') { + beat.graceType = GraceType.OnBeat; + this._sy = this.newSy(); + } else if ((this._syData as string).toLowerCase() === 'b') { + beat.graceType = GraceType.BendGrace; + this._sy = this.newSy(); + } else { + beat.graceType = GraceType.BeforeBeat; + } + return true; + } + if (syData === 'dy') { + this.newSy(); + switch ((this._syData as string).toLowerCase()) { + case 'ppp': + beat.dynamics = DynamicValue.PPP; + break; + case 'pp': + beat.dynamics = DynamicValue.PP; + break; + case 'p': + beat.dynamics = DynamicValue.P; + break; + case 'mp': + beat.dynamics = DynamicValue.MP; + break; + case 'mf': + beat.dynamics = DynamicValue.MF; + break; + case 'f': + beat.dynamics = DynamicValue.F; + break; + case 'ff': + beat.dynamics = DynamicValue.FF; + break; + case 'fff': + beat.dynamics = DynamicValue.FFF; + break; + } + this._currentDynamics = beat.dynamics; + this.newSy(); + return true; + } + if (syData === 'cre') { + beat.crescendo = CrescendoType.Crescendo; + this.newSy(); + return true; + } + if (syData === 'dec') { + beat.crescendo = CrescendoType.Decrescendo; + this.newSy(); + return true; + } + if (syData === 'tp') { + this._sy = this.newSy(); + let duration: Duration = Duration.Eighth; + if (this._sy === AlphaTexSymbols.Number) { + switch (this._syData as number) { + case 8: + duration = Duration.Eighth; + break; + case 16: + duration = Duration.Sixteenth; + break; + case 32: + duration = Duration.ThirtySecond; + break; + default: + duration = Duration.Eighth; + break; + } + this._sy = this.newSy(); + } + beat.tremoloSpeed = duration; + return true; + } + return false; + } + + private applyTuplet(beat: Beat, tuplet: number): void { + switch (tuplet) { + case 3: + beat.tupletNumerator = 3; + beat.tupletDenominator = 2; + break; + case 5: + beat.tupletNumerator = 5; + beat.tupletDenominator = 4; + break; + case 6: + beat.tupletNumerator = 6; + beat.tupletDenominator = 4; + break; + case 7: + beat.tupletNumerator = 7; + beat.tupletDenominator = 4; + break; + case 9: + beat.tupletNumerator = 9; + beat.tupletDenominator = 8; + break; + case 10: + beat.tupletNumerator = 10; + beat.tupletDenominator = 8; + break; + case 11: + beat.tupletNumerator = 11; + beat.tupletDenominator = 8; + break; + case 12: + beat.tupletNumerator = 12; + beat.tupletDenominator = 8; + break; + default: + beat.tupletNumerator = 1; + beat.tupletDenominator = 1; + break; + } + } + + private note(beat: Beat): boolean { + // fret.string + let isDead: boolean = false; + let isTie: boolean = false; + let fret: number = -1; + let octave: number = -1; + let tone: number = -1; + switch (this._sy) { + case AlphaTexSymbols.Number: + fret = this._syData as number; + break; + case AlphaTexSymbols.String: + isDead = (this._syData as string) === 'x'; + isTie = (this._syData as string) === '-'; + + if (isTie || isDead) { + fret = 0; + } else { + this.error('note-fret', AlphaTexSymbols.Number, true); + } + break; + case AlphaTexSymbols.Tuning: + let tuning: TuningParseResult = this._syData as TuningParseResult; + octave = tuning.octave; + tone = tuning.noteValue; + break; + default: + return false; + } + this._sy = this.newSy(); // Fret done + + let isFretted: boolean = octave === -1 && this._currentStaff.tuning.length > 0; + let noteString: number = -1; + if (isFretted) { + // Fret [Dot] String + if (this._sy !== AlphaTexSymbols.Dot) { + this.error('note', AlphaTexSymbols.Dot, true); + } + this._sy = this.newSy(); // dot done + + if (this._sy !== AlphaTexSymbols.Number) { + this.error('note-string', AlphaTexSymbols.Number, true); + } + noteString = this._syData as number; + if (noteString < 1 || noteString > this._currentStaff.tuning.length) { + this.error('note-string', AlphaTexSymbols.Number, false); + } + this._sy = this.newSy(); // string done + } + // read effects + let note: Note = new Note(); + if (isFretted) { + note.string = this._currentStaff.tuning.length - (noteString - 1); + note.isDead = isDead; + note.isTieDestination = isTie; + if (!isTie) { + note.fret = fret; + } + } else { + note.octave = octave; + note.tone = tone; + note.isTieDestination = isTie; + } + beat.addNote(note); + this.noteEffects(note); + return true; + } + + private noteEffects(note: Note): void { + if (this._sy !== AlphaTexSymbols.LBrace) { + return; + } + this._sy = this.newSy(); + while (this._sy === AlphaTexSymbols.String) { + let syData: string = (this._syData as string).toLowerCase(); + this._syData = syData; + if (syData === 'b' || syData === 'be') { + let exact: boolean = (this._syData as string) === 'be'; + // read points + this._sy = this.newSy(); + if (this._sy !== AlphaTexSymbols.LParensis) { + this.error('bend-effect', AlphaTexSymbols.LParensis, true); + } + this._sy = this.newSy(); + while (this._sy !== AlphaTexSymbols.RParensis && this._sy !== AlphaTexSymbols.Eof) { + let offset: number = 0; + let value: number = 0; + if (exact) { + if (this._sy !== AlphaTexSymbols.Number) { + this.error('bend-effect-value', AlphaTexSymbols.Number, true); + } + offset = this._syData as number; + this._sy = this.newSy(); + if (this._sy !== AlphaTexSymbols.Number) { + this.error('bend-effect-value', AlphaTexSymbols.Number, true); + } + value = this._syData as number; + } else { + if (this._sy !== AlphaTexSymbols.Number) { + this.error('bend-effect-value', AlphaTexSymbols.Number, true); + } + value = this._syData as number; + } + note.addBendPoint(new BendPoint(offset, value)); + this._sy = this.newSy(); + } + while (note.bendPoints.length > 60) { + note.bendPoints.splice(note.bendPoints.length - 1, 1); + } + // set positions + if (exact) { + note.bendPoints.sort((a, b) => { + return a.offset - b.offset; + }); + } else { + let count: number = note.bendPoints.length; + let step: number = (60 / (count - 1)) | 0; + let i: number = 0; + while (i < count) { + note.bendPoints[i].offset = Math.min(60, i * step); + i++; + } + } + if (this._sy !== AlphaTexSymbols.RParensis) { + this.error('bend-effect', AlphaTexSymbols.RParensis, true); + } + this._sy = this.newSy(); + } else if (syData === 'nh') { + note.harmonicType = HarmonicType.Natural; + this._sy = this.newSy(); + } else if (syData === 'ah') { + // todo: Artificial Key + note.harmonicType = HarmonicType.Artificial; + this._sy = this.newSy(); + } else if (syData === 'th') { + // todo: store tapped fret in data + note.harmonicType = HarmonicType.Tap; + this._sy = this.newSy(); + } else if (syData === 'ph') { + note.harmonicType = HarmonicType.Pinch; + this._sy = this.newSy(); + } else if (syData === 'sh') { + note.harmonicType = HarmonicType.Semi; + this._sy = this.newSy(); + } else if (syData === 'tr') { + this._sy = this.newSy(); + if (this._sy !== AlphaTexSymbols.Number) { + this.error('trill-effect', AlphaTexSymbols.Number, true); + } + let fret: number = this._syData as number; + this._sy = this.newSy(); + let duration: Duration = Duration.Sixteenth; + if (this._sy === AlphaTexSymbols.Number) { + switch (this._syData as number) { + case 16: + duration = Duration.Sixteenth; + break; + case 32: + duration = Duration.ThirtySecond; + break; + case 64: + duration = Duration.SixtyFourth; + break; + default: + duration = Duration.Sixteenth; + break; + } + this._sy = this.newSy(); + } + note.trillValue = fret + note.stringTuning; + note.trillSpeed = duration; + } else if (syData === 'v') { + this._sy = this.newSy(); + note.vibrato = VibratoType.Slight; + } else if (syData === 'sl') { + this._sy = this.newSy(); + note.slideOutType = SlideOutType.Legato; + } else if (syData === 'ss') { + this._sy = this.newSy(); + note.slideOutType = SlideOutType.Shift; + } else if (syData === 'sib') { + this._sy = this.newSy(); + note.slideInType = SlideInType.IntoFromBelow; + } else if (syData === 'sia') { + this._sy = this.newSy(); + note.slideInType = SlideInType.IntoFromAbove; + } else if (syData === 'sou') { + this._sy = this.newSy(); + note.slideOutType = SlideOutType.OutUp; + } else if (syData === 'sod') { + this._sy = this.newSy(); + note.slideOutType = SlideOutType.OutDown; + } else if (syData === 'psd') { + this._sy = this.newSy(); + note.slideOutType = SlideOutType.PickSlideDown; + } else if (syData === 'psu') { + this._sy = this.newSy(); + note.slideOutType = SlideOutType.PickSlideUp; + } else if (syData === 'h') { + this._sy = this.newSy(); + note.isHammerPullOrigin = true; + } else if (syData === 'lht') { + this._sy = this.newSy(); + note.isLeftHandTapped = true; + } else if (syData === 'g') { + this._sy = this.newSy(); + note.isGhost = true; + } else if (syData === 'ac') { + this._sy = this.newSy(); + note.accentuated = AccentuationType.Normal; + } else if (syData === 'hac') { + this._sy = this.newSy(); + note.accentuated = AccentuationType.Heavy; + } else if (syData === 'pm') { + this._sy = this.newSy(); + note.isPalmMute = true; + } else if (syData === 'st') { + this._sy = this.newSy(); + note.isStaccato = true; + } else if (syData === 'lr') { + this._sy = this.newSy(); + note.isLetRing = true; + } else if (syData === 'x') { + this._sy = this.newSy(); + note.fret = 0; + note.isDead = true; + } else if (syData === '-' || syData === 't') { + this._sy = this.newSy(); + note.isTieDestination = true; + } else if (syData === 'lf') { + this._sy = this.newSy(); + let finger: Fingers = Fingers.Thumb; + if (this._sy === AlphaTexSymbols.Number) { + finger = this.toFinger(this._syData as number); + this._sy = this.newSy(); + } + note.leftHandFinger = finger; + } else if (syData === 'rf') { + this._sy = this.newSy(); + let finger: Fingers = Fingers.Thumb; + if (this._sy === AlphaTexSymbols.Number) { + finger = this.toFinger(this._syData as number); + this._sy = this.newSy(); + } + note.rightHandFinger = finger; + } else if (this.applyBeatEffect(note.beat)) { + // Success + } else { + this.error(syData, AlphaTexSymbols.String, false); + } + } + if (this._sy !== AlphaTexSymbols.RBrace) { + this.error('note-effect', AlphaTexSymbols.RBrace, false); + } + this._sy = this.newSy(); + } + + private toFinger(syData: number): Fingers { + switch (syData) { + case 1: + return Fingers.Thumb; + case 2: + return Fingers.IndexFinger; + case 3: + return Fingers.MiddleFinger; + case 4: + return Fingers.AnnularFinger; + case 5: + return Fingers.LittleFinger; + } + return Fingers.Thumb; + } + + private parseDuration(duration: number): Duration { + switch (duration) { + case -4: + return Duration.QuadrupleWhole; + case -2: + return Duration.DoubleWhole; + case 1: + return Duration.Whole; + case 2: + return Duration.Half; + case 4: + return Duration.Quarter; + case 8: + return Duration.Eighth; + case 16: + return Duration.Sixteenth; + case 32: + return Duration.ThirtySecond; + case 64: + return Duration.SixtyFourth; + case 128: + return Duration.OneHundredTwentyEighth; + default: + return Duration.Quarter; + } + } + + private barMeta(bar: Bar): void { + let master: MasterBar = bar.masterBar; + while (this._sy === AlphaTexSymbols.MetaCommand) { + let syData: string = (this._syData as string).toLowerCase(); + if (syData === 'ts') { + this._sy = this.newSy(); + if (this._sy !== AlphaTexSymbols.Number) { + this.error('timesignature-numerator', AlphaTexSymbols.Number, true); + } + master.timeSignatureNumerator = this._syData as number; + this._sy = this.newSy(); + if (this._sy !== AlphaTexSymbols.Number) { + this.error('timesignature-denominator', AlphaTexSymbols.Number, true); + } + master.timeSignatureDenominator = this._syData as number; + this._sy = this.newSy(); + } else if (syData === 'ro') { + master.isRepeatStart = true; + this._sy = this.newSy(); + } else if (syData === 'rc') { + this._sy = this.newSy(); + if (this._sy !== AlphaTexSymbols.Number) { + this.error('repeatclose', AlphaTexSymbols.Number, true); + } + master.repeatCount = this._syData as number; + this._sy = this.newSy(); + } else if (syData === 'ks') { + this._sy = this.newSy(); + if (this._sy !== AlphaTexSymbols.String) { + this.error('keysignature', AlphaTexSymbols.String, true); + } + master.keySignature = this.parseKeySignature((this._syData as string).toLowerCase()) as KeySignature; + this._sy = this.newSy(); + } else if (syData === 'clef') { + this._sy = this.newSy(); + switch (this._sy) { + case AlphaTexSymbols.String: + bar.clef = this.parseClefFromString((this._syData as string).toLowerCase()); + break; + case AlphaTexSymbols.Number: + bar.clef = this.parseClefFromInt(this._syData as number); + break; + case AlphaTexSymbols.Tuning: + let parseResult: TuningParseResult = this._syData as TuningParseResult; + bar.clef = this.parseClefFromInt(parseResult.realValue); + break; + default: + this.error('clef', AlphaTexSymbols.String, true); + break; + } + this._sy = this.newSy(); + } else if (syData === 'tempo') { + this._sy = this.newSy(); + if (this._sy !== AlphaTexSymbols.Number) { + this.error('tempo', AlphaTexSymbols.Number, true); + } + let tempoAutomation: Automation = new Automation(); + tempoAutomation.isLinear = true; + tempoAutomation.type = AutomationType.Tempo; + tempoAutomation.value = this._syData as number; + master.tempoAutomation = tempoAutomation; + this._sy = this.newSy(); + } else if (syData === 'section') { + this._sy = this.newSy(); + if (this._sy !== AlphaTexSymbols.String) { + this.error('section', AlphaTexSymbols.String, true); + } + let text: string = this._syData as string; + this._sy = this.newSy(); + let marker: string = ''; + if (this._sy === AlphaTexSymbols.String) { + marker = text; + text = this._syData as string; + this._sy = this.newSy(); + } + let section: Section = new Section(); + section.marker = marker; + section.text = text; + master.section = section; + } else if (syData === 'tf') { + this._allowTuning = false; + this._sy = this.newSy(); + this._allowTuning = true; + switch (this._sy) { + case AlphaTexSymbols.String: + master.tripletFeel = this.parseTripletFeelFromString((this._syData as string).toLowerCase()); + break; + case AlphaTexSymbols.Number: + master.tripletFeel = this.parseTripletFeelFromInt(this._syData as number); + break; + default: + this.error('triplet-feel', AlphaTexSymbols.String, true); + break; + } + this._sy = this.newSy(); + } else if (syData === 'ac') { + master.isAnacrusis = true; + this._sy = this.newSy(); + } else { + if (bar.index === 0 && !this.handleStaffMeta()) { + this.error('measure-effects', AlphaTexSymbols.String, false); + } + } + } + } +} diff --git a/src/importer/BinaryStylesheet.ts b/src/importer/BinaryStylesheet.ts new file mode 100644 index 000000000..b6f650117 --- /dev/null +++ b/src/importer/BinaryStylesheet.ts @@ -0,0 +1,126 @@ +import { Score } from '@src/model/Score'; +import { ByteBuffer } from '@src/io/ByteBuffer'; +import { IOHelper } from '@src/io/IOHelper'; +import { GpBinaryHelpers } from '@src/importer/Gp3To5Importer'; +import { BendPoint } from '@src/model/BendPoint'; +import { Bounds } from '@src/rendering/utils/Bounds'; +import { Color } from '@src/model/Color'; + +enum DataType { + Boolean, + Integer, + Float, + String, + Point, + Size, + Rectangle, + Color +} + +/** + * A BinaryStylesheet from Guitar Pro 6 and 7 files. + * The BinaryStylesheet is a simple binary key-value store for additional settings + * related to the display of the music sheet. + * + * File: + * int32 (big endian) | Number of KeyValuePairs + * KeyValuePair[] | The raw records + * + * KeyValuePair: + * 1 Byte | length of the key + * n Bytes | key as utf8 encoded string + * 1 Byte | Data Type + * n Bytes | Value + * + * Values based on Data Type: + * 0 = bool + * 0===false + * 1 = int32 (big endian) + * 2 = float (big endian, IEEE) + * 3 = string + * int16 (big endian) | length of string + * n bytes | utf-8 encoded string + * 4 = point + * int32 (big endian) | X-coordinate + * int32 (big endian) | Y-coordinate + * 5 = size + * int32 (big endian) | Width + * int32 (big endian) | Height + * 6 = rectangle + * int32 (big endian) | X-coordinate + * int32 (big endian) | Y-coordinate + * int32 (big endian) | Width + * int32 (big endian) | Height + * 7 = color + * 1 byte | Red + * 1 byte | Green + * 1 byte | Blue + * 1 byte | Alpha + */ +export class BinaryStylesheet { + public readonly raw: Map = new Map(); + + public constructor(data: Uint8Array) { + // BinaryStylesheet apears to be big-endien + let readable: ByteBuffer = ByteBuffer.fromBuffer(data); + let entryCount: number = IOHelper.readInt32BE(readable); + for (let i: number = 0; i < entryCount; i++) { + let key: string = GpBinaryHelpers.gpReadString(readable, readable.readByte(), 'utf-8'); + let type: DataType = readable.readByte() as DataType; + switch (type) { + case DataType.Boolean: + let flag: boolean = readable.readByte() === 1; + this.addValue(key, flag); + break; + case DataType.Integer: + let ivalue: number = IOHelper.readInt32BE(readable); + this.addValue(key, ivalue); + break; + case DataType.Float: + let fvalue: number = GpBinaryHelpers.gpReadFloat(readable); + this.addValue(key, fvalue); + break; + case DataType.String: + let s: string = GpBinaryHelpers.gpReadString(readable, IOHelper.readInt16BE(readable), 'utf-8'); + this.addValue(key, s); + break; + case DataType.Point: + let x: number = IOHelper.readInt32BE(readable); + let y: number = IOHelper.readInt32BE(readable); + this.addValue(key, new BendPoint(x, y)); + break; + case DataType.Size: + let width: number = IOHelper.readInt32BE(readable); + let height: number = IOHelper.readInt32BE(readable); + this.addValue(key, new BendPoint(width, height)); + break; + case DataType.Rectangle: + let rect = new Bounds(); + rect.x = IOHelper.readInt32BE(readable); + rect.y = IOHelper.readInt32BE(readable); + rect.w = IOHelper.readInt32BE(readable); + rect.h = IOHelper.readInt32BE(readable); + this.addValue(key, rect); + break; + case DataType.Color: + let color: Color = GpBinaryHelpers.gpReadColor(readable, true); + this.addValue(key, color); + break; + } + } + } + + public apply(score: Score): void { + this.raw.forEach((value, key) => { + switch (key) { + case 'StandardNotation/hideDynamics': + score.stylesheet.hideDynamics = value as boolean; + break; + } + }); + } + + public addValue(key: string, value: unknown): void { + this.raw.set(key, value); + } +} diff --git a/src/importer/Gp3To5Importer.ts b/src/importer/Gp3To5Importer.ts new file mode 100644 index 000000000..9dac2c9c6 --- /dev/null +++ b/src/importer/Gp3To5Importer.ts @@ -0,0 +1,1244 @@ +import { GeneralMidi } from '@src/midi/GeneralMidi'; + +import { ScoreImporter } from '@src/importer/ScoreImporter'; +import { UnsupportedFormatError } from '@src/importer/UnsupportedFormatError'; + +import { IOHelper } from '@src/io/IOHelper'; +import { IReadable } from '@src/io/IReadable'; +import { AccentuationType } from '@src/model/AccentuationType'; +import { Automation, AutomationType } from '@src/model/Automation'; +import { Bar } from '@src/model/Bar'; +import { Beat } from '@src/model/Beat'; +import { BendPoint } from '@src/model/BendPoint'; +import { BrushType } from '@src/model/BrushType'; +import { Chord } from '@src/model/Chord'; +import { Clef } from '@src/model/Clef'; +import { Color } from '@src/model/Color'; +import { Duration } from '@src/model/Duration'; +import { DynamicValue } from '@src/model/DynamicValue'; +import { Fingers } from '@src/model/Fingers'; +import { GraceType } from '@src/model/GraceType'; +import { HarmonicType } from '@src/model/HarmonicType'; +import { KeySignature } from '@src/model/KeySignature'; +import { KeySignatureType } from '@src/model/KeySignatureType'; +import { Lyrics } from '@src/model/Lyrics'; +import { MasterBar } from '@src/model/MasterBar'; +import { Note } from '@src/model/Note'; +import { NoteAccidentalMode } from '@src/model/NoteAccidentalMode'; +import { PickStroke } from '@src/model/PickStroke'; +import { PlaybackInformation } from '@src/model/PlaybackInformation'; +import { Score } from '@src/model/Score'; +import { Section } from '@src/model/Section'; +import { SlideInType } from '@src/model/SlideInType'; +import { SlideOutType } from '@src/model/SlideOutType'; +import { Staff } from '@src/model/Staff'; +import { Track } from '@src/model/Track'; +import { TripletFeel } from '@src/model/TripletFeel'; +import { VibratoType } from '@src/model/VibratoType'; +import { Voice } from '@src/model/Voice'; + +import { Logger } from '@src/Logger'; +import { ModelUtils } from '@src/model/ModelUtils'; + +export class Gp3To5Importer extends ScoreImporter { + private static readonly VersionString: string = 'FICHIER GUITAR PRO '; + private _versionNumber: number = 0; + private _score!: Score; + private _globalTripletFeel: TripletFeel = TripletFeel.NoTripletFeel; + private _lyricsTrack: number = 0; + private _lyrics: Lyrics[] = []; + private _barCount: number = 0; + private _trackCount: number = 0; + private _playbackInfos: PlaybackInformation[] = []; + + public get name(): string { + return 'Guitar Pro 3-5'; + } + + public constructor() { + super(); + } + + public readScore(): Score { + this.readVersion(); + this._score = new Score(); + // basic song info + this.readScoreInformation(); + // triplet feel before Gp5 + if (this._versionNumber < 500) { + this._globalTripletFeel = GpBinaryHelpers.gpReadBool(this.data) + ? TripletFeel.Triplet8th + : TripletFeel.NoTripletFeel; + } + // beat lyrics + if (this._versionNumber >= 400) { + this.readLyrics(); + } + // rse master settings since GP5.1 + if (this._versionNumber >= 510) { + // master volume (4) + // master effect (4) + // master equalizer (10) + // master equalizer preset (1) + this.data.skip(19); + } + // page setup since GP5 + if (this._versionNumber >= 500) { + this.readPageSetup(); + this._score.tempoLabel = GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding); + } + // tempo stuff + this._score.tempo = IOHelper.readInt32LE(this.data); + if (this._versionNumber >= 510) { + GpBinaryHelpers.gpReadBool(this.data); // hide tempo? + } + // keysignature and octave + IOHelper.readInt32LE(this.data); + if (this._versionNumber >= 400) { + this.data.readByte(); + } + this.readPlaybackInfos(); + // repetition stuff + if (this._versionNumber >= 500) { + // "Coda" bar index (2) + // "Double Coda" bar index (2) + // "Segno" bar index (2) + // "Segno Segno" bar index (2) + // "Fine" bar index (2) + // "Da Capo" bar index (2) + // "Da Capo al Coda" bar index (2) + // "Da Capo al Double Coda" bar index (2) + // "Da Capo al Fine" bar index (2) + // "Da Segno" bar index (2) + // "Da Segno al Coda" bar index (2) + // "Da Segno al Double Coda" bar index (2) + // "Da Segno al Fine "bar index (2) + // "Da Segno Segno" bar index (2) + // "Da Segno Segno al Coda" bar index (2) + // "Da Segno Segno al Double Coda" bar index (2) + // "Da Segno Segno al Fine" bar index (2) + // "Da Coda" bar index (2) + // "Da Double Coda" bar index (2) + this.data.skip(38); + // unknown (4) + this.data.skip(4); + } + // contents + this._barCount = IOHelper.readInt32LE(this.data); + this._trackCount = IOHelper.readInt32LE(this.data); + this.readMasterBars(); + this.readTracks(); + this.readBars(); + this._score.finish(this.settings); + if (this._lyrics && this._lyricsTrack >= 0) { + this._score.tracks[this._lyricsTrack].applyLyrics(this._lyrics); + } + return this._score; + } + + public readVersion(): void { + let version: string = GpBinaryHelpers.gpReadStringByteLength(this.data, 30, this.settings.importer.encoding); + if (!version.startsWith(Gp3To5Importer.VersionString)) { + throw new UnsupportedFormatError('Unsupported format'); + } + version = version.substr(Gp3To5Importer.VersionString.length + 1); + let dot: number = version.indexOf(String.fromCharCode(46)); + this._versionNumber = 100 * parseInt(version.substr(0, dot)) + parseInt(version.substr(dot + 1)); + Logger.debug(this.name, 'Guitar Pro version ' + version + ' detected'); + } + + public readScoreInformation(): void { + this._score.title = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding); + this._score.subTitle = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding); + this._score.artist = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding); + this._score.album = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding); + this._score.words = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding); + this._score.music = + this._versionNumber >= 500 + ? GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding) + : this._score.words; + this._score.copyright = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding); + this._score.tab = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding); + this._score.instructions = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding); + let noticeLines: number = IOHelper.readInt32LE(this.data); + let notice: string = ''; + for (let i: number = 0; i < noticeLines; i++) { + if (i > 0) { + notice += '\r\n'; + } + notice += GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding)?.toString(); + } + this._score.notices = notice; + } + + public readLyrics(): void { + this._lyrics = []; + this._lyricsTrack = IOHelper.readInt32LE(this.data) - 1; + for (let i: number = 0; i < 5; i++) { + let lyrics: Lyrics = new Lyrics(); + lyrics.startBar = IOHelper.readInt32LE(this.data) - 1; + lyrics.text = GpBinaryHelpers.gpReadStringInt(this.data, this.settings.importer.encoding); + this._lyrics.push(lyrics); + } + } + + public readPageSetup(): void { + // Page Width (4) + // Page Heigth (4) + // Padding Left (4) + // Padding Right (4) + // Padding Top (4) + // Padding Bottom (4) + // Size Proportion(4) + // Header and Footer display flags (2) + this.data.skip(30); + // title format + // subtitle format + // artist format + // album format + // words format + // music format + // words and music format + // copyright format + // pagpublic enumber format + for (let i: number = 0; i < 10; i++) { + GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding); + } + } + + public readPlaybackInfos(): void { + this._playbackInfos = []; + for (let i: number = 0; i < 64; i++) { + let info: PlaybackInformation = new PlaybackInformation(); + info.primaryChannel = i; + info.secondaryChannel = i; + info.program = IOHelper.readInt32LE(this.data); + info.volume = this.data.readByte(); + info.balance = this.data.readByte(); + this.data.skip(6); + this._playbackInfos.push(info); + } + } + + public readMasterBars(): void { + for (let i: number = 0; i < this._barCount; i++) { + this.readMasterBar(); + } + } + + public readMasterBar(): void { + let previousMasterBar: MasterBar | null = null; + if (this._score.masterBars.length > 0) { + previousMasterBar = this._score.masterBars[this._score.masterBars.length - 1]; + } + let newMasterBar: MasterBar = new MasterBar(); + let flags: number = this.data.readByte(); + // time signature + if ((flags & 0x01) !== 0) { + newMasterBar.timeSignatureNumerator = this.data.readByte(); + } else if (previousMasterBar) { + newMasterBar.timeSignatureNumerator = previousMasterBar.timeSignatureNumerator; + } + if ((flags & 0x02) !== 0) { + newMasterBar.timeSignatureDenominator = this.data.readByte(); + } else if (previousMasterBar) { + newMasterBar.timeSignatureDenominator = previousMasterBar.timeSignatureDenominator; + } + // repeatings + newMasterBar.isRepeatStart = (flags & 0x04) !== 0; + if ((flags & 0x08) !== 0) { + newMasterBar.repeatCount = this.data.readByte() + (this._versionNumber >= 500 ? 0 : 1); + } + // alternate endings + if ((flags & 0x10) !== 0) { + if (this._versionNumber < 500) { + let currentMasterBar: MasterBar | null = previousMasterBar; + // get the already existing alternatives to ignore them + let existentAlternatives: number = 0; + while (currentMasterBar) { + // found another repeat ending? + if (currentMasterBar.isRepeatEnd && currentMasterBar !== previousMasterBar) { + break; + } + // found the opening? + if (currentMasterBar.isRepeatStart) { + break; + } + existentAlternatives = existentAlternatives | currentMasterBar.alternateEndings; + currentMasterBar = currentMasterBar.previousMasterBar; + } + // now calculate the alternative for this bar + let repeatAlternative: number = 0; + let repeatMask: number = this.data.readByte(); + for (let i: number = 0; i < 8; i++) { + // only add the repeating if it is not existing + let repeating: number = 1 << i; + if (repeatMask > i && (existentAlternatives & repeating) === 0) { + repeatAlternative = repeatAlternative | repeating; + } + } + newMasterBar.alternateEndings = repeatAlternative; + } else { + newMasterBar.alternateEndings = this.data.readByte(); + } + } + // marker + if ((flags & 0x20) !== 0) { + let section: Section = new Section(); + section.text = GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding); + section.marker = ''; + GpBinaryHelpers.gpReadColor(this.data, false); + newMasterBar.section = section; + } + // keysignature + if ((flags & 0x40) !== 0) { + newMasterBar.keySignature = IOHelper.readSInt8(this.data) as KeySignature; + newMasterBar.keySignatureType = this.data.readByte() as KeySignatureType; + } else if (previousMasterBar) { + newMasterBar.keySignature = previousMasterBar.keySignature; + newMasterBar.keySignatureType = previousMasterBar.keySignatureType; + } + if (this._versionNumber >= 500 && (flags & 0x03) !== 0) { + this.data.skip(4); + } + // better alternate ending mask in GP5 + if (this._versionNumber >= 500 && (flags & 0x10) === 0) { + newMasterBar.alternateEndings = this.data.readByte(); + } + // tripletfeel + if (this._versionNumber >= 500) { + let tripletFeel: number = this.data.readByte(); + switch (tripletFeel) { + case 1: + newMasterBar.tripletFeel = TripletFeel.Triplet8th; + break; + case 2: + newMasterBar.tripletFeel = TripletFeel.Triplet16th; + break; + } + this.data.readByte(); + } else { + newMasterBar.tripletFeel = this._globalTripletFeel; + } + newMasterBar.isDoubleBar = (flags & 0x80) !== 0; + this._score.addMasterBar(newMasterBar); + } + + public readTracks(): void { + for (let i: number = 0; i < this._trackCount; i++) { + this.readTrack(); + } + } + + public readTrack(): void { + let newTrack: Track = new Track(); + newTrack.ensureStaveCount(1); + this._score.addTrack(newTrack); + let mainStaff: Staff = newTrack.staves[0]; + let flags: number = this.data.readByte(); + newTrack.name = GpBinaryHelpers.gpReadStringByteLength(this.data, 40, this.settings.importer.encoding); + if ((flags & 0x01) !== 0) { + mainStaff.isPercussion = true; + } + let stringCount: number = IOHelper.readInt32LE(this.data); + let tuning: number[] = []; + for (let i: number = 0; i < 7; i++) { + let stringTuning: number = IOHelper.readInt32LE(this.data); + if (stringCount > i) { + tuning.push(stringTuning); + } + } + mainStaff.tuning = tuning; + let port: number = IOHelper.readInt32LE(this.data); + let index: number = IOHelper.readInt32LE(this.data) - 1; + let effectChannel: number = IOHelper.readInt32LE(this.data) - 1; + this.data.skip(4); // Fretcount + + if (index >= 0 && index < this._playbackInfos.length) { + let info: PlaybackInformation = this._playbackInfos[index]; + info.port = port; + info.isSolo = (flags & 0x10) !== 0; + info.isMute = (flags & 0x20) !== 0; + info.secondaryChannel = effectChannel; + if (GeneralMidi.isGuitar(info.program)) { + mainStaff.displayTranspositionPitch = -12; + } + newTrack.playbackInfo = info; + } + mainStaff.capo = IOHelper.readInt32LE(this.data); + newTrack.color = GpBinaryHelpers.gpReadColor(this.data, false); + if (this._versionNumber >= 500) { + // flags for + // 0x01 -> show tablature + // 0x02 -> show standard notation + this.data.readByte(); + // flags for + // 0x02 -> auto let ring + // 0x04 -> auto brush + this.data.readByte(); + // unknown + this.data.skip(43); + } + // unknown + if (this._versionNumber >= 510) { + this.data.skip(4); + GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding); + GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding); + } + } + + public readBars(): void { + for (let i: number = 0; i < this._barCount; i++) { + for (let t: number = 0; t < this._trackCount; t++) { + this.readBar(this._score.tracks[t]); + } + } + } + + public readBar(track: Track): void { + let newBar: Bar = new Bar(); + let mainStaff: Staff = track.staves[0]; + if (mainStaff.isPercussion) { + newBar.clef = Clef.Neutral; + } + mainStaff.addBar(newBar); + let voiceCount: number = 1; + if (this._versionNumber >= 500) { + this.data.readByte(); + voiceCount = 2; + } + for (let v: number = 0; v < voiceCount; v++) { + this.readVoice(track, newBar); + } + } + + public readVoice(track: Track, bar: Bar): void { + let beatCount: number = IOHelper.readInt32LE(this.data); + if (beatCount === 0) { + return; + } + let newVoice: Voice = new Voice(); + bar.addVoice(newVoice); + for (let i: number = 0; i < beatCount; i++) { + this.readBeat(track, bar, newVoice); + } + } + + public readBeat(track: Track, bar: Bar, voice: Voice): void { + let newBeat: Beat = new Beat(); + let flags: number = this.data.readByte(); + if ((flags & 0x01) !== 0) { + newBeat.dots = 1; + } + if ((flags & 0x40) !== 0) { + let type: number = this.data.readByte(); + newBeat.isEmpty = (type & 0x02) === 0; + } + voice.addBeat(newBeat); + let duration: number = IOHelper.readSInt8(this.data); + switch (duration) { + case -2: + newBeat.duration = Duration.Whole; + break; + case -1: + newBeat.duration = Duration.Half; + break; + case 0: + newBeat.duration = Duration.Quarter; + break; + case 1: + newBeat.duration = Duration.Eighth; + break; + case 2: + newBeat.duration = Duration.Sixteenth; + break; + case 3: + newBeat.duration = Duration.ThirtySecond; + break; + case 4: + newBeat.duration = Duration.SixtyFourth; + break; + default: + newBeat.duration = Duration.Quarter; + break; + } + if ((flags & 0x20) !== 0) { + newBeat.tupletNumerator = IOHelper.readInt32LE(this.data); + switch (newBeat.tupletNumerator) { + case 1: + newBeat.tupletDenominator = 1; + break; + case 3: + newBeat.tupletDenominator = 2; + break; + case 5: + case 6: + case 7: + newBeat.tupletDenominator = 4; + break; + case 9: + case 10: + case 11: + case 12: + case 13: + newBeat.tupletDenominator = 8; + break; + case 2: + case 4: + case 8: + break; + default: + newBeat.tupletNumerator = 1; + newBeat.tupletDenominator = 1; + break; + } + } + if ((flags & 0x02) !== 0) { + this.readChord(newBeat); + } + if ((flags & 0x04) !== 0) { + newBeat.text = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding); + } + if ((flags & 0x08) !== 0) { + this.readBeatEffects(newBeat); + } + if ((flags & 0x10) !== 0) { + this.readMixTableChange(newBeat); + } + let stringFlags: number = this.data.readByte(); + for (let i: number = 6; i >= 0; i--) { + if ((stringFlags & (1 << i)) !== 0 && 6 - i < bar.staff.tuning.length) { + this.readNote(track, bar, voice, newBeat, 6 - i); + } + } + if (this._versionNumber >= 500) { + this.data.readByte(); + let flag: number = this.data.readByte(); + if ((flag & 0x08) !== 0) { + this.data.readByte(); + } + } + } + + public readChord(beat: Beat): void { + let chord: Chord = new Chord(); + let chordId: string = ModelUtils.newGuid(); + if (this._versionNumber >= 500) { + this.data.skip(17); + chord.name = GpBinaryHelpers.gpReadStringByteLength(this.data, 21, this.settings.importer.encoding); + this.data.skip(4); + chord.firstFret = IOHelper.readInt32LE(this.data); + for (let i: number = 0; i < 7; i++) { + let fret: number = IOHelper.readInt32LE(this.data); + if (i < beat.voice.bar.staff.tuning.length) { + chord.strings.push(fret); + } + } + let numberOfBarres: number = this.data.readByte(); + let barreFrets: Uint8Array = new Uint8Array(5); + this.data.read(barreFrets, 0, barreFrets.length); + for (let i: number = 0; i < numberOfBarres; i++) { + chord.barreFrets.push(barreFrets[i]); + } + this.data.skip(26); + } else { + if (this.data.readByte() !== 0) { + // gp4 + if (this._versionNumber >= 400) { + // Sharp (1) + // Unused (3) + // Root (1) + // Major/Minor (1) + // Nin,Eleven or Thirteen (1) + // Bass (4) + // Diminished/Augmented (4) + // Add (1) + this.data.skip(16); + chord.name = GpBinaryHelpers.gpReadStringByteLength(this.data, 21, this.settings.importer.encoding); + // Unused (2) + // Fifth (1) + // Ninth (1) + // Eleventh (1) + this.data.skip(4); + chord.firstFret = IOHelper.readInt32LE(this.data); + for (let i: number = 0; i < 7; i++) { + let fret: number = IOHelper.readInt32LE(this.data); + if (i < beat.voice.bar.staff.tuning.length) { + chord.strings.push(fret); + } + } + let numberOfBarres: number = this.data.readByte(); + let barreFrets: Uint8Array = new Uint8Array(5); + this.data.read(barreFrets, 0, barreFrets.length); + for (let i: number = 0; i < numberOfBarres; i++) { + chord.barreFrets.push(barreFrets[i]); + } + // Barree end (5) + // Omission1,3,5,7,9,11,13 (7) + // Unused (1) + // Fingering (7) + // Show Diagram Fingering (1) + // ?? + this.data.skip(26); + } else { + // unknown + this.data.skip(25); + chord.name = GpBinaryHelpers.gpReadStringByteLength(this.data, 34, this.settings.importer.encoding); + chord.firstFret = IOHelper.readInt32LE(this.data); + for (let i: number = 0; i < 6; i++) { + let fret: number = IOHelper.readInt32LE(this.data); + if (i < beat.voice.bar.staff.tuning.length) { + chord.strings.push(fret); + } + } + // unknown + this.data.skip(36); + } + } else { + let strings: number = this._versionNumber >= 406 ? 7 : 6; + chord.name = GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding); + chord.firstFret = IOHelper.readInt32LE(this.data); + if (chord.firstFret > 0) { + for (let i: number = 0; i < strings; i++) { + let fret: number = IOHelper.readInt32LE(this.data); + if (i < beat.voice.bar.staff.tuning.length) { + chord.strings.push(fret); + } + } + } + } + } + if (chord.name) { + beat.chordId = chordId; + beat.voice.bar.staff.addChord(beat.chordId, chord); + } + } + + public readBeatEffects(beat: Beat): void { + let flags: number = this.data.readByte(); + let flags2: number = 0; + if (this._versionNumber >= 400) { + flags2 = this.data.readByte(); + } + beat.fadeIn = (flags & 0x10) !== 0; + if ((this._versionNumber < 400 && (flags & 0x01) !== 0) || (flags & 0x02) !== 0) { + beat.vibrato = VibratoType.Slight; + } + beat.hasRasgueado = (flags2 & 0x01) !== 0; + if ((flags & 0x20) !== 0 && this._versionNumber >= 400) { + let slapPop: number = IOHelper.readSInt8(this.data); + switch (slapPop) { + case 1: + beat.tap = true; + break; + case 2: + beat.slap = true; + break; + case 3: + beat.pop = true; + break; + } + } else if ((flags & 0x20) !== 0) { + let slapPop: number = IOHelper.readSInt8(this.data); + switch (slapPop) { + case 1: + beat.tap = true; + break; + case 2: + beat.slap = true; + break; + case 3: + beat.pop = true; + break; + } + this.data.skip(4); + } + if ((flags2 & 0x04) !== 0) { + this.readTremoloBarEffect(beat); + } + if ((flags & 0x40) !== 0) { + let strokeUp: number = 0; + let strokeDown: number = 0; + if (this._versionNumber < 500) { + strokeDown = this.data.readByte(); + strokeUp = this.data.readByte(); + } else { + strokeUp = this.data.readByte(); + strokeDown = this.data.readByte(); + } + if (strokeUp > 0) { + beat.brushType = BrushType.BrushUp; + beat.brushDuration = Gp3To5Importer.toStrokeValue(strokeUp); + } else if (strokeDown > 0) { + beat.brushType = BrushType.BrushDown; + beat.brushDuration = Gp3To5Importer.toStrokeValue(strokeDown); + } + } + if ((flags2 & 0x02) !== 0) { + switch (IOHelper.readSInt8(this.data)) { + case 0: + beat.pickStroke = PickStroke.None; + break; + case 1: + beat.pickStroke = PickStroke.Up; + break; + case 2: + beat.pickStroke = PickStroke.Down; + break; + } + } + } + + public readTremoloBarEffect(beat: Beat): void { + this.data.readByte(); // type + + IOHelper.readInt32LE(this.data); // value + + let pointCount: number = IOHelper.readInt32LE(this.data); + if (pointCount > 0) { + for (let i: number = 0; i < pointCount; i++) { + let point: BendPoint = new BendPoint(0, 0); + point.offset = IOHelper.readInt32LE(this.data); // 0...60 + + point.value = (IOHelper.readInt32LE(this.data) / Gp3To5Importer.BendStep) | 0; // 0..12 (amount of quarters) + + GpBinaryHelpers.gpReadBool(this.data); // vibrato + + beat.addWhammyBarPoint(point); + } + } + } + + private static toStrokeValue(value: number): number { + switch (value) { + case 1: + return 30; + case 2: + return 30; + case 3: + return 60; + case 4: + return 120; + case 5: + return 240; + case 6: + return 480; + default: + return 0; + } + } + + public readMixTableChange(beat: Beat): void { + let tableChange: MixTableChange = new MixTableChange(); + tableChange.instrument = IOHelper.readSInt8(this.data); + if (this._versionNumber >= 500) { + this.data.skip(16); // Rse Info + } + tableChange.volume = IOHelper.readSInt8(this.data); + tableChange.balance = IOHelper.readSInt8(this.data); + let chorus: number = IOHelper.readSInt8(this.data); + let reverb: number = IOHelper.readSInt8(this.data); + let phaser: number = IOHelper.readSInt8(this.data); + let tremolo: number = IOHelper.readSInt8(this.data); + if (this._versionNumber >= 500) { + tableChange.tempoName = GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding); + } + tableChange.tempo = IOHelper.readInt32LE(this.data); + // durations + if (tableChange.volume >= 0) { + this.data.readByte(); + } + if (tableChange.balance >= 0) { + this.data.readByte(); + } + if (chorus >= 0) { + this.data.readByte(); + } + if (reverb >= 0) { + this.data.readByte(); + } + if (phaser >= 0) { + this.data.readByte(); + } + if (tremolo >= 0) { + this.data.readByte(); + } + if (tableChange.tempo >= 0) { + tableChange.duration = IOHelper.readSInt8(this.data); + if (this._versionNumber >= 510) { + this.data.readByte(); // hideTempo (bool) + } + } + if (this._versionNumber >= 400) { + this.data.readByte(); // all tracks flag + } + // unknown + if (this._versionNumber >= 500) { + this.data.readByte(); + } + // unknown + if (this._versionNumber >= 510) { + GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding); + GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding); + } + if (tableChange.volume >= 0) { + let volumeAutomation: Automation = new Automation(); + volumeAutomation.isLinear = true; + volumeAutomation.type = AutomationType.Volume; + volumeAutomation.value = tableChange.volume; + beat.automations.push(volumeAutomation); + } + if (tableChange.balance >= 0) { + let balanceAutomation: Automation = new Automation(); + balanceAutomation.isLinear = true; + balanceAutomation.type = AutomationType.Balance; + balanceAutomation.value = tableChange.balance; + beat.automations.push(balanceAutomation); + } + if (tableChange.instrument >= 0) { + let instrumentAutomation: Automation = new Automation(); + instrumentAutomation.isLinear = true; + instrumentAutomation.type = AutomationType.Instrument; + instrumentAutomation.value = tableChange.instrument; + beat.automations.push(instrumentAutomation); + } + if (tableChange.tempo >= 0) { + let tempoAutomation: Automation = new Automation(); + tempoAutomation.isLinear = true; + tempoAutomation.type = AutomationType.Tempo; + tempoAutomation.value = tableChange.tempo; + beat.automations.push(tempoAutomation); + beat.voice.bar.masterBar.tempoAutomation = tempoAutomation; + } + } + + public readNote(track: Track, bar: Bar, voice: Voice, beat: Beat, stringIndex: number): void { + let newNote: Note = new Note(); + newNote.string = bar.staff.tuning.length - stringIndex; + let flags: number = this.data.readByte(); + if ((flags & 0x02) !== 0) { + newNote.accentuated = AccentuationType.Heavy; + } else if ((flags & 0x40) !== 0) { + newNote.accentuated = AccentuationType.Normal; + } + newNote.isGhost = (flags & 0x04) !== 0; + if ((flags & 0x20) !== 0) { + let noteType: number = this.data.readByte(); + if (noteType === 3) { + newNote.isDead = true; + } else if (noteType === 2) { + newNote.isTieDestination = true; + } + } + if ((flags & 0x01) !== 0 && this._versionNumber < 500) { + this.data.readByte(); // duration + + this.data.readByte(); // tuplet + } + if ((flags & 0x10) !== 0) { + let dynamicNumber: number = IOHelper.readSInt8(this.data); + newNote.dynamics = this.toDynamicValue(dynamicNumber); + beat.dynamics = newNote.dynamics; + } + if ((flags & 0x20) !== 0) { + newNote.fret = IOHelper.readSInt8(this.data); + } + if ((flags & 0x80) !== 0) { + newNote.leftHandFinger = IOHelper.readSInt8(this.data) as Fingers; + newNote.rightHandFinger = IOHelper.readSInt8(this.data) as Fingers; + newNote.isFingering = true; + } + if (this._versionNumber >= 500) { + if ((flags & 0x01) !== 0) { + newNote.durationPercent = GpBinaryHelpers.gpReadDouble(this.data); + } + let flags2: number = this.data.readByte(); + newNote.accidentalMode = + (flags2 & 0x02) !== 0 ? NoteAccidentalMode.SwapAccidentals : NoteAccidentalMode.Default; + } + beat.addNote(newNote); + if ((flags & 0x08) !== 0) { + this.readNoteEffects(track, voice, beat, newNote); + } + } + + public toDynamicValue(value: number): DynamicValue { + switch (value) { + case 1: + return DynamicValue.PPP; + case 2: + return DynamicValue.PP; + case 3: + return DynamicValue.P; + case 4: + return DynamicValue.MP; + case 5: + return DynamicValue.MF; + case 6: + return DynamicValue.F; + case 7: + return DynamicValue.FF; + case 8: + return DynamicValue.FFF; + default: + return DynamicValue.F; + } + } + + public readNoteEffects(track: Track, voice: Voice, beat: Beat, note: Note): void { + let flags: number = this.data.readByte(); + let flags2: number = 0; + if (this._versionNumber >= 400) { + flags2 = this.data.readByte(); + } + if ((flags & 0x01) !== 0) { + this.readBend(note); + } + if ((flags & 0x10) !== 0) { + this.readGrace(voice, note); + } + if ((flags2 & 0x04) !== 0) { + this.readTremoloPicking(beat); + } + if ((flags2 & 0x08) !== 0) { + this.readSlide(note); + } else if (this._versionNumber < 400) { + if ((flags & 0x04) !== 0) { + note.slideOutType = SlideOutType.Shift; + } + } + if ((flags2 & 0x10) !== 0) { + this.readArtificialHarmonic(note); + } else if (this._versionNumber < 400) { + if ((flags & 0x04) !== 0) { + note.harmonicType = HarmonicType.Natural; + note.harmonicValue = this.deltaFretToHarmonicValue(note.fret); + } + if ((flags & 0x08) !== 0) { + note.harmonicType = HarmonicType.Artificial; + } + } + if ((flags2 & 0x20) !== 0) { + this.readTrill(note); + } + note.isLetRing = (flags & 0x08) !== 0; + note.isHammerPullOrigin = (flags & 0x02) !== 0; + if ((flags2 & 0x40) !== 0) { + note.vibrato = VibratoType.Slight; + } + note.isPalmMute = (flags2 & 0x02) !== 0; + note.isStaccato = (flags2 & 0x01) !== 0; + } + + private static readonly BendStep: number = 25; + + public readBend(note: Note): void { + this.data.readByte(); // type + + IOHelper.readInt32LE(this.data); // value + + let pointCount: number = IOHelper.readInt32LE(this.data); + if (pointCount > 0) { + for (let i: number = 0; i < pointCount; i++) { + let point: BendPoint = new BendPoint(0, 0); + point.offset = IOHelper.readInt32LE(this.data); // 0...60 + + point.value = (IOHelper.readInt32LE(this.data) / Gp3To5Importer.BendStep) | 0; // 0..12 (amount of quarters) + + GpBinaryHelpers.gpReadBool(this.data); // vibrato + + note.addBendPoint(point); + } + } + } + + public readGrace(voice: Voice, note: Note): void { + let graceBeat: Beat = new Beat(); + let graceNote: Note = new Note(); + graceNote.string = note.string; + graceNote.fret = IOHelper.readSInt8(this.data); + graceBeat.duration = Duration.ThirtySecond; + graceBeat.dynamics = this.toDynamicValue(IOHelper.readSInt8(this.data)); + let transition: number = IOHelper.readSInt8(this.data); + switch (transition) { + case 0: + break; + case 1: + graceNote.slideOutType = SlideOutType.Legato; + graceNote.slideTarget = note; + break; + case 2: + break; + case 3: + graceNote.isHammerPullOrigin = true; + break; + } + graceNote.dynamics = graceBeat.dynamics; + this.data.skip(1); // duration + + if (this._versionNumber < 500) { + graceBeat.graceType = GraceType.BeforeBeat; + } else { + let flags: number = this.data.readByte(); + graceNote.isDead = (flags & 0x01) !== 0; + graceBeat.graceType = (flags & 0x02) !== 0 ? GraceType.OnBeat : GraceType.BeforeBeat; + } + voice.addGraceBeat(graceBeat); + graceBeat.addNote(graceNote); + } + + public readTremoloPicking(beat: Beat): void { + let speed: number = this.data.readByte(); + switch (speed) { + case 1: + beat.tremoloSpeed = Duration.Eighth; + break; + case 2: + beat.tremoloSpeed = Duration.Sixteenth; + break; + case 3: + beat.tremoloSpeed = Duration.ThirtySecond; + break; + } + } + + public readSlide(note: Note): void { + if (this._versionNumber >= 500) { + let type: number = IOHelper.readSInt8(this.data); + if ((type & 1) !== 0) { + note.slideOutType = SlideOutType.Shift; + } else if ((type & 2) !== 0) { + note.slideOutType = SlideOutType.Legato; + } else if ((type & 4) !== 0) { + note.slideOutType = SlideOutType.OutDown; + } else if ((type & 8) !== 0) { + note.slideOutType = SlideOutType.OutUp; + } + if ((type & 16) !== 0) { + note.slideInType = SlideInType.IntoFromBelow; + } else if ((type & 32) !== 0) { + note.slideInType = SlideInType.IntoFromAbove; + } + } else { + let type: number = IOHelper.readSInt8(this.data); + switch (type) { + case 1: + note.slideOutType = SlideOutType.Shift; + break; + case 2: + note.slideOutType = SlideOutType.Legato; + break; + case 3: + note.slideOutType = SlideOutType.OutDown; + break; + case 4: + note.slideOutType = SlideOutType.OutUp; + break; + case -1: + note.slideInType = SlideInType.IntoFromBelow; + break; + case -2: + note.slideInType = SlideInType.IntoFromAbove; + break; + } + } + } + + public readArtificialHarmonic(note: Note): void { + let type: number = this.data.readByte(); + if (this._versionNumber >= 500) { + switch (type) { + case 1: + note.harmonicType = HarmonicType.Natural; + note.harmonicValue = this.deltaFretToHarmonicValue(note.fret); + break; + case 2: + /*let _harmonicTone: number = */ this.data.readByte(); + /*let _harmonicKey: number = */ this.data.readByte(); + /*let _harmonicOctaveOffset: number = */ this.data.readByte(); + note.harmonicType = HarmonicType.Artificial; + break; + case 3: + note.harmonicType = HarmonicType.Tap; + note.harmonicValue = this.deltaFretToHarmonicValue(this.data.readByte()); + break; + case 4: + note.harmonicType = HarmonicType.Pinch; + note.harmonicValue = 12; + break; + case 5: + note.harmonicType = HarmonicType.Semi; + note.harmonicValue = 12; + break; + } + } else if (this._versionNumber >= 400) { + switch (type) { + case 1: + note.harmonicType = HarmonicType.Natural; + break; + case 3: + note.harmonicType = HarmonicType.Tap; + break; + case 4: + note.harmonicType = HarmonicType.Pinch; + break; + case 5: + note.harmonicType = HarmonicType.Semi; + break; + case 15: + note.harmonicType = HarmonicType.Artificial; + break; + case 17: + note.harmonicType = HarmonicType.Artificial; + break; + case 22: + note.harmonicType = HarmonicType.Artificial; + break; + } + } + } + + public deltaFretToHarmonicValue(deltaFret: number): number { + switch (deltaFret) { + case 2: + return 2.4; + case 3: + return 3.2; + case 4: + case 5: + case 7: + case 9: + case 12: + case 16: + case 17: + case 19: + case 24: + return deltaFret; + case 8: + return 8.2; + case 10: + return 9.6; + case 14: + case 15: + return 14.7; + case 21: + case 22: + return 21.7; + default: + return 12; + } + } + + public readTrill(note: Note): void { + note.trillValue = this.data.readByte() + note.stringTuning; + switch (this.data.readByte()) { + case 1: + note.trillSpeed = Duration.Sixteenth; + break; + case 2: + note.trillSpeed = Duration.ThirtySecond; + break; + case 3: + note.trillSpeed = Duration.SixtyFourth; + break; + } + } +} + +export class GpBinaryHelpers { + public static gpReadDouble(data: IReadable): number { + let bytes: Uint8Array = new Uint8Array(8); + data.read(bytes, 0, bytes.length); + + let array: Float64Array = new Float64Array(bytes.buffer); + return array[0]; + } + + public static gpReadFloat(data: IReadable): number { + let bytes: Uint8Array = new Uint8Array(4); + bytes[3] = data.readByte(); + bytes[2] = data.readByte(); + bytes[2] = data.readByte(); + bytes[1] = data.readByte(); + + let array: Float32Array = new Float32Array(bytes.buffer); + return array[0]; + } + + public static gpReadColor(data: IReadable, readAlpha: boolean = false): Color { + let r: number = data.readByte(); + let g: number = data.readByte(); + let b: number = data.readByte(); + let a: number = 255; + if (readAlpha) { + a = data.readByte(); + } else { + data.skip(1); + } + return new Color(r, g, b, a); + } + + public static gpReadBool(data: IReadable): boolean { + return data.readByte() !== 0; + } + + /** + * Skips an integer (4byte) and reads a string using + * a bytesize + */ + public static gpReadStringIntUnused(data: IReadable, encoding: string): string { + data.skip(4); + return GpBinaryHelpers.gpReadString(data, data.readByte(), encoding); + } + + /** + * Reads an integer as size, and then the string itself + */ + public static gpReadStringInt(data: IReadable, encoding: string): string { + return GpBinaryHelpers.gpReadString(data, IOHelper.readInt32LE(data), encoding); + } + + /** + * Reads an integer as size, skips a byte and reads the string itself + */ + public static gpReadStringIntByte(data: IReadable, encoding: string): string { + let length: number = IOHelper.readInt32LE(data) - 1; + data.readByte(); + return GpBinaryHelpers.gpReadString(data, length, encoding); + } + + public static gpReadString(data: IReadable, length: number, encoding: string): string { + let b: Uint8Array = new Uint8Array(length); + data.read(b, 0, b.length); + return IOHelper.toString(b, encoding); + } + + /** + * Reads a byte as size and the string itself. + * Additionally it is ensured the specified amount of bytes is read. + * @param data the data to read from. + * @param length the amount of bytes to read + * @param encoding The encoding to use to decode the byte into a string + * @returns + */ + public static gpReadStringByteLength(data: IReadable, length: number, encoding: string): string { + let stringLength: number = data.readByte(); + let s: string = GpBinaryHelpers.gpReadString(data, stringLength, encoding); + if (stringLength < length) { + data.skip(length - stringLength); + } + return s; + } +} + +/** + * A mixtablechange describes several track changes. + */ +class MixTableChange { + public volume: number = -1; + public balance: number = -1; + public instrument: number = -1; + public tempoName: string = ""; + public tempo: number = -1; + public duration: number = -1; +} diff --git a/src/importer/Gp7Importer.ts b/src/importer/Gp7Importer.ts new file mode 100644 index 000000000..5ac7dcd3e --- /dev/null +++ b/src/importer/Gp7Importer.ts @@ -0,0 +1,83 @@ +import { BinaryStylesheet } from '@src/importer/BinaryStylesheet'; +import { GpifParser } from '@src/importer/GpifParser'; +import { PartConfiguration } from '@src/importer/PartConfiguration'; +import { ScoreImporter } from '@src/importer/ScoreImporter'; +import { UnsupportedFormatError } from '@src/importer/UnsupportedFormatError'; + +import { Score } from '@src/model/Score'; + +import { Logger } from '@src/Logger'; + +import { ZipEntry, ZipReader } from '@src/zip/ZipReader'; +import { IOHelper } from '@src/io/IOHelper'; + +/** + * This ScoreImporter can read Guitar Pro 7 (gp) files. + */ +export class Gp7Importer extends ScoreImporter { + public get name(): string { + return 'Guitar Pro 7'; + } + + public constructor() { + super(); + } + + public readScore(): Score { + // at first we need to load the binary file system + // from the GPX container + Logger.debug(this.name, 'Loading ZIP entries'); + let fileSystem: ZipReader = new ZipReader(this.data); + let entries: ZipEntry[]; + try { + entries = fileSystem.read(); + } catch (e) { + throw new UnsupportedFormatError('No Zip archive', e); + } + + Logger.debug(this.name, 'Zip entries loaded'); + let xml: string | null = null; + let binaryStylesheetData: Uint8Array | null = null; + let partConfigurationData: Uint8Array | null = null; + for (let entry of entries) { + switch (entry.fileName) { + case 'score.gpif': + xml = IOHelper.toString(entry.data, this.settings.importer.encoding); + break; + case 'BinaryStylesheet': + binaryStylesheetData = entry.data; + break; + case 'PartConfiguration': + partConfigurationData = entry.data; + break; + } + } + + if (!xml) { + throw new UnsupportedFormatError('No score.gpif found in zip archive'); + } + + // the score.gpif file within this filesystem stores + // the score information as XML we need to parse. + Logger.debug(this.name, 'Start Parsing score.gpif'); + let gpifParser: GpifParser = new GpifParser(); + gpifParser.parseXml(xml, this.settings); + Logger.debug(this.name, 'score.gpif parsed'); + let score: Score = gpifParser.score; + + if (binaryStylesheetData) { + Logger.debug(this.name, 'Start Parsing BinaryStylesheet'); + let stylesheet: BinaryStylesheet = new BinaryStylesheet(binaryStylesheetData); + stylesheet.apply(score); + Logger.debug(this.name, 'BinaryStylesheet parsed'); + } + + if (partConfigurationData) { + Logger.debug(this.name, 'Start Parsing Part Configuration'); + let partConfigurationParser: PartConfiguration = new PartConfiguration(partConfigurationData); + partConfigurationParser.apply(score); + Logger.debug(this.name, 'Part Configuration parsed'); + } + return score; + } +} diff --git a/src/importer/GpifParser.ts b/src/importer/GpifParser.ts new file mode 100644 index 000000000..56cc7d6f8 --- /dev/null +++ b/src/importer/GpifParser.ts @@ -0,0 +1,1825 @@ +import { UnsupportedFormatError } from '@src/importer/UnsupportedFormatError'; +import { AccentuationType } from '@src/model/AccentuationType'; +import { Automation, AutomationType } from '@src/model/Automation'; +import { Bar } from '@src/model/Bar'; +import { Beat } from '@src/model/Beat'; +import { BendPoint } from '@src/model/BendPoint'; +import { BrushType } from '@src/model/BrushType'; +import { Chord } from '@src/model/Chord'; +import { Clef } from '@src/model/Clef'; +import { Color } from '@src/model/Color'; +import { CrescendoType } from '@src/model/CrescendoType'; +import { Duration } from '@src/model/Duration'; +import { DynamicValue } from '@src/model/DynamicValue'; +import { Fermata, FermataType } from '@src/model/Fermata'; +import { Fingers } from '@src/model/Fingers'; +import { GraceType } from '@src/model/GraceType'; +import { HarmonicType } from '@src/model/HarmonicType'; +import { KeySignature } from '@src/model/KeySignature'; +import { KeySignatureType } from '@src/model/KeySignatureType'; +import { Lyrics } from '@src/model/Lyrics'; +import { MasterBar } from '@src/model/MasterBar'; +import { Note } from '@src/model/Note'; +import { Ottavia } from '@src/model/Ottavia'; +import { PickStroke } from '@src/model/PickStroke'; +import { Score } from '@src/model/Score'; +import { Section } from '@src/model/Section'; +import { SimileMark } from '@src/model/SimileMark'; +import { SlideInType } from '@src/model/SlideInType'; +import { SlideOutType } from '@src/model/SlideOutType'; +import { Staff } from '@src/model/Staff'; +import { Track } from '@src/model/Track'; +import { TripletFeel } from '@src/model/TripletFeel'; +import { VibratoType } from '@src/model/VibratoType'; +import { Voice } from '@src/model/Voice'; +import { Settings } from '@src/Settings'; +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'; + +/** + * This structure represents a duration within a gpif + */ +class GpifRhythm { + public dots: number = 0; + public tupletDenominator: number = -1; + public tupletNumerator: number = -1; + public value: Duration = Duration.Quarter; +} + +/** + * This public class can parse a score.gpif xml file into the model structure + */ +export class GpifParser { + private static readonly InvalidId: string = '-1'; + + /** + * GPX range: 0-100 + * Internal range: 0 - 60 + */ + private static readonly BendPointPositionFactor: number = BendPoint.MaxPosition / 100.0; + + /** + * GPIF: 25 per quarternote + * Internal Range: 1 per quarter note + */ + private static readonly BendPointValueFactor: number = 1 / 25.0; + + public score!: Score; + + private _masterTrackAutomations!: Map; + private _tracksMapping!: string[]; + private _tracksById!: Map; + private _masterBars!: MasterBar[]; + private _barsOfMasterBar!: Array; + private _barsById!: Map; + private _voicesOfBar!: Map; + private _voiceById!: Map; + private _beatsOfVoice!: Map; + private _rhythmOfBeat!: Map; + private _beatById!: Map; + private _rhythmById!: Map; + private _noteById!: Map; + private _notesOfBeat!: Map; + private _tappedNotes!: Map; + private _lyricsByTrack!: Map; + private _hasAnacrusis: boolean = false; + + public parseXml(xml: string, settings: Settings): void { + this._masterTrackAutomations = new Map(); + this._tracksMapping = []; + this._tracksById = new Map(); + this._masterBars = []; + this._barsOfMasterBar = []; + this._voicesOfBar = new Map(); + this._barsById = new Map(); + this._voiceById = new Map(); + this._beatsOfVoice = new Map(); + this._beatById = new Map(); + this._rhythmOfBeat = new Map(); + this._rhythmById = new Map(); + this._notesOfBeat = new Map(); + this._noteById = new Map(); + this._tappedNotes = new Map(); + this._lyricsByTrack = new Map(); + + let dom: XmlDocument; + try { + dom = new XmlDocument(xml); + } catch (e) { + throw new UnsupportedFormatError('Could not parse XML', e); + } + + this.parseDom(dom); + this.buildModel(); + this.score.finish(settings); + if (this._lyricsByTrack.size > 0) { + this._lyricsByTrack.forEach((lyrics, t) => { + let track: Track = this._tracksById.get(t)!; + track.applyLyrics(lyrics); + }); + } + } + + private parseDom(dom: XmlDocument): void { + let root: XmlNode | null = dom.documentElement; + if (!root) { + return; + } + // the XML uses IDs for referring elements within the + // Therefore we do the parsing in 2 steps: + // - at first we read all model elements and store them by ID in a lookup table + // - after that we need to join up the information. + if (root.localName === 'GPIF') { + this.score = new Score(); + // parse all children + for (let n of root.childNodes) { + if (n.nodeType === XmlNodeType.Element) { + switch (n.localName) { + case 'Score': + this.parseScoreNode(n); + break; + case 'MasterTrack': + this.parseMasterTrackNode(n); + break; + case 'Tracks': + this.parseTracksNode(n); + break; + case 'MasterBars': + this.parseMasterBarsNode(n); + break; + case 'Bars': + this.parseBars(n); + break; + case 'Voices': + this.parseVoices(n); + break; + case 'Beats': + this.parseBeats(n); + break; + case 'Notes': + this.parseNotes(n); + break; + case 'Rhythms': + this.parseRhythms(n); + break; + } + } + } + } else { + throw new UnsupportedFormatError('Root node of XML was not GPIF'); + } + } + + // + // ... + // + private parseScoreNode(element: XmlNode): void { + for (let c of element.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'Title': + this.score.title = c.firstChild!.innerText; + break; + case 'SubTitle': + this.score.subTitle = c.firstChild!.innerText; + break; + case 'Artist': + this.score.artist = c.firstChild!.innerText; + break; + case 'Album': + this.score.album = c.firstChild!.innerText; + break; + case 'Words': + this.score.words = c.firstChild!.innerText; + break; + case 'Music': + this.score.music = c.firstChild!.innerText; + break; + case 'WordsAndMusic': + if (c.firstChild && c.firstChild.innerText !== '') { + let wordsAndMusic: string = c.firstChild.innerText; + if (wordsAndMusic && !this.score.words) { + this.score.words = wordsAndMusic; + } + if (wordsAndMusic && !this.score.music) { + this.score.music = wordsAndMusic; + } + } + break; + case 'Copyright': + this.score.copyright = c.firstChild!.innerText; + break; + case 'Tabber': + this.score.tab = c.firstChild!.innerText; + break; + case 'Instructions': + this.score.instructions = c.firstChild!.innerText; + break; + case 'Notices': + this.score.notices = c.firstChild!.innerText; + break; + } + } + } + } + + // + // ... + // + private parseMasterTrackNode(node: XmlNode): void { + for (let c of node.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'Automations': + this.parseAutomations(c, this._masterTrackAutomations); + break; + case 'Tracks': + this._tracksMapping = c.innerText.split(' '); + break; + case 'Anacrusis': + this._hasAnacrusis = true; + break; + } + } + } + } + + private parseAutomations(node: XmlNode, automations: Map): void { + for (let c of node.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'Automation': + this.parseAutomation(c, automations); + break; + } + } + } + } + + private parseAutomation(node: XmlNode, automations: Map): void { + let type: string | null = null; + let isLinear: boolean = false; + let barId: string | null = null; + let ratioPosition: number = 0; + let value: number = 0; + let reference: number = 0; + let text: string | null = null; + for (let c of node.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'Type': + type = c.innerText; + break; + case 'Linear': + isLinear = c.innerText.toLowerCase() === 'true'; + break; + case 'Bar': + barId = c.innerText; + break; + case 'Position': + ratioPosition = parseFloat(c.innerText); + break; + case 'Value': + let parts: string[] = c.innerText.split(' '); + value = parseFloat(parts[0]); + reference = parseInt(parts[1]); + break; + case 'Text': + text = c.innerText; + break; + } + } + } + if (!type) { + return; + } + let automation: Automation | null = null; + switch (type) { + case 'Tempo': + automation = Automation.buildTempoAutomation(isLinear, ratioPosition, value, reference); + break; + } + if (automation) { + if (text) { + automation.text = text; + } + + if (barId) { + if (!automations.has(barId)) { + automations.set(barId, []); + } + automations.get(barId)!.push(automation); + } + } + } + + // + // ... + // + private parseTracksNode(node: XmlNode): void { + for (let c of node.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'Track': + this.parseTrack(c); + break; + } + } + } + } + + private parseTrack(node: XmlNode): void { + let track: Track = new Track(); + track.ensureStaveCount(1); + let staff: Staff = track.staves[0]; + staff.showStandardNotation = true; + let trackId: string = node.getAttribute('id'); + for (let c of node.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'Name': + track.name = c.innerText; + break; + case 'Color': + let parts: string[] = c.innerText.split(' '); + if (parts.length >= 3) { + let r: number = parseInt(parts[0]); + let g: number = parseInt(parts[1]); + let b: number = parseInt(parts[2]); + track.color = new Color(r, g, b, 0xff); + } + break; + case 'Instrument': + let instrumentName: string = c.getAttribute('ref'); + if (instrumentName.endsWith('-gs') || instrumentName.endsWith('GrandStaff')) { + track.ensureStaveCount(2); + track.staves[1].showStandardNotation = true; + } + break; + case 'InstrumentSet': + this.parseInstrumentSet(track, c); + break; + case 'ShortName': + track.shortName = c.innerText; + break; + case 'Lyrics': + this.parseLyrics(trackId, c); + break; + case 'Properties': + this.parseTrackProperties(track, c); + break; + case 'GeneralMidi': + case 'MidiConnection': + case 'MIDISettings': + this.parseGeneralMidi(track, c); + break; + case 'Sounds': + this.parseSounds(track, c); + break; + case 'PlaybackState': + let state: string = c.innerText; + track.playbackInfo.isSolo = state === 'Solo'; + track.playbackInfo.isMute = state === 'Mute'; + break; + case 'PartSounding': + this.parsePartSounding(track, c); + break; + case 'Staves': + this.parseStaves(track, c); + break; + case 'Transpose': + this.parseTranspose(track, c); + break; + } + } + } + this._tracksById.set(trackId, track); + } + + private parseInstrumentSet(track: Track, node: XmlNode): void { + for (let c of node.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'Type': + switch (c.innerText) { + case 'drumKit': + for (let staff of track.staves) { + staff.isPercussion = true; + } + break; + } + if (c.innerText === 'drumKit') { + for (let staff of track.staves) { + staff.isPercussion = true; + } + } + break; + } + } + } + } + + private parseStaves(track: Track, node: XmlNode): void { + let staffIndex: number = 0; + for (let c of node.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'Staff': + track.ensureStaveCount(staffIndex + 1); + let staff: Staff = track.staves[staffIndex]; + this.parseStaff(staff, c); + staffIndex++; + break; + } + } + } + } + + private parseStaff(staff: Staff, node: XmlNode): void { + for (let c of node.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'Properties': + this.parseStaffProperties(staff, c); + break; + } + } + } + } + + private parseStaffProperties(staff: Staff, node: XmlNode): void { + for (let c of node.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'Property': + this.parseStaffProperty(staff, c); + break; + } + } + } + } + + private parseStaffProperty(staff: Staff, node: XmlNode): void { + let propertyName: string = node.getAttribute('name'); + switch (propertyName) { + case 'Tuning': + let tuningParts: string[] = node.findChildElement('Pitches')!.innerText.split(' '); + let tuning = new Array(tuningParts.length); + for (let i: number = 0; i < tuning.length; i++) { + tuning[tuning.length - 1 - i] = parseInt(tuningParts[i]); + } + staff.tuning = tuning; + if (!staff.isPercussion) { + staff.showTablature = true; + } + break; + case 'DiagramCollection': + case 'ChordCollection': + this.parseDiagramCollection_Staff_XmlNode(staff, node); + break; + case 'CapoFret': + let capo: number = parseInt(node.findChildElement('Fret')!.innerText); + staff.capo = capo; + break; + } + } + + private parseLyrics(trackId: string, node: XmlNode): void { + let tracks: Lyrics[] = []; + for (let c of node.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'Line': + tracks.push(this.parseLyricsLine(c)); + break; + } + } + } + this._lyricsByTrack.set(trackId, tracks); + } + + private parseLyricsLine(node: XmlNode): Lyrics { + let lyrics: Lyrics = new Lyrics(); + for (let c of node.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'Offset': + lyrics.startBar = parseInt(c.innerText); + break; + case 'Text': + lyrics.text = c.innerText; + break; + } + } + } + return lyrics; + } + + private parseDiagramCollection_Track_XmlNode(track: Track, node: XmlNode): void { + let items: XmlNode = node.findChildElement('Items')!; + for (let c of items.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'Item': + this.parseDiagramItem_Track_XmlNode(track, c); + break; + } + } + } + } + + private parseDiagramCollection_Staff_XmlNode(staff: Staff, node: XmlNode): void { + let items: XmlNode = node.findChildElement('Items')!; + for (let c of items.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'Item': + this.parseDiagramItem_Staff_XmlNode(staff, c); + break; + } + } + } + } + + private parseDiagramItem_Track_XmlNode(track: Track, node: XmlNode): void { + let chord: Chord = new Chord(); + let chordId: string = node.getAttribute('id'); + for (let staff of track.staves) { + staff.addChord(chordId, chord); + } + this.parseDiagramItem_Chord_XmlNode(chord, node); + } + + private parseDiagramItem_Staff_XmlNode(staff: Staff, node: XmlNode): void { + let chord: Chord = new Chord(); + let chordId: string = node.getAttribute('id'); + staff.addChord(chordId, chord); + this.parseDiagramItem_Chord_XmlNode(chord, node); + } + + private parseDiagramItem_Chord_XmlNode(chord: Chord, node: XmlNode): void { + chord.name = node.getAttribute('name'); + let diagram: XmlNode = node.findChildElement('Diagram')!; + let stringCount: number = parseInt(diagram.getAttribute('stringCount')); + let baseFret: number = parseInt(diagram.getAttribute('baseFret')); + chord.firstFret = baseFret + 1; + for (let i: number = 0; i < stringCount; i++) { + chord.strings.push(-1); + } + for (let c of diagram.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'Fret': + let guitarString: number = parseInt(c.getAttribute('string')); + chord.strings[stringCount - guitarString - 1] = baseFret + parseInt(c.getAttribute('fret')); + break; + case 'Fingering': + let existingFingers: Map = new Map(); + for (let p of c.childNodes) { + if (p.nodeType === XmlNodeType.Element) { + switch (p.localName) { + case 'Position': + let finger: Fingers = Fingers.Unknown; + let fret: number = baseFret + parseInt(p.getAttribute('fret')); + switch (p.getAttribute('finger')) { + case 'Index': + finger = Fingers.IndexFinger; + break; + case 'Middle': + finger = Fingers.MiddleFinger; + break; + case 'Rank': + finger = Fingers.AnnularFinger; + break; + case 'Pinky': + finger = Fingers.LittleFinger; + break; + case 'Thumb': + finger = Fingers.Thumb; + break; + case 'None': + break; + } + if (finger !== Fingers.Unknown) { + if (existingFingers.has(finger)) { + chord.barreFrets.push(fret); + } else { + existingFingers.set(finger, true); + } + } + break; + } + } + } + break; + case 'Property': + switch (c.getAttribute('name')) { + case 'ShowName': + chord.showName = c.getAttribute('value') === 'true'; + break; + case 'ShowDiagram': + chord.showDiagram = c.getAttribute('value') === 'true'; + break; + case 'ShowFingering': + chord.showFingering = c.getAttribute('value') === 'true'; + break; + } + break; + } + } + } + } + + private parseTrackProperties(track: Track, node: XmlNode): void { + for (let c of node.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'Property': + this.parseTrackProperty(track, c); + break; + } + } + } + } + + private parseTrackProperty(track: Track, node: XmlNode): void { + let propertyName: string = node.getAttribute('name'); + switch (propertyName) { + case 'Tuning': + let tuningParts: string[] = node.findChildElement('Pitches')!.innerText.split(' '); + let tuning = new Array(tuningParts.length); + for (let i: number = 0; i < tuning.length; i++) { + tuning[tuning.length - 1 - i] = parseInt(tuningParts[i]); + } + for (let staff of track.staves) { + staff.tuning = tuning; + staff.showStandardNotation = true; + staff.showTablature = true; + } + break; + case 'DiagramCollection': + case 'ChordCollection': + this.parseDiagramCollection_Track_XmlNode(track, node); + break; + case 'CapoFret': + let capo: number = parseInt(node.findChildElement('Fret')!.innerText); + for (let staff of track.staves) { + staff.capo = capo; + } + break; + } + } + + private parseGeneralMidi(track: Track, node: XmlNode): void { + for (let c of node.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'Program': + track.playbackInfo.program = parseInt(c.innerText); + break; + case 'Port': + track.playbackInfo.port = parseInt(c.innerText); + break; + case 'PrimaryChannel': + track.playbackInfo.primaryChannel = parseInt(c.innerText); + break; + case 'SecondaryChannel': + track.playbackInfo.secondaryChannel = parseInt(c.innerText); + break; + } + } + } + let isPercussion: boolean = node.getAttribute('table') === 'Percussion'; + if (isPercussion) { + for (let staff of track.staves) { + staff.isPercussion = true; + } + } + } + + private parseSounds(track: Track, node: XmlNode): void { + for (let c of node.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'Sound': + this.parseSound(track, c); + break; + } + } + } + } + + private parseSound(track: Track, node: XmlNode): void { + for (let c of node.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'MIDI': + this.parseSoundMidi(track, c); + break; + } + } + } + } + + private parseSoundMidi(track: Track, node: XmlNode): void { + for (let c of node.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'Program': + track.playbackInfo.program = parseInt(c.innerText); + break; + } + } + } + } + + private parsePartSounding(track: Track, node: XmlNode): void { + for (let c of node.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'TranspositionPitch': + for (let staff of track.staves) { + staff.displayTranspositionPitch = parseInt(c.innerText); + } + break; + } + } + } + } + + private parseTranspose(track: Track, node: XmlNode): void { + let octave: number = 0; + let chromatic: number = 0; + for (let c of node.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'Chromatic': + chromatic = parseInt(c.innerText); + break; + case 'Octave': + octave = parseInt(c.innerText); + break; + } + } + } + for (let staff of track.staves) { + staff.displayTranspositionPitch = octave * 12 + chromatic; + } + } + + // + // ... + // + private parseMasterBarsNode(node: XmlNode): void { + for (let c of node.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'MasterBar': + this.parseMasterBar(c); + break; + } + } + } + } + + private parseMasterBar(node: XmlNode): void { + let masterBar: MasterBar = new MasterBar(); + if (this._masterBars.length === 0 && this._hasAnacrusis) { + masterBar.isAnacrusis = true; + } + for (let c of node.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'Time': + let timeParts: string[] = c.innerText.split('/'); + masterBar.timeSignatureNumerator = parseInt(timeParts[0]); + masterBar.timeSignatureDenominator = parseInt(timeParts[1]); + break; + case 'DoubleBar': + masterBar.isDoubleBar = true; + break; + case 'Section': + masterBar.section = new Section(); + masterBar.section.marker = c.findChildElement('Letter')!.innerText; + masterBar.section.text = c.findChildElement('Text')!.innerText; + break; + case 'Repeat': + if (c.getAttribute('start').toLowerCase() === 'true') { + masterBar.isRepeatStart = true; + } + if (c.getAttribute('end').toLowerCase() === 'true' && c.getAttribute('count')) { + masterBar.repeatCount = parseInt(c.getAttribute('count')); + } + break; + case 'AlternateEndings': + let alternateEndings: string[] = c.innerText.split(' '); + let i: number = 0; + for (let k: number = 0; k < alternateEndings.length; k++) { + i = i | (1 << (-1 + parseInt(alternateEndings[k]))); + } + masterBar.alternateEndings = i; + break; + case 'Bars': + this._barsOfMasterBar.push(c.innerText.split(' ')); + break; + case 'TripletFeel': + switch (c.innerText) { + case 'NoTripletFeel': + masterBar.tripletFeel = TripletFeel.NoTripletFeel; + break; + case 'Triplet8th': + masterBar.tripletFeel = TripletFeel.Triplet8th; + break; + case 'Triplet16th': + masterBar.tripletFeel = TripletFeel.Triplet16th; + break; + case 'Dotted8th': + masterBar.tripletFeel = TripletFeel.Dotted8th; + break; + case 'Dotted16th': + masterBar.tripletFeel = TripletFeel.Dotted16th; + break; + case 'Scottish8th': + masterBar.tripletFeel = TripletFeel.Scottish8th; + break; + case 'Scottish16th': + masterBar.tripletFeel = TripletFeel.Scottish16th; + break; + } + break; + case 'Key': + masterBar.keySignature = parseInt( + c.findChildElement('AccidentalCount')!.innerText + ) as KeySignature; + let mode: XmlNode = c.findChildElement('Mode')!; + if (mode) { + switch (mode.innerText.toLowerCase()) { + case 'major': + masterBar.keySignatureType = KeySignatureType.Major; + break; + case 'minor': + masterBar.keySignatureType = KeySignatureType.Minor; + break; + } + } + break; + case 'Fermatas': + this.parseFermatas(masterBar, c); + break; + } + } + } + this._masterBars.push(masterBar); + } + + private parseFermatas(masterBar: MasterBar, node: XmlNode): void { + for (let c of node.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'Fermata': + this.parseFermata(masterBar, c); + break; + } + } + } + } + + private parseFermata(masterBar: MasterBar, node: XmlNode): void { + let offset: number = 0; + let fermata: Fermata = new Fermata(); + for (let c of node.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'Type': + switch (c.innerText) { + case 'Short': + fermata.type = FermataType.Short; + break; + case 'Medium': + fermata.type = FermataType.Medium; + break; + case 'Long': + fermata.type = FermataType.Long; + break; + } + break; + case 'Length': + fermata.length = parseFloat(c.innerText); + break; + case 'Offset': + let parts: string[] = c.innerText.split('/'); + if (parts.length === 2) { + let numerator: number = parseInt(parts[0]); + let denominator: number = parseInt(parts[1]); + offset = ((numerator / denominator) * MidiUtils.QuarterTime) | 0; + } + break; + } + } + } + masterBar.addFermata(offset, fermata); + } + + // + // ... + // + private parseBars(node: XmlNode): void { + for (let c of node.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'Bar': + this.parseBar(c); + break; + } + } + } + } + + private parseBar(node: XmlNode): void { + let bar: Bar = new Bar(); + let barId: string = node.getAttribute('id'); + for (let c of node.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'Voices': + this._voicesOfBar.set(barId, c.innerText.split(' ')); + break; + case 'Clef': + switch (c.innerText) { + case 'Neutral': + bar.clef = Clef.Neutral; + break; + case 'G2': + bar.clef = Clef.G2; + break; + case 'F4': + bar.clef = Clef.F4; + break; + case 'C4': + bar.clef = Clef.C4; + break; + case 'C3': + bar.clef = Clef.C3; + break; + } + break; + case 'Ottavia': + switch (c.innerText) { + case '8va': + bar.clefOttava = Ottavia._8va; + break; + case '15ma': + bar.clefOttava = Ottavia._15ma; + break; + case '8vb': + bar.clefOttava = Ottavia._8vb; + break; + case '15mb': + bar.clefOttava = Ottavia._15mb; + break; + } + break; + case 'SimileMark': + switch (c.innerText) { + case 'Simple': + bar.simileMark = SimileMark.Simple; + break; + case 'FirstOfDouble': + bar.simileMark = SimileMark.FirstOfDouble; + break; + case 'SecondOfDouble': + bar.simileMark = SimileMark.SecondOfDouble; + break; + } + break; + } + } + } + this._barsById.set(barId, bar); + } + + // + // ... + // + private parseVoices(node: XmlNode): void { + for (let c of node.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'Voice': + this.parseVoice(c); + break; + } + } + } + } + + private parseVoice(node: XmlNode): void { + let voice: Voice = new Voice(); + let voiceId: string = node.getAttribute('id'); + for (let c of node.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'Beats': + this._beatsOfVoice.set(voiceId, c.innerText.split(' ')); + break; + } + } + } + this._voiceById.set(voiceId, voice); + } + + // + // ... + // + private parseBeats(node: XmlNode): void { + for (let c of node.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'Beat': + this.parseBeat(c); + break; + } + } + } + } + + private parseBeat(node: XmlNode): void { + let beat: Beat = new Beat(); + let beatId: string = node.getAttribute('id'); + for (let c of node.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'Notes': + this._notesOfBeat.set(beatId, c.innerText.split(' ')); + break; + case 'Rhythm': + this._rhythmOfBeat.set(beatId, c.getAttribute('ref')); + break; + case 'Fadding': + if (c.innerText === 'FadeIn') { + beat.fadeIn = true; + } + break; + case 'Tremolo': + switch (c.innerText) { + case '1/2': + beat.tremoloSpeed = Duration.Eighth; + break; + case '1/4': + beat.tremoloSpeed = Duration.Sixteenth; + break; + case '1/8': + beat.tremoloSpeed = Duration.ThirtySecond; + break; + } + break; + case 'Chord': + beat.chordId = c.innerText; + break; + case 'Hairpin': + switch (c.innerText) { + case 'Crescendo': + beat.crescendo = CrescendoType.Crescendo; + break; + case 'Decrescendo': + beat.crescendo = CrescendoType.Decrescendo; + break; + } + break; + case 'Arpeggio': + if (c.innerText === 'Up') { + beat.brushType = BrushType.ArpeggioUp; + } else { + beat.brushType = BrushType.ArpeggioDown; + } + break; + case 'Properties': + this.parseBeatProperties(c, beat); + break; + case 'XProperties': + this.parseBeatXProperties(c, beat); + break; + case 'FreeText': + beat.text = c.innerText; + break; + case 'TransposedPitchStemOrientation': + switch (c.innerText) { + case 'Upward': + beat.preferredBeamDirection = BeamDirection.Up; + break; + case 'Downward': + beat.preferredBeamDirection = BeamDirection.Down; + break; + } + break; + case 'Dynamic': + switch (c.innerText) { + case 'PPP': + beat.dynamics = DynamicValue.PPP; + break; + case 'PP': + beat.dynamics = DynamicValue.PP; + break; + case 'P': + beat.dynamics = DynamicValue.P; + break; + case 'MP': + beat.dynamics = DynamicValue.MP; + break; + case 'MF': + beat.dynamics = DynamicValue.MF; + break; + case 'F': + beat.dynamics = DynamicValue.F; + break; + case 'FF': + beat.dynamics = DynamicValue.FF; + break; + case 'FFF': + beat.dynamics = DynamicValue.FFF; + break; + } + break; + case 'GraceNotes': + switch (c.innerText) { + case 'OnBeat': + beat.graceType = GraceType.OnBeat; + break; + case 'BeforeBeat': + beat.graceType = GraceType.BeforeBeat; + break; + } + break; + case 'Legato': + if (c.getAttribute('origin') === 'true') { + beat.isLegatoOrigin = true; + } + break; + case 'Whammy': + let whammyOrigin: BendPoint = new BendPoint(0, 0); + whammyOrigin.value = this.toBendValue(parseFloat(c.getAttribute('originValue'))); + whammyOrigin.offset = this.toBendOffset(parseFloat(c.getAttribute('originOffset'))); + beat.addWhammyBarPoint(whammyOrigin); + let whammyMiddle1: BendPoint = new BendPoint(0, 0); + whammyMiddle1.value = this.toBendValue(parseFloat(c.getAttribute('middleValue'))); + whammyMiddle1.offset = this.toBendOffset(parseFloat(c.getAttribute('middleOffset1'))); + beat.addWhammyBarPoint(whammyMiddle1); + let whammyMiddle2: BendPoint = new BendPoint(0, 0); + whammyMiddle2.value = this.toBendValue(parseFloat(c.getAttribute('middleValue'))); + whammyMiddle2.offset = this.toBendOffset(parseFloat(c.getAttribute('middleOffset2'))); + beat.addWhammyBarPoint(whammyMiddle2); + let whammyDestination: BendPoint = new BendPoint(0, 0); + whammyDestination.value = this.toBendValue(parseFloat(c.getAttribute('destinationValue'))); + whammyDestination.offset = this.toBendOffset(parseFloat(c.getAttribute('destinationOffset'))); + beat.addWhammyBarPoint(whammyDestination); + break; + case 'Ottavia': + switch (c.innerText) { + case '8va': + beat.ottava = Ottavia._8va; + break; + case '8vb': + beat.ottava = Ottavia._8vb; + break; + case '15ma': + beat.ottava = Ottavia._15ma; + break; + case '15mb': + beat.ottava = Ottavia._15mb; + break; + } + break; + } + } + } + this._beatById.set(beatId, beat); + } + + private parseBeatXProperties(node: XmlNode, beat: Beat): void { + for (let c of node.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'XProperty': + let id: string = c.getAttribute('id'); + let val: number = 0; + switch (id) { + case '1124204545': + val = parseInt(c.findChildElement('Int')!.innerText); + beat.invertBeamDirection = val === 1; + break; + case '687935489': + val = parseInt(c.findChildElement('Int')!.innerText); + beat.brushDuration = val; + break; + } + break; + } + } + } + } + + private parseBeatProperties(node: XmlNode, beat: Beat): void { + let isWhammy: boolean = false; + let whammyOrigin: BendPoint | null = null; + let whammyMiddleValue: number | null = null; + let whammyMiddleOffset1: number | null = null; + let whammyMiddleOffset2: number | null = null; + let whammyDestination: BendPoint | null = null; + for (let c of node.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'Property': + let name: string = c.getAttribute('name'); + switch (name) { + case 'Brush': + if (c.findChildElement('Direction')!.innerText === 'Up') { + beat.brushType = BrushType.BrushUp; + } else { + beat.brushType = BrushType.BrushDown; + } + break; + case 'PickStroke': + if (c.findChildElement('Direction')!.innerText === 'Up') { + beat.pickStroke = PickStroke.Up; + } else { + beat.pickStroke = PickStroke.Down; + } + break; + case 'Slapped': + if (c.findChildElement('Enable')) { + beat.slap = true; + } + break; + case 'Popped': + if (c.findChildElement('Enable')) { + beat.pop = true; + } + break; + case 'VibratoWTremBar': + switch (c.findChildElement('Strength')!.innerText) { + case 'Wide': + beat.vibrato = VibratoType.Wide; + break; + case 'Slight': + beat.vibrato = VibratoType.Slight; + break; + } + break; + case 'WhammyBar': + isWhammy = true; + break; + case 'WhammyBarExtend': + // not clear what this is used for + break; + case 'WhammyBarOriginValue': + if (!whammyOrigin) { + whammyOrigin = new BendPoint(0, 0); + } + whammyOrigin.value = this.toBendValue( + parseFloat(c.findChildElement('Float')!.innerText) + ); + break; + case 'WhammyBarOriginOffset': + if (!whammyOrigin) { + whammyOrigin = new BendPoint(0, 0); + } + whammyOrigin.offset = this.toBendOffset( + parseFloat(c.findChildElement('Float')!.innerText) + ); + break; + case 'WhammyBarMiddleValue': + whammyMiddleValue = this.toBendValue( + parseFloat(c.findChildElement('Float')!.innerText) + ); + break; + case 'WhammyBarMiddleOffset1': + whammyMiddleOffset1 = this.toBendOffset( + parseFloat(c.findChildElement('Float')!.innerText) + ); + break; + case 'WhammyBarMiddleOffset2': + whammyMiddleOffset2 = this.toBendOffset( + parseFloat(c.findChildElement('Float')!.innerText) + ); + break; + case 'WhammyBarDestinationValue': + if (!whammyDestination) { + whammyDestination = new BendPoint(BendPoint.MaxPosition, 0); + } + whammyDestination.value = this.toBendValue( + parseFloat(c.findChildElement('Float')!.innerText) + ); + break; + case 'WhammyBarDestinationOffset': + if (!whammyDestination) { + whammyDestination = new BendPoint(0, 0); + } + whammyDestination.offset = this.toBendOffset( + parseFloat(c.findChildElement('Float')!.innerText) + ); + break; + } + break; + } + } + } + if (isWhammy) { + if (!whammyOrigin) { + whammyOrigin = new BendPoint(0, 0); + } + if (!whammyDestination) { + whammyDestination = new BendPoint(BendPoint.MaxPosition, 0); + } + beat.addWhammyBarPoint(whammyOrigin); + if (whammyMiddleOffset1 && whammyMiddleValue) { + beat.addWhammyBarPoint(new BendPoint(whammyMiddleOffset1, whammyMiddleValue)); + } + if (whammyMiddleOffset2 && whammyMiddleValue) { + beat.addWhammyBarPoint(new BendPoint(whammyMiddleOffset2, whammyMiddleValue)); + } + if (!whammyMiddleOffset1 && !whammyMiddleOffset2 && whammyMiddleValue) { + beat.addWhammyBarPoint(new BendPoint((BendPoint.MaxPosition / 2) | 0, whammyMiddleValue)); + } + beat.addWhammyBarPoint(whammyDestination); + } + } + + // + // ... + // + private parseNotes(node: XmlNode): void { + for (let c of node.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'Note': + this.parseNote(c); + break; + } + } + } + } + + private parseNote(node: XmlNode): void { + let note: Note = new Note(); + let noteId: string = node.getAttribute('id'); + for (let c of node.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'Properties': + this.parseNoteProperties(c, note, noteId); + break; + case 'AntiAccent': + if (c.innerText.toLowerCase() === 'normal') { + note.isGhost = true; + } + break; + case 'LetRing': + note.isLetRing = true; + break; + case 'Trill': + note.trillValue = parseInt(c.innerText); + note.trillSpeed = Duration.Sixteenth; + break; + case 'Accent': + let accentFlags: number = parseInt(c.innerText); + if ((accentFlags & 0x01) !== 0) { + note.isStaccato = true; + } + if ((accentFlags & 0x04) !== 0) { + note.accentuated = AccentuationType.Heavy; + } + if ((accentFlags & 0x08) !== 0) { + note.accentuated = AccentuationType.Normal; + } + break; + case 'Tie': + if (c.getAttribute('destination').toLowerCase() === 'true') { + note.isTieDestination = true; + } + break; + case 'Vibrato': + switch (c.innerText) { + case 'Slight': + note.vibrato = VibratoType.Slight; + break; + case 'Wide': + note.vibrato = VibratoType.Wide; + break; + } + break; + case 'LeftFingering': + note.isFingering = true; + switch (c.innerText) { + case 'P': + note.leftHandFinger = Fingers.Thumb; + break; + case 'I': + note.leftHandFinger = Fingers.IndexFinger; + break; + case 'M': + note.leftHandFinger = Fingers.MiddleFinger; + break; + case 'A': + note.leftHandFinger = Fingers.AnnularFinger; + break; + case 'C': + note.leftHandFinger = Fingers.LittleFinger; + break; + } + break; + case 'RightFingering': + note.isFingering = true; + switch (c.innerText) { + case 'P': + note.rightHandFinger = Fingers.Thumb; + break; + case 'I': + note.rightHandFinger = Fingers.IndexFinger; + break; + case 'M': + note.rightHandFinger = Fingers.MiddleFinger; + break; + case 'A': + note.rightHandFinger = Fingers.AnnularFinger; + break; + case 'C': + note.rightHandFinger = Fingers.LittleFinger; + break; + } + break; + } + } + } + this._noteById.set(noteId, note); + } + + private parseNoteProperties(node: XmlNode, note: Note, noteId: string): void { + let isBended: boolean = false; + let bendOrigin: BendPoint | null = null; + let bendMiddleValue: number | null = null; + let bendMiddleOffset1: number | null = null; + let bendMiddleOffset2: number | null = null; + let bendDestination: BendPoint | null = null; + for (let c of node.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'Property': + let name: string = c.getAttribute('name'); + switch (name) { + case 'String': + note.string = parseInt(c.findChildElement('String')!.innerText) + 1; + break; + case 'Fret': + note.fret = parseInt(c.findChildElement('Fret')!.innerText); + break; + case 'Element': + note.element = parseInt(c.findChildElement('Element')!.innerText); + break; + case 'Variation': + note.variation = parseInt(c.findChildElement('Variation')!.innerText); + break; + case 'Tapped': + this._tappedNotes.set(noteId, true); + break; + case 'HarmonicType': + let htype: XmlNode = c.findChildElement('HType')!; + if (htype) { + switch (htype.innerText) { + case 'NoHarmonic': + note.harmonicType = HarmonicType.None; + break; + case 'Natural': + note.harmonicType = HarmonicType.Natural; + break; + case 'Artificial': + note.harmonicType = HarmonicType.Artificial; + break; + case 'Pinch': + note.harmonicType = HarmonicType.Pinch; + break; + case 'Tap': + note.harmonicType = HarmonicType.Tap; + break; + case 'Semi': + note.harmonicType = HarmonicType.Semi; + break; + case 'Feedback': + note.harmonicType = HarmonicType.Feedback; + break; + } + } + break; + case 'HarmonicFret': + let hfret: XmlNode = c.findChildElement('HFret')!; + if (hfret) { + note.harmonicValue = parseFloat(hfret.innerText); + } + break; + case 'Muted': + if (c.findChildElement('Enable')) { + note.isDead = true; + } + break; + case 'PalmMuted': + if (c.findChildElement('Enable')) { + note.isPalmMute = true; + } + break; + case 'Octave': + note.octave = parseInt(c.findChildElement('Number')!.innerText); + break; + case 'Tone': + note.tone = parseInt(c.findChildElement('Step')!.innerText); + break; + case 'Bended': + isBended = true; + break; + case 'BendOriginValue': + if (!bendOrigin) { + bendOrigin = new BendPoint(0, 0); + } + bendOrigin.value = this.toBendValue(parseFloat(c.findChildElement('Float')!.innerText)); + break; + case 'BendOriginOffset': + if (!bendOrigin) { + bendOrigin = new BendPoint(0, 0); + } + bendOrigin.offset = this.toBendOffset( + parseFloat(c.findChildElement('Float')!.innerText) + ); + break; + case 'BendMiddleValue': + bendMiddleValue = this.toBendValue(parseFloat(c.findChildElement('Float')!.innerText)); + break; + case 'BendMiddleOffset1': + bendMiddleOffset1 = this.toBendOffset( + parseFloat(c.findChildElement('Float')!.innerText) + ); + break; + case 'BendMiddleOffset2': + bendMiddleOffset2 = this.toBendOffset( + parseFloat(c.findChildElement('Float')!.innerText) + ); + break; + case 'BendDestinationValue': + if (!bendDestination) { + bendDestination = new BendPoint(BendPoint.MaxPosition, 0); + } + bendDestination.value = this.toBendValue( + parseFloat(c.findChildElement('Float')!.innerText) + ); + break; + case 'BendDestinationOffset': + if (!bendDestination) { + bendDestination = new BendPoint(0, 0); + } + bendDestination.offset = this.toBendOffset( + parseFloat(c.findChildElement('Float')!.innerText) + ); + break; + case 'HopoOrigin': + if (c.findChildElement('Enable')) { + note.isHammerPullOrigin = true; + } + break; + case 'HopoDestination': + // NOTE: gets automatically calculated + // if (FindChildElement(node, "Enable")) + // note.isHammerPullDestination = true; + break; + case 'LeftHandTapped': + note.isLeftHandTapped = true; + break; + case 'Slide': + let slideFlags: number = parseInt(c.findChildElement('Flags')!.innerText); + if ((slideFlags & 1) !== 0) { + note.slideOutType = SlideOutType.Shift; + } else if ((slideFlags & 2) !== 0) { + note.slideOutType = SlideOutType.Legato; + } else if ((slideFlags & 4) !== 0) { + note.slideOutType = SlideOutType.OutDown; + } else if ((slideFlags & 8) !== 0) { + note.slideOutType = SlideOutType.OutUp; + } + if ((slideFlags & 16) !== 0) { + note.slideInType = SlideInType.IntoFromBelow; + } else if ((slideFlags & 32) !== 0) { + note.slideInType = SlideInType.IntoFromAbove; + } + if ((slideFlags & 64) !== 0) { + note.slideOutType = SlideOutType.PickSlideDown; + } else if ((slideFlags & 128) !== 0) { + note.slideOutType = SlideOutType.PickSlideUp; + } + break; + } + break; + } + } + } + if (isBended) { + if (!bendOrigin) { + bendOrigin = new BendPoint(0, 0); + } + if (!bendDestination) { + bendDestination = new BendPoint(BendPoint.MaxPosition, 0); + } + note.addBendPoint(bendOrigin); + if (bendMiddleOffset1 && bendMiddleValue) { + note.addBendPoint(new BendPoint(bendMiddleOffset1, bendMiddleValue)); + } + if (bendMiddleOffset2 && bendMiddleValue) { + note.addBendPoint(new BendPoint(bendMiddleOffset2, bendMiddleValue)); + } + if (!bendMiddleOffset1 && !bendMiddleOffset2 && bendMiddleValue) { + note.addBendPoint(new BendPoint((BendPoint.MaxPosition / 2) | 0, bendMiddleValue)); + } + note.addBendPoint(bendDestination); + } + } + + private toBendValue(gpxValue: number): number { + return (gpxValue * GpifParser.BendPointValueFactor) | 0; + } + + private toBendOffset(gpxOffset: number): number { + return (gpxOffset * GpifParser.BendPointPositionFactor) | 0; + } + + private parseRhythms(node: XmlNode): void { + for (let c of node.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'Rhythm': + this.parseRhythm(c); + break; + } + } + } + } + + private parseRhythm(node: XmlNode): void { + let rhythm: GpifRhythm = new GpifRhythm(); + let rhythmId: string = node.getAttribute('id'); + for (let c of node.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'NoteValue': + switch (c.innerText) { + case 'Long': + rhythm.value = Duration.QuadrupleWhole; + break; + case 'DoubleWhole': + rhythm.value = Duration.DoubleWhole; + break; + case 'Whole': + rhythm.value = Duration.Whole; + break; + case 'Half': + rhythm.value = Duration.Half; + break; + case 'Quarter': + rhythm.value = Duration.Quarter; + break; + case 'Eighth': + rhythm.value = Duration.Eighth; + break; + case '16th': + rhythm.value = Duration.Sixteenth; + break; + case '32nd': + rhythm.value = Duration.ThirtySecond; + break; + case '64th': + rhythm.value = Duration.SixtyFourth; + break; + case '128th': + rhythm.value = Duration.OneHundredTwentyEighth; + break; + case '256th': + rhythm.value = Duration.TwoHundredFiftySixth; + break; + } + break; + case 'PrimaryTuplet': + rhythm.tupletNumerator = parseInt(c.getAttribute('num')); + rhythm.tupletDenominator = parseInt(c.getAttribute('den')); + break; + case 'AugmentationDot': + rhythm.dots = parseInt(c.getAttribute('count')); + break; + } + } + } + this._rhythmById.set(rhythmId, rhythm); + } + + private buildModel(): void { + // build score + for (let i: number = 0, j: number = this._masterBars.length; i < j; i++) { + let masterBar: MasterBar = this._masterBars[i]; + this.score.addMasterBar(masterBar); + } + // add tracks to score + for (let trackId of this._tracksMapping) { + if (!trackId) { + continue; + } + let track: Track = this._tracksById.get(trackId)!; + this.score.addTrack(track); + } + // process all masterbars + for (let barIds of this._barsOfMasterBar) { + // add all bars of masterbar vertically to all tracks + let staffIndex: number = 0; + for ( + let barIndex: number = 0, trackIndex: number = 0; + barIndex < barIds.length && trackIndex < this.score.tracks.length; + barIndex++ + ) { + let barId: string = barIds[barIndex]; + if (barId !== GpifParser.InvalidId) { + let bar: Bar = this._barsById.get(barId)!; + let track: Track = this.score.tracks[trackIndex]; + let staff: Staff = track.staves[staffIndex]; + staff.addBar(bar); + if (this._voicesOfBar.has(barId)) { + // add voices to bars + for (let voiceId of this._voicesOfBar.get(barId)!) { + if (voiceId !== GpifParser.InvalidId) { + let voice: Voice = this._voiceById.get(voiceId)!; + bar.addVoice(voice); + if (this._beatsOfVoice.has(voiceId)) { + // add beats to voices + for (let beatId of this._beatsOfVoice.get(voiceId)!) { + if (beatId !== GpifParser.InvalidId) { + // important! we clone the beat because beats get reused + // in gp6, our model needs to have unique beats. + let beat: Beat = this._beatById.get(beatId)!.clone(); + voice.addBeat(beat); + let rhythmId: string = this._rhythmOfBeat.get(beatId)!; + let rhythm: GpifRhythm = this._rhythmById.get(rhythmId)!; + // set beat duration + beat.duration = rhythm.value; + beat.dots = rhythm.dots; + beat.tupletNumerator = rhythm.tupletNumerator; + beat.tupletDenominator = rhythm.tupletDenominator; + // add notes to beat + if (this._notesOfBeat.has(beatId)) { + for (let noteId of this._notesOfBeat.get(beatId)!) { + if (noteId !== GpifParser.InvalidId) { + beat.addNote(this._noteById.get(noteId)!.clone()); + if (this._tappedNotes.has(noteId)) { + beat.tap = true; + } + } + } + } + } + } + } + } else { + // invalid voice -> empty voice + let voice: Voice = new Voice(); + bar.addVoice(voice); + let beat: Beat = new Beat(); + beat.isEmpty = true; + beat.duration = Duration.Quarter; + voice.addBeat(beat); + } + } + } + // stave is full? -> next track + if (staffIndex === track.staves.length - 1) { + trackIndex++; + staffIndex = 0; + } else { + staffIndex++; + } + } else { + // no bar for track + trackIndex++; + } + } + } + // build masterbar automations + this._masterTrackAutomations.forEach((automations, barIndex) => { + let masterBar: MasterBar = this.score.masterBars[parseInt(barIndex)]; + for (let i: number = 0, j: number = automations.length; i < j; i++) { + let automation: Automation = automations[i]; + if (automation.type === AutomationType.Tempo) { + if (barIndex === '0') { + this.score.tempo = automation.value | 0; + if (automation.text) { + this.score.tempoLabel = automation.text; + } + } + masterBar.tempoAutomation = automation; + } + } + }); + } +} diff --git a/src/importer/GpxFileSystem.ts b/src/importer/GpxFileSystem.ts new file mode 100644 index 000000000..639e56f53 --- /dev/null +++ b/src/importer/GpxFileSystem.ts @@ -0,0 +1,231 @@ +import { UnsupportedFormatError } from '@src/importer/UnsupportedFormatError'; +import { BitReader, EndOfReaderError } from '@src/io/BitReader'; +import { ByteBuffer } from '@src/io/ByteBuffer'; +import { IReadable } from '@src/io/IReadable'; + +/** + * this public class represents a file within the GpxFileSystem + */ +export class GpxFile { + public fileName: string = ''; + public fileSize: number = 0; + public data: Uint8Array | null = null; +} + +/** + * This public class represents the file system structure + * stored within a GPX container file. + */ +export class GpxFileSystem { + public static readonly HeaderBcFs: string = 'BCFS'; + public static readonly HeaderBcFz: string = 'BCFZ'; + public static readonly ScoreGpif: string = 'score.gpif'; + public static readonly BinaryStylesheet: string = 'BinaryStylesheet'; + public static readonly PartConfiguration: string = 'PartConfiguration'; + + /** + * You can set a file filter method using this setter. On parsing + * the filestructure this function can determine based on the filename + * whether this file will be available after loading. + * This way we can reduce the amount of memory we store. + */ + public fileFilter: (fileName: string) => boolean; + + /** + * Gets the list of files stored in this FileSystem. + */ + public files: GpxFile[] = []; + + /** + * Creates a new GpxFileSystem instance + */ + public constructor() { + this.files = []; + this.fileFilter = s => { + return true; + }; + } + + /** + * Load a complete FileSystem to the memory. + * @param s the binary source to read from. + * @returns + */ + public load(s: IReadable): void { + let src: BitReader = new BitReader(s); + this.readBlock(src); + } + + /** + * Reads the 4 byte header as a string. + * @param src the BitInput to read from + * @returns a string with 4 characters representing the header. + */ + public readHeader(src: BitReader): string { + return this.getString(src.readBytes(4), 0, 4); + } + + /** + * Decompresses the given bitinput using the GPX compression format. Only use this method + * if you are sure the binary data is compressed using the GPX format. Otherwise unexpected + * behavior can occure. + * @param src the bitInput to read the data from + * @param skipHeader true if the header should NOT be included in the result byteset, otherwise false + * @returns the decompressed byte data. if skipHeader is set to false the BCFS header is included. + */ + public decompress(src: BitReader, skipHeader: boolean = false): Uint8Array { + let uncompressed: ByteBuffer = ByteBuffer.empty(); + let buffer: Uint8Array; + let expectedLength: number = this.getInteger(src.readBytes(4), 0); + try { + // as long we reach our expected length we try to decompress, a EOF might occure. + while (uncompressed.length < expectedLength) { + // compression flag + let flag: number = src.readBits(1); + if (flag === 1) { + // get offset and size of the content we need to read. + // compressed does mean we already have read the data and need + // to copy it from our uncompressed buffer to the end + let wordSize: number = src.readBits(4); + let offset: number = src.readBitsReversed(wordSize); + let size: number = src.readBitsReversed(wordSize); + // the offset is relative to the end + let sourcePosition: number = uncompressed.length - offset; + let toRead: number = Math.min(offset, size); + // get the subbuffer storing the data and add it again to the end + buffer = uncompressed.getBuffer(); + uncompressed.write(buffer, sourcePosition, toRead); + } else { + // on raw content we need to read the data from the source buffer + let size: number = src.readBitsReversed(2); + for (let i: number = 0; i < size; i++) { + uncompressed.writeByte(src.readByte()); + } + } + } + } catch (e) { + if (!(e instanceof EndOfReaderError)) { + throw e; + } + } + buffer = uncompressed.getBuffer(); + let resultOffset: number = skipHeader ? 4 : 0; + let resultSize: number = uncompressed.length - resultOffset; + let result: Uint8Array = new Uint8Array(resultSize); + let count: number = resultSize; + result.set(buffer.subarray(resultOffset, resultOffset + count), 0); + return result; + } + + /** + * Reads a block from the given data source. + * @param data the data source + * @returns + */ + private readBlock(data: BitReader): void { + let header: string = this.readHeader(data); + if (header === 'BCFZ') { + // decompress the data and use this + // we will skip the header + this.readUncompressedBlock(this.decompress(data, true)); + } else if (header === 'BCFS') { + this.readUncompressedBlock(data.readAll()); + } else { + throw new UnsupportedFormatError('Unsupported format'); + } + } + + /** + * Reads an uncompressed data block into the model. + * @param data the data store to read from. + */ + private readUncompressedBlock(data: Uint8Array): void { + // the uncompressed block contains a list of filesystem entires + // as long we have data we will try to read more entries + // the first sector (0x1000 bytes) is empty (filled with 0xFF) + // so the first sector starts at 0x1000 + // (we already skipped the 4 byte header so we don't have to take care of this) + let sectorSize: number = 0x1000; + let offset: number = sectorSize; + // we always need 4 bytes (+3 including offset) to read the type + while (offset + 3 < data.length) { + let entryType: number = this.getInteger(data, offset); + if (entryType === 2) { + // file structure: + // offset | type | size | what + // --------+----------+----------+------ + // 0x04 | string | 127byte | FileName (zero terminated) + // 0x83 | ? | 9byte | Unknown + // 0x8c | int | 4byte | FileSize + // 0x90 | ? | 4byte | Unknown + // 0x94 | int[] | n*4byte | Indices of the sector containing the data (end is marked with 0) + // The sectors marked at 0x94 are absolutely positioned ( 1*0x1000 is sector 1, 2*0x1000 is sector 2,...) + let file: GpxFile = new GpxFile(); + file.fileName = this.getString(data, offset + 0x04, 127); + file.fileSize = this.getInteger(data, offset + 0x8c); + // store file if needed + let storeFile: boolean = !this.fileFilter || this.fileFilter(file.fileName); + if (storeFile) { + this.files.push(file); + } + // we need to iterate the blocks because we need to move after the last datasector + let dataPointerOffset: number = offset + 0x94; + let sector: number = 0; + // this var is storing the sector index + let sectorCount: number = 0; + // we're keeping count so we can calculate the offset of the array item + // as long we have data blocks we need to iterate them, + let fileData: ByteBuffer | null = storeFile ? ByteBuffer.withCapactiy(file.fileSize) : null; + // tslint:disable-next-line: no-conditional-assignment + while ((sector = this.getInteger(data, dataPointerOffset + 4 * sectorCount++)) !== 0) { + // the next file entry starts after the last data sector so we + // move the offset along + offset = sector * sectorSize; + // write data only if needed + if (storeFile) { + fileData!.write(data, offset, sectorSize); + } + } + if (storeFile) { + // trim data to filesize if needed + file.data = new Uint8Array(Math.min(file.fileSize, fileData!.length)); + // we can use the getBuffer here because we are intelligent and know not to read the empty data. + let raw: Uint8Array = fileData!.toArray(); + file.data.set(raw.subarray(0, 0 + file.data.length), 0); + } + } + // let's move to the next sector + offset += sectorSize; + } + } + + /** + * Reads a zeroterminated ascii string from the given source + * @param data the data source to read from + * @param offset the offset to start reading from + * @param length the max length to read + * @returns the ascii string read from the datasource. + */ + private getString(data: Uint8Array, offset: number, length: number): string { + let buf: string = ''; + for (let i: number = 0; i < length; i++) { + let code: number = data[offset + i] & 0xff; + if (code === 0) { + break; + // zero terminated string + } + buf += String.fromCharCode(code); + } + return buf; + } + + /** + * Reads an 4 byte signed integer from the given source + * @param data the data source to read from + * @param offset offset the offset to start reading from + * @returns + */ + private getInteger(data: Uint8Array, offset: number): number { + return (data[offset + 3] << 24) | (data[offset + 2] << 16) | (data[offset + 1] << 8) | data[offset]; + } +} diff --git a/src/importer/GpxImporter.ts b/src/importer/GpxImporter.ts new file mode 100644 index 000000000..209d24cac --- /dev/null +++ b/src/importer/GpxImporter.ts @@ -0,0 +1,79 @@ +import { BinaryStylesheet } from '@src/importer/BinaryStylesheet'; +import { GpifParser } from '@src/importer/GpifParser'; +import { GpxFileSystem } from '@src/importer/GpxFileSystem'; +import { PartConfiguration } from '@src/importer/PartConfiguration'; +import { ScoreImporter } from '@src/importer/ScoreImporter'; +import { Score } from '@src/model/Score'; +import { Logger } from '@src/Logger'; +import { UnsupportedFormatError } from '@src/importer/UnsupportedFormatError'; +import { IOHelper } from '@src/io/IOHelper'; + +/** + * This ScoreImporter can read Guitar Pro 6 (gpx) files. + */ +export class GpxImporter extends ScoreImporter { + public get name(): string { + return 'Guitar Pro 6'; + } + + public constructor() { + super(); + } + + public readScore(): Score { + // at first we need to load the binary file system + // from the GPX container + Logger.debug(this.name, 'Loading GPX filesystem'); + let fileSystem: GpxFileSystem = new GpxFileSystem(); + fileSystem.fileFilter = s => { + return s.endsWith('score.gpif') || s.endsWith('BinaryStylesheet') || s.endsWith('PartConfiguration'); + }; + fileSystem.load(this.data); + Logger.debug(this.name, 'GPX filesystem loaded'); + + let xml: string | null = null; + let binaryStylesheetData: Uint8Array | null = null; + let partConfigurationData: Uint8Array | null = null; + for (let entry of fileSystem.files) { + switch (entry.fileName) { + case 'score.gpif': + xml = IOHelper.toString(entry.data!, this.settings.importer.encoding); + break; + case 'BinaryStylesheet': + binaryStylesheetData = entry.data; + break; + case 'PartConfiguration': + partConfigurationData = entry.data; + break; + } + } + + if (!xml) { + throw new UnsupportedFormatError('No score.gpif found in GPX'); + } + + // the score.gpif file within this filesystem stores + // the score information as XML we need to parse. + Logger.debug(this.name, 'Start Parsing score.gpif'); + let gpifParser: GpifParser = new GpifParser(); + gpifParser.parseXml(xml, this.settings); + Logger.debug(this.name, 'score.gpif parsed'); + let score: Score = gpifParser.score; + + if (binaryStylesheetData) { + Logger.debug(this.name, 'Start Parsing BinaryStylesheet'); + let binaryStylesheet: BinaryStylesheet = new BinaryStylesheet(binaryStylesheetData); + binaryStylesheet.apply(score); + Logger.debug(this.name, 'BinaryStylesheet parsed'); + } + + if (partConfigurationData) { + Logger.debug(this.name, 'Start Parsing Part Configuration'); + let partConfiguration: PartConfiguration = new PartConfiguration(partConfigurationData); + partConfiguration.apply(score); + Logger.debug(this.name, 'Part Configuration parsed'); + } + + return score; + } +} diff --git a/src/importer/MusicXmlImporter.ts b/src/importer/MusicXmlImporter.ts new file mode 100644 index 000000000..bd47f226a --- /dev/null +++ b/src/importer/MusicXmlImporter.ts @@ -0,0 +1,1406 @@ +import { ScoreImporter } from '@src/importer/ScoreImporter'; +import { UnsupportedFormatError } from '@src/importer/UnsupportedFormatError'; +import { AccentuationType } from '@src/model/AccentuationType'; +import { Automation, AutomationType } from '@src/model/Automation'; +import { Bar } from '@src/model/Bar'; +import { Beat } from '@src/model/Beat'; +import { Chord } from '@src/model/Chord'; +import { Clef } from '@src/model/Clef'; +import { Duration } from '@src/model/Duration'; +import { DynamicValue } from '@src/model/DynamicValue'; +import { GraceType } from '@src/model/GraceType'; +import { KeySignature } from '@src/model/KeySignature'; +import { KeySignatureType } from '@src/model/KeySignatureType'; +import { MasterBar } from '@src/model/MasterBar'; +import { Note } from '@src/model/Note'; +import { NoteAccidentalMode } from '@src/model/NoteAccidentalMode'; +import { Ottavia } from '@src/model/Ottavia'; +import { PickStroke } from '@src/model/PickStroke'; +import { Score } from '@src/model/Score'; +import { SlideOutType } from '@src/model/SlideOutType'; +import { Staff } from '@src/model/Staff'; +import { Track } from '@src/model/Track'; +import { Voice } from '@src/model/Voice'; + +import { ModelUtils } from '@src/model/ModelUtils'; + +import { XmlDocument } from '@src/xml/XmlDocument'; +import { XmlNode, XmlNodeType } from '@src/xml/XmlNode'; +import { IOHelper } from '@src/io/IOHelper'; + +export class MusicXmlImporter extends ScoreImporter { + private _score!: Score; + private _trackById!: Map; + private _partGroups!: Map; + private _currentPartGroup: string | null = null; + private _trackFirstMeasureNumber: number = 0; + private _maxVoices: number = 0; + private _currentDirection: string | null = null; + private _tieStarts!: Note[]; + private _tieStartIds!: Map; + private _slurStarts!: Map; + + public get name(): string { + return 'MusicXML'; + } + + public constructor() { + super(); + } + + public readScore(): Score { + this._trackById = new Map(); + this._partGroups = new Map(); + this._tieStarts = []; + this._tieStartIds = new Map(); + this._slurStarts = new Map(); + let xml: string = IOHelper.toString(this.data.readAll(), this.settings.importer.encoding); + let dom: XmlDocument; + try { + dom = new XmlDocument(xml); + } catch (e) { + throw new UnsupportedFormatError('Unsupported format'); + } + this._score = new Score(); + this._score.tempo = 120; + this.parseDom(dom); + // merge partgroups into a single track with multiple staves + if (this.settings.importer.mergePartGroupsInMusicXml) { + this.mergePartGroups(); + } + this._score.finish(this.settings); + // the structure of MusicXML does not allow live creation of the groups, + this._score.rebuildRepeatGroups(); + return this._score; + } + + private mergePartGroups(): void { + let anyMerged: boolean = false; + this._partGroups.forEach((tracks) =>{ + if (tracks.length > 1) { + this.mergeGroup(tracks); + anyMerged = true; + } + }); + // if any groups were merged, we need to rebuild the indexes + if (anyMerged) { + for (let i: number = 0; i < this._score.tracks.length; i++) { + this._score.tracks[i].index = i; + } + } + } + + private mergeGroup(partGroup: Track[]): void { + let primaryTrack: Track = partGroup[0]; + for (let i: number = 1; i < partGroup.length; i++) { + // merge staves over to primary track + let secondaryTrack: Track = partGroup[i]; + for (let staff of secondaryTrack.staves) { + primaryTrack.addStaff(staff); + } + // remove track from score + let trackIndex: number = this._score.tracks.indexOf(secondaryTrack); + this._score.tracks.splice(trackIndex, 1); + } + } + + private parseDom(dom: XmlDocument): void { + let root: XmlNode | null = dom.documentElement; + if (!root) { + throw new UnsupportedFormatError('Unsupported format'); + } + switch (root.localName) { + case 'score-partwise': + this.parsePartwise(root); + break; + case 'score-timewise': + // ParseTimewise(root); + break; + default: + throw new UnsupportedFormatError('Unsupported format'); + } + } + + private parsePartwise(element: XmlNode): void { + for (let c of element.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'work': + this.parseWork(c); + break; + case 'movement-title': + this._score.title = c.innerText; + break; + case 'identification': + this.parseIdentification(c); + break; + case 'part-list': + this.parsePartList(c); + break; + case 'part': + this.parsePart(c); + break; + } + } + } + } + + private parseWork(element: XmlNode): void { + for (let c of element.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'work-title': + this._score.title = c.innerText; + break; + } + } + } + } + + private parsePart(element: XmlNode): void { + let id: string = element.getAttribute('id'); + if (!this._trackById.has(id)) { + if (this._trackById.size === 1) { + this._trackById.forEach((t, x)=> { + if (t.staves.length === 0 || t.staves[0].bars.length === 0) { + id = x; + } + }); + if (!this._trackById.has(id)) { + return; + } + } else { + return; + } + } + let track: Track = this._trackById.get(id)!; + let isFirstMeasure: boolean = true; + this._maxVoices = 0; + for (let c of element.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'measure': + if (this.parseMeasure(c, track, isFirstMeasure)) { + isFirstMeasure = false; + } + break; + } + } + } + // ensure voices for all bars + for (let staff of track.staves) { + for (let bar of staff.bars) { + this.ensureVoices(bar); + } + } + } + + private parseMeasure(element: XmlNode, track: Track, isFirstMeasure: boolean): boolean { + if (element.getAttribute('implicit') === 'yes' && element.getElementsByTagName('note', false).length === 0) { + return false; + } + let barIndex: number = 0; + if (isFirstMeasure) { + this._divisionsPerQuarterNote = 0; + this._trackFirstMeasureNumber = parseInt(element.getAttribute('number')); + if(!this._trackFirstMeasureNumber) { + this._trackFirstMeasureNumber = 0; + } + barIndex = 0; + } else { + barIndex = parseInt(element.getAttribute('number')); + if (!barIndex) { + return false; + } + barIndex -= this._trackFirstMeasureNumber; + } + // try to find out the number of staffs required + if (isFirstMeasure) { + let attributes: XmlNode[] = element.getElementsByTagName('attributes', false); + if (attributes.length > 0) { + let stavesElements: XmlNode[] = attributes[0].getElementsByTagName('staves', false); + if (stavesElements.length > 0) { + let staves: number = parseInt(stavesElements[0].innerText); + track.ensureStaveCount(staves); + } + } + } + // create empty bars to the current index + let bars: Bar[] = new Array(track.staves.length); + let masterBar: MasterBar | null = null; + for (let b: number = track.staves[0].bars.length; b <= barIndex; b++) { + for (let s: number = 0; s < track.staves.length; s++) { + let bar: Bar = (bars[s] = new Bar()); + if (track.staves[s].bars.length > 0) { + let previousBar: Bar = track.staves[s].bars[track.staves[s].bars.length - 1]; + bar.clef = previousBar.clef; + } + masterBar = this.getOrCreateMasterBar(barIndex); + track.staves[s].addBar(bar); + this.ensureVoices(bar); + } + } + let attributesParsed: boolean = false; + for (let c of element.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'note': + this.parseNoteBeat(c, bars); + break; + case 'forward': + this.parseForward(c, bars); + break; + case 'direction': + this.parseDirection(c, masterBar!); + break; + case 'attributes': + if (!attributesParsed) { + this.parseAttributes(c, bars, masterBar!, track); + attributesParsed = true; + } + break; + case 'harmony': + this.parseHarmony(c, track); + break; + case 'sound': + // TODO + break; + case 'barline': + this.parseBarline(c, masterBar!); + break; + } + } + } + return true; + } + + private ensureVoices(bar: Bar): void { + while (bar.voices.length < this._maxVoices) { + let emptyVoice: Voice = new Voice(); + bar.addVoice(emptyVoice); + let emptyBeat: Beat = new Beat(); + emptyBeat.isEmpty = true; + emptyBeat.chordId = this._currentChord; + emptyVoice.addBeat(emptyBeat); + } + } + + private getOrCreateBeat(element: XmlNode, bars: Bar[], chord: boolean): Beat { + let voiceIndex: number = 0; + let voiceNodes: XmlNode[] = element.getElementsByTagName('voice', false); + if (voiceNodes.length > 0) { + voiceIndex = parseInt(voiceNodes[0].innerText) - 1; + } + let previousBeatWasPulled: boolean = this._previousBeatWasPulled; + this._previousBeatWasPulled = false; + let staffElement: XmlNode[] = element.getElementsByTagName('staff', false); + let staff: number = 1; + if (staffElement.length > 0) { + staff = parseInt(staffElement[0].innerText); + // in case we have a beam with a staff-jump we pull the note to the previous staff + if ( + (this._isBeamContinue || previousBeatWasPulled) && + this._previousBeat!.voice.bar.staff.index !== staff - 1 + ) { + staff = this._previousBeat!.voice.bar.staff.index + 1; + this._previousBeatWasPulled = true; + } + let staffId: string = bars[0].staff.track.index + '-' + staff; + if (!this._voiceOfStaff.has(staffId)) { + this._voiceOfStaff.set(staffId, voiceIndex); + } + } + staff--; + let bar: Bar; + if (staff < 0) { + bar = bars[0]; + } else if (staff >= bars.length) { + bar = bars[bars.length - 1]; + } else { + bar = bars[staff]; + } + let beat: Beat; + let voice: Voice = this.getOrCreateVoice(bar, voiceIndex); + if ((chord && voice.beats.length > 0) || (voice.beats.length === 1 && voice.isEmpty)) { + beat = voice.beats[voice.beats.length - 1]; + } else { + beat = new Beat(); + beat.isEmpty = false; + voice.addBeat(beat); + } + this._isBeamContinue = false; + this._previousBeat = beat; + return beat; + } + + private parseForward(element: XmlNode, bars: Bar[]): void { + let beat: Beat = this.getOrCreateBeat(element, bars, false); + let durationInDivisions: number = parseInt(element.findChildElement('duration')!.innerText); + let duration: number = (durationInDivisions * Duration.Quarter) / this._divisionsPerQuarterNote; + let durations = [ + Duration.SixtyFourth, + Duration.ThirtySecond, + Duration.Sixteenth, + Duration.Eighth, + Duration.Quarter, + Duration.Half, + Duration.Whole + ]; + for (let d of durations) { + if (duration >= d) { + beat.duration = d; + duration -= d; + break; + } + } + if (duration > 0) { + // TODO: Handle remaining duration + // (additional beats, dotted durations,...) + } + beat.isEmpty = false; + } + + private parseStaffDetails(element: XmlNode, track: Track): void { + for (let c of element.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'staff-lines': + for (let staff of track.staves) { + staff.tuning = new Array(parseInt(c.innerText)).fill(0); + } + break; + case 'staff-tuning': + this.parseStaffTuning(c, track); + break; + } + } + } + for (let staff of track.staves) { + if (this.isEmptyTuning(staff.tuning)) { + staff.tuning = []; + } + } + } + + private parseStaffTuning(element: XmlNode, track: Track): void { + let line: number = parseInt(element.getAttribute('line')); + let tuningStep: string = 'C'; + let tuningOctave: string = ''; + let tuningAlter: number = 0; + for (let c of element.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'tuning-step': + tuningStep = c.innerText; + break; + case 'tuning-alter': + tuningAlter = parseInt(c.innerText); + break; + case 'tuning-octave': + tuningOctave = c.innerText; + break; + } + } + } + let tuning: number = ModelUtils.getTuningForText(tuningStep + tuningOctave) + tuningAlter; + for (let staff of track.staves) { + staff.tuning[staff.tuning.length - line] = tuning; + } + } + + private _currentChord: string | null = null; + private _divisionsPerQuarterNote: number = 0; + + private parseHarmony(element: XmlNode, track: Track): void { + let rootStep: string | null = null; + let rootAlter: string = ''; + // let kind: string | null = null; + // let kindText: string | null = null; + for (let c of element.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'root': + for (let rootChild of c.childNodes) { + if (rootChild.nodeType === XmlNodeType.Element) { + switch (rootChild.localName) { + case 'root-step': + rootStep = rootChild.innerText; + break; + case 'root-alter': + switch (parseInt(c.innerText)) { + case -2: + rootAlter = ' bb'; + break; + case -1: + rootAlter = ' b'; + break; + case 0: + rootAlter = ''; + break; + case 1: + rootAlter = ' #'; + break; + case 2: + rootAlter = ' ##'; + break; + } + break; + } + } + } + break; + case 'kind': + // kindText = c.getAttribute('text'); + // kind = c.innerText; + break; + } + } + } + let chord: Chord = new Chord(); + chord.name = rootStep + rootAlter; + // TODO: find proper names for the rest + // switch (kind) + // { + // // triads + // case "major": + // break; + // case "minor": + // chord.Name += "m"; + // break; + // // Sevenths + // case "augmented": + // break; + // case "diminished": + // break; + // case "dominant": + // break; + // case "major-seventh": + // chord.Name += "7M"; + // break; + // case "minor-seventh": + // chord.Name += "m7"; + // break; + // case "diminished-seventh": + // break; + // case "augmented-seventh": + // break; + // case "half-diminished": + // break; + // case "major-minor": + // break; + // // Sixths + // case "major-sixth": + // break; + // case "minor-sixth": + // break; + // // Ninths + // case "dominant-ninth": + // break; + // case "major-ninth": + // break; + // case "minor-ninth": + // break; + // // 11ths + // case "dominant-11th": + // break; + // case "major-11th": + // break; + // case "minor-11th": + // break; + // // 13ths + // case "dominant-13th": + // break; + // case "major-13th": + // break; + // case "minor-13th": + // break; + // // Suspended + // case "suspended-second": + // break; + // case "suspended-fourth": + // break; + // // Functional sixths + // case "Neapolitan": + // break; + // case "Italian": + // break; + // case "French": + // break; + // case "German": + // break; + // // Other + // case "pedal": + // break; + // case "power": + // break; + // case "Tristan": + // break; + // } + // var degree = element.GetElementsByTagName("degree"); + // if (degree.Length > 0) + // { + // var degreeValue = Platform.GetNodeValue(degree[0].GetElementsByTagName("degree-value")[0]); + // var degreeAlter = Platform.GetNodeValue(degree[0].GetElementsByTagName("degree-alter")[0]); + // var degreeType = Platform.GetNodeValue(degree[0].GetElementsByTagName("degree-type")[0]); + // if (!string.IsNullOrEmpty(degreeType)) + // { + // chord.Name += degreeType; + // } + // if (!string.IsNullOrEmpty(degreeValue)) + // { + // chord.Name += "#" + degreeValue; + // } + // } + this._currentChord = ModelUtils.newGuid(); + for (let staff of track.staves) { + staff.addChord(this._currentChord, chord); + } + } + + private parseBarline(element: XmlNode, masterBar: MasterBar): void { + for (let c of element.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'repeat': + this.parseRepeat(c, masterBar); + break; + case 'ending': + this.parseEnding(c, masterBar); + break; + } + } + } + } + + private parseEnding(element: XmlNode, masterBar: MasterBar): void { + let num: number = parseInt(element.getAttribute('number')); + if (num > 0) { + --num; + masterBar.alternateEndings |= (0x01 << num) & 0xff; + } + } + + private parseRepeat(element: XmlNode, masterBar: MasterBar): void { + let direction: string = element.getAttribute('direction'); + let times: number = parseInt(element.getAttribute('times')); + if (times < 0 || isNaN(times)) { + times = 2; + } + if (direction === 'backward') { + masterBar.repeatCount = times; + } else if (direction === 'forward') { + masterBar.isRepeatStart = true; + } + } + + private _voiceOfStaff: Map = new Map(); + private _isBeamContinue: boolean = false; + private _previousBeatWasPulled: boolean = false; + private _previousBeat: Beat | null = null; + + private parseNoteBeat(element: XmlNode, bars: Bar[]): void { + let chord: boolean = element.getElementsByTagName('chord', false).length > 0; + let beat: Beat = this.getOrCreateBeat(element, bars, chord); + if (!beat.chordId && this._currentChord) { + beat.chordId = this._currentChord; + this._currentChord = null; + } + if (this._currentDirection) { + beat.text = this._currentDirection; + this._currentDirection = null; + } + let note: Note = new Note(); + beat.voice.isEmpty = false; + beat.isEmpty = false; + beat.addNote(note); + beat.dots = 0; + for (let c of element.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'grace': + // var slash = e.GetAttribute("slash"); + // var makeTime = Platform.ParseInt(e.GetAttribute("make-time")); + // var stealTimePrevious = Platform.ParseInt(e.GetAttribute("steal-time-previous")); + // var stealTimeFollowing = Platform.ParseInt(e.GetAttribute("steal-time-following")); + beat.graceType = GraceType.BeforeBeat; + beat.duration = Duration.ThirtySecond; + break; + case 'duration': + if (beat.isRest) { + // unit: divisions per quarter note + let duration: number = parseInt(c.innerText); + switch (duration) { + case 1: + beat.duration = Duration.Whole; + break; + case 2: + beat.duration = Duration.Half; + break; + case 4: + beat.duration = Duration.Quarter; + break; + case 8: + beat.duration = Duration.Eighth; + break; + case 16: + beat.duration = Duration.Sixteenth; + break; + case 32: + beat.duration = Duration.ThirtySecond; + break; + case 64: + beat.duration = Duration.SixtyFourth; + break; + default: + beat.duration = Duration.Quarter; + break; + } + } + break; + case 'tie': + this.parseTied(c, note); + break; + case 'cue': + // not supported + break; + case 'instrument': + // not supported + break; + case 'type': + beat.duration = this.getDuration(c.innerText); + if (beat.graceType !== GraceType.None && beat.duration < Duration.Sixteenth) { + beat.duration = Duration.Eighth; + } + break; + case 'dot': + beat.dots++; + break; + case 'accidental': + this.parseAccidental(c, note); + break; + case 'time-modification': + this.parseTimeModification(c, beat); + break; + case 'stem': + // not supported + break; + case 'notehead': + if (c.getAttribute('parentheses') === 'yes') { + note.isGhost = true; + } + break; + case 'beam': + let beamMode: string = c.innerText; + if (beamMode === 'continue') { + this._isBeamContinue = true; + } + break; + case 'notations': + this.parseNotations(c, beat, note); + break; + case 'lyric': + this.parseLyric(c, beat); + break; + case 'pitch': + this.parsePitch(c, note); + break; + case 'unpitched': + this.parseUnpitched(c, note); + break; + case 'rest': + beat.isEmpty = false; + beat.notes = []; + break; + } + } + } + // check if new note is duplicate on string + if (note.isStringed) { + for (let i: number = 0; i < beat.notes.length; i++) { + if (beat.notes[i].string === note.string && beat.notes[i] !== note) { + beat.removeNote(note); + break; + } + } + } + } + + private getDuration(text: string): Duration { + switch (text) { + case '256th': + case '128th': + case '64th': + return Duration.SixtyFourth; + case '32nd': + return Duration.ThirtySecond; + case '16th': + return Duration.Sixteenth; + case 'eighth': + return Duration.Eighth; + case 'quarter': + return Duration.Quarter; + case 'half': + return Duration.Half; + case 'long': + case 'breve': + case 'whole': + return Duration.Whole; + } + return Duration.Quarter; + } + + private parseLyric(element: XmlNode, beat: Beat): void { + for (let c of element.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'text': + if (beat.text) { + beat.text += ' ' + c.innerText; + } else { + beat.text = c.innerText; + } + break; + } + } + } + } + + private parseAccidental(element: XmlNode, note: Note): void { + switch (element.innerText) { + case 'sharp': + note.accidentalMode = NoteAccidentalMode.ForceSharp; + break; + case 'natural': + note.accidentalMode = NoteAccidentalMode.ForceNatural; + break; + case 'flat': + note.accidentalMode = NoteAccidentalMode.ForceFlat; + break; + case 'double-sharp': + break; + case 'sharp-sharp': + break; + case 'flat-flat': + break; + case 'natural-sharp': + break; + case 'natural-flat': + break; + case 'quarter-flat': + break; + case 'quarter-sharp': + break; + case 'three-quarters-flat': + break; + case 'three-quarters-sharp': + break; + } + } + + private parseTied(element: XmlNode, note: Note): void { + if (element.getAttribute('type') === 'start') { + if (!this._tieStartIds.has(note.id)) { + this._tieStartIds.set(note.id, true); + this._tieStarts.push(note); + } + } else if (element.getAttribute('type') === 'stop' && this._tieStarts.length > 0 && !note.isTieDestination) { + note.isTieDestination = true; + note.tieOrigin = this._tieStarts[0]; + this._tieStarts.splice(0, 1); + this._tieStartIds.delete(note.id); + } + } + + private parseNotations(element: XmlNode, beat: Beat, note: Note): void { + for (let c of element.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'articulations': + this.parseArticulations(c, note); + break; + case 'tied': + this.parseTied(c, note); + break; + case 'slide': + case 'glissando': + if (c.getAttribute('type') === 'start') { + note.slideOutType = SlideOutType.Shift; + } + break; + case 'dynamics': + this.parseDynamics(c, beat); + break; + case 'technical': + this.parseTechnical(c, note); + break; + case 'ornaments': + this.parseOrnaments(c, note); + break; + case 'slur': + let slurNumber: string = c.getAttribute('number'); + if (!slurNumber) { + slurNumber = '1'; + } + switch (c.getAttribute('type')) { + case 'start': + this._slurStarts.set(slurNumber, note); + break; + case 'stop': + if (this._slurStarts.has(slurNumber)) { + note.isSlurDestination = true; + let slurStart: Note = this._slurStarts.get(slurNumber)!; + slurStart.slurDestination = note; + note.slurOrigin = note; + } + break; + } + break; + } + } + } + } + + private parseOrnaments(element: XmlNode, note: Note): void { + for (let c of element.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'tremolo': + let tremoloSpeed: number = parseInt(c.innerText); + switch (tremoloSpeed) { + case 1: + note.beat.tremoloSpeed = Duration.Eighth; + break; + case 2: + note.beat.tremoloSpeed = Duration.Sixteenth; + break; + case 3: + note.beat.tremoloSpeed = Duration.ThirtySecond; + break; + } + break; + } + } + } + } + + private parseTechnical(element: XmlNode, note: Note): void { + for (let c of element.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'string': + note.string = parseInt(c.innerText); + if (note.string !== -2147483648) { + note.string = note.beat.voice.bar.staff.tuning.length - note.string + 1; + } + break; + case 'fret': + note.fret = parseInt(c.innerText); + break; + case 'down-bow': + note.beat.pickStroke = PickStroke.Down; + break; + case 'up-bow': + note.beat.pickStroke = PickStroke.Up; + break; + } + } + } + if (note.string === -2147483648 || note.fret === -2147483648) { + note.string = -1; + note.fret = -1; + } + } + + private parseArticulations(element: XmlNode, note: Note): void { + for (let c of element.childNodes) { + switch (c.localName) { + case 'accent': + note.accentuated = AccentuationType.Normal; + break; + case 'strong-accent': + note.accentuated = AccentuationType.Heavy; + break; + case 'staccato': + case 'detached-legato': + note.isStaccato = true; + break; + } + } + } + + private parseDynamics(element: XmlNode, beat: Beat): void { + for (let c of element.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'p': + beat.dynamics = DynamicValue.P; + break; + case 'pp': + beat.dynamics = DynamicValue.PP; + break; + case 'ppp': + beat.dynamics = DynamicValue.PPP; + break; + case 'f': + beat.dynamics = DynamicValue.F; + break; + case 'ff': + beat.dynamics = DynamicValue.FF; + break; + case 'fff': + beat.dynamics = DynamicValue.FFF; + break; + case 'mp': + beat.dynamics = DynamicValue.MP; + break; + case 'mf': + beat.dynamics = DynamicValue.MF; + break; + } + } + } + } + + private parseTimeModification(element: XmlNode, beat: Beat): void { + for (let c of element.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'actual-notes': + beat.tupletNumerator = parseInt(c.innerText); + break; + case 'normal-notes': + beat.tupletDenominator = parseInt(c.innerText); + break; + } + } + } + } + + private parseUnpitched(element: XmlNode, note: Note): void { + let step: string = ''; + let semitones: number = 0; + let octave: number = 0; + for (let c of element.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'display-step': + step = c.innerText; + break; + case 'display-alter': + semitones = parseInt(c.innerText); + break; + case 'display-octave': + // 0-9, 4 for middle C + octave = parseInt(c.innerText); + break; + } + } + } + let value: number = octave * 12 + ModelUtils.getToneForText(step) + semitones; + note.octave = (value / 12) | 0; + note.tone = value - note.octave * 12; + } + + private parsePitch(element: XmlNode, note: Note): void { + let step: string = ''; + let semitones: number = 0; + let octave: number = 0; + for (let c of element.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'step': + step = c.innerText; + break; + case 'alter': + semitones = parseFloat(c.innerText); + if (isNaN(semitones)) { + semitones = 0; + } + break; + case 'octave': + // 0-9, 4 for middle C + octave = parseInt(c.innerText) + 1; + break; + } + } + } + let value: number = octave * 12 + ModelUtils.getToneForText(step) + (semitones | 0); + note.octave = (value / 12) | 0; + note.tone = value - note.octave * 12; + } + + private getOrCreateVoice(bar: Bar, index: number): Voice { + if (index < bar.voices.length) { + return bar.voices[index]; + } + for (let i: number = bar.voices.length; i <= index; i++) { + bar.addVoice(new Voice()); + } + this._maxVoices = Math.max(this._maxVoices, bar.voices.length); + return bar.voices[index]; + } + + private parseDirection(element: XmlNode, masterBar: MasterBar): void { + for (let c of element.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'sound': + let tempo: string = c.getAttribute('tempo'); + if (tempo) { + let tempoAutomation: Automation = new Automation(); + tempoAutomation.isLinear = true; + tempoAutomation.type = AutomationType.Tempo; + tempoAutomation.value = parseInt(tempo); + masterBar.tempoAutomation = tempoAutomation; + } + break; + case 'direction-type': + let directionType: XmlNode = c.firstElement!; + switch (directionType.localName) { + case 'words': + this._currentDirection = directionType.innerText; + break; + case 'metronome': + this.parseMetronome(directionType, masterBar); + break; + } + break; + } + } + } + } + + private parseMetronome(element: XmlNode, masterBar: MasterBar): void { + let unit: Duration = Duration.Quarter; + let perMinute: number = 120; + for (let c of element.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'beat-unit': + unit = this.getDuration(c.innerText); + break; + case 'per-minute': + perMinute = parseInt(c.innerText); + break; + } + } + } + let tempoAutomation: Automation = (masterBar.tempoAutomation = new Automation()); + tempoAutomation.type = AutomationType.Tempo; + tempoAutomation.value = perMinute * ((unit / 4) | 0); + } + + private parseAttributes(element: XmlNode, bars: Bar[], masterBar: MasterBar, track: Track): void { + let num: number = 0; + let hasTime: boolean = false; + for (let c of element.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'divisions': + this._divisionsPerQuarterNote = parseInt(c.innerText); + break; + case 'key': + this.parseKey(c, masterBar); + break; + case 'time': + this.parseTime(c, masterBar); + hasTime = true; + break; + case 'clef': + num = parseInt(c.getAttribute('number')); + if (isNaN(num)) { + num = 1; + } + this.parseClef(c, bars[num - 1]); + break; + case 'staff-details': + this.parseStaffDetails(c, track); + break; + case 'transpose': + this.parseTranspose(c, track); + break; + } + } + } + if (!hasTime) { + masterBar.timeSignatureCommon = true; + } + } + + private parseTranspose(element: XmlNode, track: Track): void { + let semitones: number = 0; + for (let c of element.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'chromatic': + semitones += parseInt(c.innerText); + break; + case 'octave-change': + semitones += parseInt(c.innerText) * 12; + break; + } + } + } + for (let staff of track.staves) { + staff.transpositionPitch = semitones; + } + } + + private parseClef(element: XmlNode, bar: Bar): void { + let sign: string = 's'; + let line: number = 0; + for (let c of element.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'sign': + sign = c.innerText.toLowerCase(); + break; + case 'line': + line = parseInt(c.innerText); + break; + case 'clef-octave-change': + switch (parseInt(c.innerText)) { + case -2: + bar.clefOttava = Ottavia._15mb; + break; + case -1: + bar.clefOttava = Ottavia._8vb; + break; + case 1: + bar.clefOttava = Ottavia._8va; + break; + case 2: + bar.clefOttava = Ottavia._15mb; + break; + } + break; + } + } + } + switch (sign) { + case 'g': + bar.clef = Clef.G2; + break; + case 'f': + bar.clef = Clef.F4; + break; + case 'c': + if (line === 3) { + bar.clef = Clef.C3; + } else { + bar.clef = Clef.C4; + } + break; + case 'percussion': + bar.clef = Clef.Neutral; + bar.staff.isPercussion = true; + break; + case 'tab': + bar.clef = Clef.G2; + bar.staff.showTablature = true; + break; + default: + bar.clef = Clef.G2; + break; + } + } + + private parseTime(element: XmlNode, masterBar: MasterBar): void { + if (element.getAttribute('symbol') === 'common') { + masterBar.timeSignatureCommon = true; + } + let beatsParsed: boolean = false; + let beatTypeParsed: boolean = false; + for (let c of element.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + let v: string = c.innerText; + switch (c.localName) { + case 'beats': + if (!beatsParsed) { + if (v.indexOf('+') === -1) { + masterBar.timeSignatureNumerator = parseInt(v); + } else { + masterBar.timeSignatureNumerator = 4; + } + beatsParsed = true; + } + break; + case 'beat-type': + if (!beatTypeParsed) { + if (v.indexOf('+') === -1) { + masterBar.timeSignatureDenominator = parseInt(v); + } else { + masterBar.timeSignatureDenominator = 4; + } + beatTypeParsed = true; + } + break; + } + } + } + } + + private parseKey(element: XmlNode, masterBar: MasterBar): void { + let fifths: number = -2147483648; + //let keyStep: number = -2147483648; + //let keyAlter: number = -2147483648; + let mode: string = ''; + for (let c of element.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'fifths': + fifths = parseInt(c.innerText); + break; + case 'key-step': + //keyStep = parseInt(c.innerText); + break; + case 'key-alter': + //keyAlter = parseInt(c.innerText); + break; + case 'mode': + mode = c.innerText; + break; + } + } + } + if (-7 <= fifths && fifths <= 7) { + // TODO: check if this is conrrect + masterBar.keySignature = fifths as KeySignature; + } else { + masterBar.keySignature = KeySignature.C; + // TODO: map keyStep/keyAlter to internal keysignature + } + if (mode === 'minor') { + masterBar.keySignatureType = KeySignatureType.Minor; + } else { + masterBar.keySignatureType = KeySignatureType.Major; + } + } + + private getOrCreateMasterBar(index: number): MasterBar { + if (index < this._score.masterBars.length) { + return this._score.masterBars[index]; + } + for (let i: number = this._score.masterBars.length; i <= index; i++) { + let mb: MasterBar = new MasterBar(); + if (this._score.masterBars.length > 0) { + let prev: MasterBar = this._score.masterBars[this._score.masterBars.length - 1]; + mb.timeSignatureDenominator = prev.timeSignatureDenominator; + mb.timeSignatureNumerator = prev.timeSignatureNumerator; + mb.keySignature = prev.keySignature; + mb.keySignatureType = prev.keySignatureType; + } + this._score.addMasterBar(mb); + } + return this._score.masterBars[index]; + } + + private parseIdentification(element: XmlNode): void { + for (let c of element.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'creator': + if (c.getAttribute('type') === 'composer') { + this._score.music = c.innerText; + } + break; + case 'rights': + if (this._score.copyright) { + this._score.copyright += '\n'; + } + this._score.copyright += c.innerText; + break; + } + } + } + } + + private parsePartList(element: XmlNode): void { + for (let c of element.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'part-group': + this.parsePartGroup(c); + break; + case 'score-part': + this.parseScorePart(c); + break; + } + } + } + } + + private parsePartGroup(element: XmlNode): void { + let type: string = element.getAttribute('type'); + switch (type) { + case 'start': + this._currentPartGroup = element.getAttribute('number'); + this._partGroups.set(this._currentPartGroup, []); + break; + case 'stop': + this._currentPartGroup = null; + break; + } + } + + private parseScorePart(element: XmlNode): void { + let id: string = element.getAttribute('id'); + let track: Track = new Track(); + track.ensureStaveCount(1); + let staff: Staff = track.staves[0]; + staff.showStandardNotation = true; + this._trackById.set(id, track); + this._score.addTrack(track); + if (this._currentPartGroup) { + this._partGroups.get(this._currentPartGroup)!.push(track); + } + for (let c of element.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'part-name': + track.name = c.innerText; + break; + case 'part-abbreviation': + track.shortName = c.innerText; + break; + case 'midi-instrument': + this.parseMidiInstrument(c, track); + break; + } + } + } + if (this.isEmptyTuning(track.staves[0].tuning)) { + track.staves[0].tuning = []; + } + } + + private isEmptyTuning(tuning: number[]): boolean { + if (!tuning) { + return true; + } + for (let i: number = 0; i < tuning.length; i++) { + if (tuning[i] !== 0) { + return false; + } + } + return true; + } + + private parseMidiInstrument(element: XmlNode, track: Track): void { + for (let c of element.childNodes) { + if (c.nodeType === XmlNodeType.Element) { + switch (c.localName) { + case 'midi-channel': + track.playbackInfo.primaryChannel = parseInt(c.innerText); + break; + case 'midi-program': + track.playbackInfo.program = parseInt(c.innerText); + break; + case 'midi-volume': + track.playbackInfo.volume = parseInt(c.innerText); + break; + } + } + } + } +} diff --git a/src/importer/PartConfiguration.ts b/src/importer/PartConfiguration.ts new file mode 100644 index 000000000..7a91fb9fd --- /dev/null +++ b/src/importer/PartConfiguration.ts @@ -0,0 +1,82 @@ +import { GpBinaryHelpers } from '@src/importer/Gp3To5Importer'; +import { ByteBuffer } from '@src/io/ByteBuffer'; +import { IOHelper } from '@src/io/IOHelper'; +import { Score } from '@src/model/Score'; +import { Staff } from '@src/model/Staff'; +import { Track } from '@src/model/Track'; + +export class TrackConfiguration { + public isVisible: boolean = false; + public showSlash: boolean = false; + public showStandardNotation: boolean = false; + public showTablature: boolean = false; +} + +export class Part { + public isMultiRest: boolean = false; + public tracks: TrackConfiguration[] = []; +} + +export class PartConfiguration { + public parts: Part[] = []; + public zoomLevel: number = 0; + public layout: number = 0; + + public apply(score: Score): void { + let staffIndex: number = 0; + let trackIndex: number = 0; + // the PartConfiguration is really twisted compared to how the score structure looks like. + // the first part typically contains the settings for the first staff of all tracks. + // but then there is 1 part with 1 track for each other staff of the tracks. + // So the structure in the PartConfig appears to be: + // Parts[0].Tracks = { Track1-Staff1, Track2-Staff1, Track3-Staff1, Track4-Staff1, .. } + // Parts[1].Tracks = { Track1-Staff2 } + // Parts[2].Tracks = { Track2-Staff2 } + // Parts[3].Tracks = { Track3-Staff2 } + // Parts[4].Tracks = { Track4-Staff2 } + // + // even if a track has only 1 staff, there are 2 staff configurations stored. + // I hope Arobas never changes this in the format as the PartConfiguration is not versionized. + for (let part of this.parts) { + for (let trackConfig of part.tracks) { + if (trackIndex < score.tracks.length) { + let track: Track = score.tracks[trackIndex]; + if (staffIndex < track.staves.length) { + let staff: Staff = track.staves[staffIndex]; + staff.showTablature = trackConfig.showTablature; + staff.showStandardNotation = trackConfig.showStandardNotation; + } + } + trackIndex++; + if (trackIndex >= score.tracks.length) { + staffIndex++; + trackIndex = 0; + } + } + } + } + + public constructor(partConfigurationData: Uint8Array) { + let readable: ByteBuffer = ByteBuffer.fromBuffer(partConfigurationData); + let entryCount: number = IOHelper.readInt32BE(readable); + + for (let i: number = 0; i < entryCount; i++) { + let part = new Part(); + this.parts.push(part); + part.isMultiRest = GpBinaryHelpers.gpReadBool(readable); + let groupCount: number = IOHelper.readInt32BE(readable); + for (let j: number = 0; j < groupCount; j++) { + let flags: number = readable.readByte(); + // enable at least standard notation + if (flags === 0) { + flags = 1; + } + let trackConfiguration = new TrackConfiguration(); + trackConfiguration.showStandardNotation = (flags & 0x01) !== 0; + trackConfiguration.showTablature = (flags & 0x02) !== 0; + trackConfiguration.showSlash = (flags & 0x04) !== 0; + part.tracks.push(trackConfiguration); + } + } + } +} diff --git a/src/importer/ScoreImporter.ts b/src/importer/ScoreImporter.ts new file mode 100644 index 000000000..e86fe2e3a --- /dev/null +++ b/src/importer/ScoreImporter.ts @@ -0,0 +1,28 @@ +import { IReadable } from '@src/io/IReadable'; +import { Score } from '@src/model/Score'; +import { Settings } from '@src/Settings'; + +/** + * This is the base public class for creating new song importers which + * enable reading scores from any binary datasource + */ +export abstract class ScoreImporter { + protected data!: IReadable; + protected settings!: Settings; + + /** + * Initializes the importer with the given data and settings. + */ + public init(data: IReadable, settings: Settings): void { + this.data = data; + this.settings = settings; + } + + public abstract get name(): string; + + /** + * Reads the {@link Score} contained in the data. + * @returns The score that was contained in the data. + */ + public abstract readScore(): Score; +} diff --git a/src/importer/ScoreLoader.ts b/src/importer/ScoreLoader.ts new file mode 100644 index 000000000..5190e5893 --- /dev/null +++ b/src/importer/ScoreLoader.ts @@ -0,0 +1,134 @@ +import { Environment } from '@src/Environment'; +import { FileLoadError } from '@src/FileLoadError'; + +import { ScoreImporter } from '@src/importer/ScoreImporter'; +import { UnsupportedFormatError } from '@src/importer/UnsupportedFormatError'; +import { ByteBuffer } from '@src/io/ByteBuffer'; + +import { Score } from '@src/model/Score'; +import { Settings } from '@src/Settings'; + +import { Logger } from '@src/Logger'; + +declare var VbAjaxLoader: any; + +/** + * The ScoreLoader enables you easy loading of Scores using all + * available importers + */ +export class ScoreLoader { + /** + * Loads a score asynchronously from the given datasource + * @param path the source path to load the binary file from + * @param success this function is called if the Score was successfully loaded from the datasource + * @param error this function is called if any error during the loading occured. + * @param settings settings for the score import + * @target web + */ + // TODO: use promises + public static loadScoreAsync( + path: string, + success: (score: Score) => void, + error: (error: any) => void, + settings?: Settings + ): void { + let xhr: XMLHttpRequest = new XMLHttpRequest(); + xhr.open('GET', path, true, null, null); + xhr.responseType = 'arraybuffer'; + xhr.onreadystatechange = () => { + if (xhr.readyState === XMLHttpRequest.DONE) { + let response: unknown = xhr.response; + if (xhr.status === 200 || (xhr.status === 0 && response)) { + try { + let buffer: ArrayBuffer = xhr.response; + let reader: Uint8Array = new Uint8Array(buffer); + let score: Score = ScoreLoader.loadScoreFromBytes(reader, settings); + success(score); + } catch (e) { + error(e); + } + } else if (xhr.status === 0) { + error(new FileLoadError('You are offline!!\n Please Check Your Network.', xhr)); + } else if (xhr.status === 404) { + error(new FileLoadError('Requested URL not found.', xhr)); + } else if (xhr.status === 500) { + error(new FileLoadError('Internel Server Error.', xhr)); + } else if (xhr.statusText === 'parsererror') { + error(new FileLoadError('Error.\nParsing JSON Request failed.', xhr)); + } else if (xhr.statusText === 'timeout') { + error(new FileLoadError('Request Time out.', xhr)); + } else { + error(new FileLoadError('Unknow Error: ' + xhr.responseText, xhr)); + } + } + }; + // IE fallback + if (xhr.responseType !== 'arraybuffer') { + // use VB Loader to load binary array + let vbArr: any = VbAjaxLoader('GET', path); + let fileContents: any = vbArr.toArray(); + // decode byte array to string + let data: string = ''; + let i: number = 0; + while (i < fileContents.length - 1) { + data += (fileContents[i] as number).toString(); + i++; + } + let reader: Uint8Array = ScoreLoader.getBytesFromString(data); + let score: Score = ScoreLoader.loadScoreFromBytes(reader, settings); + success(score); + } + xhr.send(); + } + + private static getBytesFromString(s: string): Uint8Array { + let b: Uint8Array = new Uint8Array(s.length); + for (let i: number = 0; i < s.length; i++) { + b[i] = s.charCodeAt(i); + } + return b; + } + + /** + * Loads the score from the given binary data. + * @param data The binary data containing a score in any known file format. + * @param settings The settings to use during importing. + * @returns The loaded score. + */ + public static loadScoreFromBytes(data: Uint8Array, settings?: Settings): Score { + if (!settings) { + settings = new Settings(); + } + + let importers: ScoreImporter[] = Environment.buildImporters(); + Logger.debug( + 'ScoreLoader', + 'Loading score from ' + data.length + ' bytes using ' + importers.length + ' importers', + null + ); + let score: Score | null = null; + let bb: ByteBuffer = ByteBuffer.fromBuffer(data); + for (let importer of importers) { + bb.reset(); + try { + Logger.debug('ScoreLoader', 'Importing using importer ' + importer.name); + importer.init(bb, settings); + score = importer.readScore(); + Logger.debug('ScoreLoader', 'Score imported using ' + importer.name); + break; + } catch (e) { + if (e instanceof UnsupportedFormatError) { + Logger.debug('ScoreLoader', importer.name + ' does not support the file'); + } else { + Logger.error('ScoreLoader', 'Score import failed due to unexpected error: ', e); + throw e; + } + } + } + if (score) { + return score; + } + Logger.error('ScoreLoader', 'No compatible importer found for file'); + throw new UnsupportedFormatError('No compatible importer found for file'); + } +} diff --git a/src/importer/UnsupportedFormatError.ts b/src/importer/UnsupportedFormatError.ts new file mode 100644 index 000000000..0530808f1 --- /dev/null +++ b/src/importer/UnsupportedFormatError.ts @@ -0,0 +1,15 @@ +import { AlphaTabError, AlphaTabErrorType } from "@src/AlphaTabError"; + +/** + * The exception thrown by a {@link ScoreImporter} in case the + * binary data does not contain a reader compatible structure. + */ +export class UnsupportedFormatError extends AlphaTabError { + public inner: Error | null; + + public constructor(message: string = 'Unsupported format', inner: Error | null = null) { + super(AlphaTabErrorType.Format, message); + this.inner = inner; + Object.setPrototypeOf(this, UnsupportedFormatError.prototype); + } +} diff --git a/src/io/BitReader.ts b/src/io/BitReader.ts new file mode 100644 index 000000000..1bae9c5e8 --- /dev/null +++ b/src/io/BitReader.ts @@ -0,0 +1,85 @@ +import { ByteBuffer } from '@src/io/ByteBuffer'; +import { IReadable } from '@src/io/IReadable'; +import { AlphaTabError, AlphaTabErrorType } from '@src/AlphaTabError'; + +export class EndOfReaderError extends AlphaTabError { + public constructor() { + super(AlphaTabErrorType.Format, 'Unexpected end of data within reader'); + Object.setPrototypeOf(this, EndOfReaderError.prototype); + } +} + +/** + * This utility public class allows bitwise reading of a stream + */ +export class BitReader { + private static readonly ByteSize: number = 8; + + private _currentByte: number = 0; + private _position: number = BitReader.ByteSize; + private _source: IReadable; + + public constructor(source: IReadable) { + this._source = source; + } + + public readByte(): number { + return this.readBits(8); + } + + public readBytes(count: number): Uint8Array { + const bytes: Uint8Array = new Uint8Array(count); + for (let i: number = 0; i < count; i++) { + bytes[i] = this.readByte() & 0xff; + } + return bytes; + } + + public readBits(count: number): number { + let bits: number = 0; + let i: number = count - 1; + while (i >= 0) { + bits = bits | (this.readBit() << i); + i--; + } + return bits; + } + + public readBitsReversed(count: number): number { + let bits: number = 0; + for (let i: number = 0; i < count; i++) { + bits = bits | (this.readBit() << i); + } + return bits; + } + + public readBit(): number { + // need a new byte? + if (this._position >= 8) { + this._currentByte = this._source.readByte(); + if (this._currentByte === -1) { + throw new EndOfReaderError(); + } + this._position = 0; + } + // shift the desired byte to the least significant bit and + // get the value using masking + const value: number = (this._currentByte >> (BitReader.ByteSize - this._position - 1)) & 0x01; + this._position++; + return value; + } + + public readAll(): Uint8Array { + let all: ByteBuffer = ByteBuffer.empty(); + try { + while (true) { + all.writeByte(this.readByte() & 0xff); + } + } catch (e) { + if (!(e instanceof EndOfReaderError)) { + throw e; + } + } + return all.toArray(); + } +} diff --git a/src/io/ByteBuffer.ts b/src/io/ByteBuffer.ts new file mode 100644 index 000000000..a78ddb142 --- /dev/null +++ b/src/io/ByteBuffer.ts @@ -0,0 +1,138 @@ +import { IReadable } from '@src/io/IReadable'; +import { IWriteable } from '@src/io/IWriteable'; + +export class ByteBuffer implements IWriteable, IReadable { + private _buffer!: Uint8Array; + private _capacity: number = 0; + + public length: number = 0; + public position: number = 0; + + public getBuffer(): Uint8Array { + return this._buffer; + } + + public static empty(): ByteBuffer { + return ByteBuffer.withCapactiy(0); + } + + public static withCapactiy(capacity: number): ByteBuffer { + let buffer: ByteBuffer = new ByteBuffer(); + buffer._buffer = new Uint8Array(capacity); + buffer._capacity = capacity; + return buffer; + } + + public static fromBuffer(data: Uint8Array): ByteBuffer { + let buffer: ByteBuffer = new ByteBuffer(); + buffer._buffer = data; + buffer._capacity = buffer.length = data.length; + return buffer; + } + + public static fromString(contents: string): ByteBuffer { + let byteArray: Uint8Array = new Uint8Array(contents.length); + for (let i: number = 0; i < contents.length; i++) { + byteArray[i] = contents.charCodeAt(i); + } + return ByteBuffer.fromBuffer(byteArray); + } + + public reset(): void { + this.position = 0; + } + + public skip(offset: number): void { + this.position += offset; + } + + private setCapacity(value: number): void { + if (value !== this._capacity) { + if (value > 0) { + let newBuffer: Uint8Array = new Uint8Array(value); + if (this.length > 0) { + newBuffer.set(this._buffer.subarray(0, 0 + this.length), 0); + } + this._buffer = newBuffer; + } + this._capacity = value; + } + } + + public readByte(): number { + let n: number = this.length - this.position; + if (n <= 0) { + return -1; + } + return this._buffer[this.position++]; + } + + public read(buffer: Uint8Array, offset: number, count: number): number { + let n: number = this.length - this.position; + if (n > count) { + n = count; + } + if (n <= 0) { + return 0; + } + if (n <= 8) { + let byteCount: number = n; + while (--byteCount >= 0) { + buffer[offset + byteCount] = this._buffer[this.position + byteCount]; + } + } else { + buffer.set(this._buffer.subarray(this.position, this.position + n), offset); + } + this.position += n; + return n; + } + + public writeByte(value: number): void { + let buffer: Uint8Array = new Uint8Array(1); + buffer[0] = value; + this.write(buffer, 0, 1); + } + + public write(buffer: Uint8Array, offset: number, count: number): void { + let i: number = this.position + count; + if (i > this.length) { + if (i > this._capacity) { + this.ensureCapacity(i); + } + this.length = i; + } + if (count <= 8 && buffer !== this._buffer) { + let byteCount: number = count; + while (--byteCount >= 0) { + this._buffer[this.position + byteCount] = buffer[offset + byteCount]; + } + } else { + let count1: number = Math.min(count, buffer.length - offset); + this._buffer.set(buffer.subarray(offset, offset + count1), this.position); + } + this.position = i; + } + + private ensureCapacity(value: number): void { + if (value > this._capacity) { + let newCapacity: number = value; + if (newCapacity < 256) { + newCapacity = 256; + } + if (newCapacity < this._capacity * 2) { + newCapacity = this._capacity * 2; + } + this.setCapacity(newCapacity); + } + } + + public readAll(): Uint8Array { + return this.toArray(); + } + + public toArray(): Uint8Array { + let copy: Uint8Array = new Uint8Array(this.length); + copy.set(this._buffer.subarray(0, 0 + this.length), 0); + return copy; + } +} diff --git a/src/io/IOHelper.ts b/src/io/IOHelper.ts new file mode 100644 index 000000000..68d652e5f --- /dev/null +++ b/src/io/IOHelper.ts @@ -0,0 +1,173 @@ +import { IReadable } from '@src/io/IReadable'; +import { TypeConversions } from '@src/io/TypeConversions'; +import { Environment } from '@src/Environment'; + +export class IOHelper { + public static readInt32BE(input: IReadable): number { + let ch1: number = input.readByte(); + let ch2: number = input.readByte(); + let ch3: number = input.readByte(); + let ch4: number = input.readByte(); + return (ch1 << 24) | (ch2 << 16) | (ch3 << 8) | ch4; + } + + public static readInt32LE(input: IReadable): number { + let ch1: number = input.readByte(); + let ch2: number = input.readByte(); + let ch3: number = input.readByte(); + let ch4: number = input.readByte(); + return (ch4 << 24) | (ch3 << 16) | (ch2 << 8) | ch1; + } + + public static readUInt32LE(input: IReadable): number { + let ch1: number = input.readByte(); + let ch2: number = input.readByte(); + let ch3: number = input.readByte(); + let ch4: number = input.readByte(); + return (ch4 << 24) | (ch3 << 16) | (ch2 << 8) | ch1; + } + + public static readUInt16LE(input: IReadable): number { + let ch1: number = input.readByte(); + let ch2: number = input.readByte(); + return TypeConversions.int32ToUint16((ch2 << 8) | ch1); + } + + public static readInt16LE(input: IReadable): number { + let ch1: number = input.readByte(); + let ch2: number = input.readByte(); + return TypeConversions.int32ToInt16((ch2 << 8) | ch1); + } + + public static readUInt32BE(input: IReadable): number { + let ch1: number = input.readByte(); + let ch2: number = input.readByte(); + let ch3: number = input.readByte(); + let ch4: number = input.readByte(); + return TypeConversions.int32ToUint32((ch1 << 24) | (ch2 << 16) | (ch3 << 8) | ch4); + } + + public static readUInt16BE(input: IReadable): number { + let ch1: number = input.readByte(); + let ch2: number = input.readByte(); + return TypeConversions.int32ToInt16((ch1 << 8) | ch2); + } + + public static readInt16BE(input: IReadable): number { + let ch1: number = input.readByte(); + let ch2: number = input.readByte(); + return TypeConversions.int32ToInt16((ch1 << 8) | ch2); + } + + public static readByteArray(input: IReadable, length: number): Uint8Array { + let v: Uint8Array = new Uint8Array(length); + input.read(v, 0, length); + return v; + } + + public static read8BitChars(input: IReadable, length: number): string { + let b: Uint8Array = new Uint8Array(length); + input.read(b, 0, b.length); + return IOHelper.toString(b, 'utf-8'); + } + + public static read8BitString(input: IReadable): string { + let s: string = ''; + let c: number = input.readByte(); + while (c !== 0) { + s += String.fromCharCode(c); + c = input.readByte(); + } + return s; + } + + public static read8BitStringLength(input: IReadable, length: number): string { + let s: string = ''; + let z: number = -1; + for (let i: number = 0; i < length; i++) { + let c: number = input.readByte(); + if (c === 0 && z === -1) { + z = i; + } + s += String.fromCharCode(c); + } + let t: string = s; + if (z >= 0) { + return t.substr(0, z); + } + return t; + } + + public static readSInt8(input: IReadable): number { + let v: number = input.readByte(); + return ((v & 255) >> 7) * -256 + (v & 255); + } + + public static readInt24(input: Uint8Array, index: number): number { + let i: number = input[index] | (input[index + 1] << 8) | (input[index + 2] << 16); + if ((i & 0x800000) === 0x800000) { + i = i | (0xff << 24); + } + return i; + } + + public static readInt16(input: Uint8Array, index: number): number { + return TypeConversions.int32ToInt16(input[index] | (input[index + 1] << 8)); + } + + public static toString(data: Uint8Array, encoding: string): string { + if (Environment.supportsTextDecoder) { + let detectedEncoding: string | null = IOHelper.detectEncoding(data); + if (detectedEncoding) { + encoding = detectedEncoding; + } + if (!encoding) { + encoding = 'utf-8'; + } + let decoder: TextDecoder = new TextDecoder(encoding); + return decoder.decode(data.buffer); + } else { + // manual UTF8 decoding for older browsers + let s: string = ''; + let i: number = 0; + while (i < data.length) { + let c: number = data[i++]; + if (c < 0x80) { + if (c === 0) { + break; + } + s += String.fromCharCode(c); + } else if (c < 0xe0) { + s += String.fromCharCode(((c & 0x3f) << 6) | (data[i++] & 0x7f)); + } else if (c < 0xf0) { + s += String.fromCharCode(((c & 0x1f) << 12) | ((data[i++] & 0x7f) << 6) | (data[i++] & 0x7f)); + } else { + let u: number = + ((c & 0x0f) << 18) | + ((data[i++] & 0x7f) << 12) | + ((data[i++] & 0x7f) << 6) | + (data[i++] & 0x7f); + s += String.fromCharCode((u >> 18) + 0xd7c0); + s += String.fromCharCode((u & 0x3ff) | 0xdc00); + } + } + return s; + } + } + + private static detectEncoding(data: Uint8Array): string | null { + if (data.length > 2 && data[0] === 0xfe && data[1] === 0xff) { + return 'utf-16be'; + } + if (data.length > 2 && data[0] === 0xff && data[1] === 0xfe) { + return 'utf-16le'; + } + if (data.length > 4 && data[0] === 0x00 && data[1] === 0x00 && data[2] === 0xfe && data[3] === 0xff) { + return 'utf-32be'; + } + if (data.length > 4 && data[0] === 0xff && data[1] === 0xfe && data[2] === 0x00 && data[3] === 0x00) { + return 'utf-32le'; + } + return null; + } +} diff --git a/src/io/IReadable.ts b/src/io/IReadable.ts new file mode 100644 index 000000000..f7737305b --- /dev/null +++ b/src/io/IReadable.ts @@ -0,0 +1,46 @@ +/** + * Represents a stream of binary data that can be read from. + */ +export interface IReadable { + /** + * Gets or sets the current read position relative in the stream. + */ + position: number; + + /** + * Gets the total number of bytes contained in the stream. + */ + readonly length: number; + + /** + * Resets the stream for reading the data from the beginning. + */ + reset(): void; + + /** + * Skip the given number of bytes. + * @param offset The number of bytes to skip. + */ + skip(offset: number): void; + + /** + * Read a single byte from the data stream. + * @returns The value of the next byte or -1 if there is no more data. + */ + readByte(): number; + + /** + * Reads the given number of bytes from the stream into the given buffer. + * @param buffer The buffer to fill. + * @param offset The offset in the buffer where to start writing. + * @param count The number of bytes to read. + * @returns + */ + read(buffer: Uint8Array, offset: number, count: number): number; + + /** + * Reads the remaining data. + * @returns + */ + readAll(): Uint8Array; +} diff --git a/src/io/IWriteable.ts b/src/io/IWriteable.ts new file mode 100644 index 000000000..3dfdb12d6 --- /dev/null +++ b/src/io/IWriteable.ts @@ -0,0 +1,18 @@ +/** + * Represents a writer where binary data can be written to. + */ +export interface IWriteable { + /** + * Write a single byte to the stream. + * @param value The value to write. + */ + writeByte(value: number): void; + + /** + * Write data from the given buffer. + * @param buffer The buffer to get the data from. + * @param offset The offset where to start reading the data. + * @param count The number of bytes to write + */ + write(buffer: Uint8Array, offset: number, count: number): void; +} diff --git a/src/io/TypeConversions.ts b/src/io/TypeConversions.ts new file mode 100644 index 000000000..ef44ac904 --- /dev/null +++ b/src/io/TypeConversions.ts @@ -0,0 +1,34 @@ +export class TypeConversions { + private static _conversionBuffer: ArrayBuffer = new ArrayBuffer(8); + private static _dataView = new DataView(TypeConversions._conversionBuffer); + + public static uint16ToInt16(v: number): number { + TypeConversions._dataView.setUint16(0, v, true); + return TypeConversions._dataView.getInt16(0, true); + } + + public static int16ToUint32(v: number): number { + TypeConversions._dataView.setInt16(0, v, true); + return TypeConversions._dataView.getUint32(0, true); + } + + public static int32ToUint16(v: number): number { + TypeConversions._dataView.setInt32(0, v, true); + return TypeConversions._dataView.getUint16(0, true); + } + + public static int32ToInt16(v: number): number { + TypeConversions._dataView.setInt32(0, v, true); + return TypeConversions._dataView.getInt16(0, true); + } + + public static int32ToUint32(v: number): number { + TypeConversions._dataView.setInt32(0, v, true); + return TypeConversions._dataView.getUint32(0, true); + } + + public static uint8ToInt8(v: number): number { + TypeConversions._dataView.setUint8(0, v); + return TypeConversions._dataView.getInt8(0); + } +} diff --git a/src/midi/AlphaSynthMidiFileHandler.ts b/src/midi/AlphaSynthMidiFileHandler.ts new file mode 100644 index 000000000..b0c8a7625 --- /dev/null +++ b/src/midi/AlphaSynthMidiFileHandler.ts @@ -0,0 +1,139 @@ +import { MetaDataEvent } from '@src/midi/MetaDataEvent'; +import { MetaEventType } from '@src/midi/MetaEvent'; +import { MetaNumberEvent } from '@src/midi/MetaNumberEvent'; +import { MidiEvent, MidiEventType } from '@src/midi/MidiEvent'; +import { SystemCommonType } from '@src/midi/SystemCommonEvent'; +import { SystemExclusiveEvent } from '@src/midi/SystemExclusiveEvent'; +import { IMidiFileHandler } from '@src/midi/IMidiFileHandler'; +import { MidiFile } from '@src/midi/MidiFile'; +import { MidiUtils } from '@src/midi/MidiUtils'; +import { DynamicValue } from '@src/model/DynamicValue'; +import { SynthConstants } from '@src/synth/SynthConstants'; + +/** + * This implementation of the {@link IMidiFileHandler} + * generates a {@link MidiFile} object which can be used in AlphaSynth for playback. + */ +export class AlphaSynthMidiFileHandler implements IMidiFileHandler { + private _midiFile: MidiFile; + + /** + * Initializes a new instance of the {@link AlphaSynthMidiFileHandler} class. + * @param midiFile The midi file. + */ + public constructor(midiFile: MidiFile) { + this._midiFile = midiFile; + } + + public addTimeSignature(tick: number, timeSignatureNumerator: number, timeSignatureDenominator: number): void { + let denominatorIndex: number = 0; + // tslint:disable-next-line: no-conditional-assignment + while ((timeSignatureDenominator = timeSignatureDenominator >> 1) > 0) { + denominatorIndex++; + } + const message: MetaDataEvent = new MetaDataEvent( + tick, + 0xff, + MetaEventType.TimeSignature, + new Uint8Array([timeSignatureNumerator & 0xff, denominatorIndex & 0xff, 48, 8]) + ); + this._midiFile.addEvent(message); + } + + public addRest(track: number, tick: number, channel: number): void { + const message: SystemExclusiveEvent = new SystemExclusiveEvent( + tick, + SystemCommonType.SystemExclusive, + 0, + new Uint8Array([0xff]) + ); + this._midiFile.addEvent(message); + } + + public addNote( + track: number, + start: number, + length: number, + key: number, + dynamicValue: DynamicValue, + channel: number + ): void { + const velocity: number = MidiUtils.dynamicToVelocity(dynamicValue); + const noteOn: MidiEvent = new MidiEvent( + start, + this.makeCommand(MidiEventType.NoteOn, channel), + AlphaSynthMidiFileHandler.fixValue(key), + AlphaSynthMidiFileHandler.fixValue(velocity) + ); + this._midiFile.addEvent(noteOn); + const noteOff: MidiEvent = new MidiEvent( + start + length, + this.makeCommand(MidiEventType.NoteOff, channel), + AlphaSynthMidiFileHandler.fixValue(key), + AlphaSynthMidiFileHandler.fixValue(velocity) + ); + this._midiFile.addEvent(noteOff); + } + + private makeCommand(command: number, channel: number): number { + return (command & 0xf0) | (channel & 0x0f); + } + + private static fixValue(value: number): number { + if (value > 127) { + return 127; + } + if (value < 0) { + return 0; + } + return value; + } + + public addControlChange(track: number, tick: number, channel: number, controller: number, value: number): void { + const message: MidiEvent = new MidiEvent( + tick, + this.makeCommand(MidiEventType.Controller, channel), + AlphaSynthMidiFileHandler.fixValue(controller), + AlphaSynthMidiFileHandler.fixValue(value) + ); + this._midiFile.addEvent(message); + } + + public addProgramChange(track: number, tick: number, channel: number, program: number): void { + const message: MidiEvent = new MidiEvent( + tick, + this.makeCommand(MidiEventType.ProgramChange, channel), + AlphaSynthMidiFileHandler.fixValue(program), + 0 + ); + this._midiFile.addEvent(message); + } + + public addTempo(tick: number, tempo: number): void { + // bpm -> microsecond per quarter note + const tempoInUsq: number = (60000000 / tempo) | 0; + const message: MetaNumberEvent = new MetaNumberEvent(tick, 0xff, MetaEventType.Tempo, tempoInUsq); + this._midiFile.addEvent(message); + } + + public addBend(track: number, tick: number, channel: number, value: number): void { + if (value >= SynthConstants.MaxPitchWheel) { + value = SynthConstants.MaxPitchWheel; + } else { + value = Math.floor(value); + } + + const message: MidiEvent = new MidiEvent( + tick, + this.makeCommand(MidiEventType.PitchBend, channel), + value & 0x7F, + (value >> 7) & 0x7F + ); + this._midiFile.addEvent(message); + } + + public finishTrack(track: number, tick: number): void { + const message: MetaDataEvent = new MetaDataEvent(tick, 0xff, MetaEventType.EndOfTrack, new Uint8Array(0)); + this._midiFile.addEvent(message); + } +} diff --git a/src/midi/BeatTickLookup.ts b/src/midi/BeatTickLookup.ts new file mode 100644 index 000000000..1662ac98e --- /dev/null +++ b/src/midi/BeatTickLookup.ts @@ -0,0 +1,41 @@ +import { Beat } from '@src/model/Beat'; + +/** + * Represents the time period, for which a {@link Beat} is played. + */ +export class BeatTickLookup { + private _highlightedBeats: Map = new Map(); + + /** + * Gets or sets the start time in midi ticks at which the given beat is played. + */ + public start: number = 0; + + /** + * Gets or sets the end time in midi ticks at which the given beat is played. + */ + public end: number = 0; + + /** + * Gets or sets the beat which is played. + */ + public beat!: Beat; + + /** + * Gets or sets whether the beat is the placeholder beat for an empty bar. + */ + public isEmptyBar: boolean = false; + + /** + * Gets or sets a list of all beats that should be highlighted when + * the beat of this lookup starts playing. + */ + public beatsToHighlight: Beat[] = []; + + public highlightBeat(beat: Beat): void { + if (!this._highlightedBeats.has(beat.id)) { + this._highlightedBeats.set(beat.id, true); + this.beatsToHighlight.push(beat); + } + } +} diff --git a/src/midi/ControllerType.ts b/src/midi/ControllerType.ts new file mode 100644 index 000000000..49256a375 --- /dev/null +++ b/src/midi/ControllerType.ts @@ -0,0 +1,148 @@ +/** + * Lists all midi controllers. + */ +export enum ControllerType { + /** + * Bank Select. MSB + */ + BankSelectCoarse = 0x00, + + /** + * Modulation wheel or lever MSB + */ + ModulationCoarse = 0x01, + + //BreathControllerCoarse = 0x02, + //FootControllerCoarse = 0x04, + //PortamentoTimeCoarse = 0x05, + /** + * Data entry MSB + */ + DataEntryCoarse = 0x06, + + /** + * Channel Volume MSB + */ + VolumeCoarse = 0x07, + + //BalanceCoarse = 0x08, + /** + * Pan MSB + */ + PanCoarse = 0x0A, + + /** + * Expression Controller MSB + */ + ExpressionControllerCoarse = 0x0B, + + //EffectControl1Coarse = 0x0C, + //EffectControl2Coarse = 0x0D, + //GeneralPurposeSlider1 = 0x10, + //GeneralPurposeSlider2 = 0x11, + //GeneralPurposeSlider3 = 0x12, + //GeneralPurposeSlider4 = 0x13, + //BankSelectFine = 0x20, + /** + * Modulation wheel or level LSB + */ + ModulationFine = 0x21, + + //BreathControllerFine = 0x22, + //FootControllerFine = 0x24, + //PortamentoTimeFine = 0x25, + /** + * Data Entry LSB + */ + DataEntryFine = 0x26, + + /** + * Channel Volume LSB + */ + VolumeFine = 0x27, + + //BalanceFine = 0x28, + /** + * Pan LSB + */ + PanFine = 0x2A, + + /** + * Expression controller LSB + */ + ExpressionControllerFine = 0x2B, + + //EffectControl1Fine = 0x2C, + //EffectControl2Fine = 0x2D, + /** + * Damper pedal (sustain) + */ + HoldPedal = 0x40, + + //Portamento = 0x41, + //SostenutoPedal = 0x42, + //SoftPedal = 0x43, + /** + * Legato Footswitch + */ + LegatoPedal = 0x44, + + //Hold2Pedal = 0x45, + //SoundVariation = 0x46, + //SoundTimbre = 0x47, + //SoundReleaseTime = 0x48, + //SoundAttackTime = 0x49, + //SoundBrightness = 0x4A, + //SoundControl6 = 0x4B, + //SoundControl7 = 0x4C, + //SoundControl8 = 0x4D, + //SoundControl9 = 0x4E, + //SoundControl10 = 0x4F, + //GeneralPurposeButton1 = 0x50, + //GeneralPurposeButton2 = 0x51, + //GeneralPurposeButton3 = 0x52, + //GeneralPurposeButton4 = 0x53, + //EffectsLevel = 0x5B, + //TremuloLevel = 0x5C, + //ChorusLevel = 0x5D, + //CelesteLevel = 0x5E, + //PhaseLevel = 0x5F, + //DataButtonIncrement = 0x60, + //DataButtonDecrement = 0x61, + /** + * Non-Registered Parameter Number LSB + */ + NonRegisteredParameterFine = 0x62, + + /** + * Non-Registered Parameter Number MSB + */ + NonRegisteredParameterCourse = 0x63, + + /** + * Registered Parameter Number LSB + */ + RegisteredParameterFine = 0x64, + + /** + * Registered Parameter Number MSB + */ + RegisteredParameterCourse = 0x65, + + //AllSoundOff = 0x78, + /** + * Reset all controllers + */ + ResetControllers = 0x79, + + //LocalKeyboard = 0x7A, + /** + * All notes of. + */ + AllNotesOff = 0x7B + + //OmniModeOff = 0x7C, + //OmniModeOn = 0x7D, + //MonoMode = 0x7E, + //PolyMode = 0x7F +} diff --git a/src/midi/GeneralMidi.ts b/src/midi/GeneralMidi.ts new file mode 100644 index 000000000..8ab0534e2 --- /dev/null +++ b/src/midi/GeneralMidi.ts @@ -0,0 +1,55 @@ +/** + * This public class provides names for all general midi instruments. + */ +export class GeneralMidi { + private static _values: Map = 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] + ]); + + public static getValue(name: string): number { + if (!GeneralMidi._values) { + GeneralMidi._values = new Map(); + } + name = name.toLowerCase().split(' ').join(''); + return GeneralMidi._values.has(name) ? GeneralMidi._values.get(name)! : 0; + } + + public static isPiano(program: number): boolean { + return program <= 7 || program >= 16 && program <= 23; + } + + public static isGuitar(program: number): boolean { + return program >= 24 && program <= 39 || program===105 || program===43; + } +} diff --git a/src/midi/IMidiFileHandler.ts b/src/midi/IMidiFileHandler.ts new file mode 100644 index 000000000..296d8c80c --- /dev/null +++ b/src/midi/IMidiFileHandler.ts @@ -0,0 +1,82 @@ +import { DynamicValue } from '@src/model/DynamicValue'; + +/** + * A handler is responsible for writing midi events to a custom structure + */ +export interface IMidiFileHandler { + /** + * Adds a time signature to the generated midi file + * @param tick The midi ticks when this event should be happening. + * @param timeSignatureNumerator The time signature numerator + * @param timeSignatureDenominator The time signature denominator + */ + addTimeSignature(tick: number, timeSignatureNumerator: number, timeSignatureDenominator: number): void; + + /** + * Adds a rest to the generated midi file. + * @param track The midi track on which the rest should be "played". + * @param tick The midi ticks when the rest is "playing". + * @param channel The midi channel on which the rest should be "played". + */ + addRest(track: number, tick: number, channel: number): void; + + /** + * Adds a note to the generated midi file + * @param track The midi track on which the note should be played. + * @param start The midi ticks when the note should start playing. + * @param length The duration the note in midi ticks. + * @param key The key of the note to play + * @param dynamicValue The dynamic which should be applied to the note. + * @param channel The midi channel on which the note should be played. + */ + addNote( + track: number, + start: number, + length: number, + key: number, + dynamicValue: DynamicValue, + channel: number + ): void; + + /** + * Adds a control change to the generated midi file. + * @param track The midi track on which the controller should change. + * @param tick The midi ticks when the controller should change. + * @param channel The midi channel on which the controller should change. + * @param controller The midi controller that should change. + * @param value The value to which the midi controller should change + */ + addControlChange(track: number, tick: number, channel: number, controller: number, value: number): void; + + /** + * Add a program change to the generated midi file + * @param track The midi track on which the program should change. + * @param tick The midi ticks when the program should change. + * @param channel The midi channel on which the program should change. + * @param program The new program for the selected track and channel. + */ + addProgramChange(track: number, tick: number, channel: number, program: number): void; + + /** + * Add a tempo change to the generated midi file. + * @param tick The midi ticks when the tempo should change change. + * @param tempo The tempo as BPM + */ + addTempo(tick: number, tempo: number): void; + + /** + * Add a bend to the generated midi file. + * @param track The midi track on which the bend should change. + * @param tick The midi ticks when the bend should change. + * @param channel The midi channel on which the bend should change. + * @param value The new bend for the selected track and channel. + */ + addBend(track: number, tick: number, channel: number, value: number): void; + + /** + * Indicates that the track is finished on the given ticks. + * @param track The track that was finished. + * @param tick The end tick for this track. + */ + finishTrack(track: number, tick: number): void; +} diff --git a/src/midi/MasterBarTickLookup.ts b/src/midi/MasterBarTickLookup.ts new file mode 100644 index 000000000..fadcc0e53 --- /dev/null +++ b/src/midi/MasterBarTickLookup.ts @@ -0,0 +1,55 @@ +import { BeatTickLookup } from '@src/midi/BeatTickLookup'; +import { MasterBar } from '@src/model/MasterBar'; + +/** + * Represents the time period, for which all bars of a {@link MasterBar} are played. + */ +export class MasterBarTickLookup { + /** + * Gets or sets the start time in midi ticks at which the MasterBar is played. + */ + public start: number = 0; + + /** + * Gets or sets the end time in midi ticks at which the MasterBar is played. + */ + public end: number = 0; + + /** + * Gets or sets the current tempo when the MasterBar is played. + */ + public tempo: number = 0; + + /** + * Gets or sets the MasterBar which is played. + */ + public masterBar!: MasterBar; + + /** + * Gets or sets the list of {@link BeatTickLookup} object which define the durations + * for all {@link Beats} played within the period of this MasterBar. + */ + public beats: BeatTickLookup[] = []; + + /** + * Gets or sets the {@link MasterBarTickLookup} of the next masterbar in the {@link Score} + */ + public nextMasterBar: MasterBarTickLookup | null = null; + + /** + * Performs the neccessary finalization steps after all information was written. + */ + public finish(): void { + this.beats.sort((a, b) => { + return a.start - b.start; + }); + } + + /** + * Adds a new {@link BeatTickLookup} to the list of played beats during this MasterBar period. + * @param beat + */ + public addBeat(beat: BeatTickLookup): void { + this.beats.push(beat); + } +} diff --git a/src/midi/MetaDataEvent.ts b/src/midi/MetaDataEvent.ts new file mode 100644 index 000000000..cb21c86ff --- /dev/null +++ b/src/midi/MetaDataEvent.ts @@ -0,0 +1,20 @@ +import { MetaEvent } from '@src/midi/MetaEvent'; +import { MidiFile } from '@src/midi/MidiFile'; +import { IWriteable } from '@src/io/IWriteable'; + +export class MetaDataEvent extends MetaEvent { + public data: Uint8Array; + + public constructor(delta: number, status: number, metaId: number, data: Uint8Array) { + super(delta, status, metaId, 0); + this.data = data; + } + + public writeTo(s: IWriteable): void { + s.writeByte(0xff); + s.writeByte(this.metaStatus); + let l: number = this.data.length; + MidiFile.writeVariableInt(s, l); + s.write(this.data, 0, this.data.length); + } +} diff --git a/src/midi/MetaEvent.ts b/src/midi/MetaEvent.ts new file mode 100644 index 000000000..1017bca8d --- /dev/null +++ b/src/midi/MetaEvent.ts @@ -0,0 +1,40 @@ +import { MidiEvent, MidiEventType } from '@src/midi/MidiEvent'; + +export enum MetaEventType { + SequenceNumber = 0x00, + TextEvent = 0x01, + CopyrightNotice = 0x02, + SequenceOrTrackName = 0x03, + InstrumentName = 0x04, + LyricText = 0x05, + MarkerText = 0x06, + CuePoint = 0x07, + PatchName = 0x08, + PortName = 0x09, + MidiChannel = 0x20, + MidiPort = 0x21, + EndOfTrack = 0x2F, + Tempo = 0x51, + SmpteOffset = 0x54, + TimeSignature = 0x58, + KeySignature = 0x59, + SequencerSpecific = 0x7F +} + +export class MetaEvent extends MidiEvent { + public get channel(): number { + return -1; + } + + public get command(): MidiEventType { + return (this.message & 0x00000ff) as MidiEventType; + } + + public get metaStatus(): number { + return this.data1; + } + + protected constructor(delta: number, status: number, data1: number, data2: number) { + super(delta, status, data1, data2); + } +} diff --git a/src/midi/MetaNumberEvent.ts b/src/midi/MetaNumberEvent.ts new file mode 100644 index 000000000..e757d7d35 --- /dev/null +++ b/src/midi/MetaNumberEvent.ts @@ -0,0 +1,20 @@ +import { MetaEvent } from '@src/midi/MetaEvent'; +import { MidiFile } from '@src/midi/MidiFile'; +import { IWriteable } from '@src/io/IWriteable'; + +export class MetaNumberEvent extends MetaEvent { + public value: number; + + public constructor(delta: number, status: number, metaId: number, value: number) { + super(delta, status, metaId, 0); + this.value = value; + } + + public writeTo(s: IWriteable): void { + s.writeByte(0xff); + s.writeByte(this.metaStatus); + MidiFile.writeVariableInt(s, 3); + let b: Uint8Array = new Uint8Array([(this.value >> 16) & 0xff, (this.value >> 8) & 0xff, this.value & 0xff]); + s.write(b, 0, b.length); + } +} diff --git a/src/midi/MidiEvent.ts b/src/midi/MidiEvent.ts new file mode 100644 index 000000000..446a6b507 --- /dev/null +++ b/src/midi/MidiEvent.ts @@ -0,0 +1,113 @@ +import { IWriteable } from '@src/io/IWriteable'; + +/** + * Lists all midi events. + */ +export enum MidiEventType { + /** + * A note is released. + */ + NoteOff = 0x80, + + /** + * A note is started. + */ + NoteOn = 0x90, + + /** + * The pressure that was used to play the note. + */ + NoteAftertouch = 0xA0, + + /** + * Change of a midi controller + */ + Controller = 0xB0, + + /** + * Change of a midi program + */ + ProgramChange = 0xC0, + + /** + * The pressure that should be applied to the whole channel. + */ + ChannelAftertouch = 0xD0, + + /** + * A change of the audio pitch. + */ + PitchBend = 0xE0, + + /** + * A meta event. See for details. + */ + Meta = 0xFF +} + +/** + * Represents a midi event. + */ +export class MidiEvent { + /** + * Gets or sets the raw midi message. + */ + public message: number; + + /** + * Gets or sets the absolute tick of this midi event. + */ + public tick: number; + + public get channel(): number { + return this.message & 0x000000f; + } + + public get command(): MidiEventType { + return (this.message & 0x00000f0) as MidiEventType; + } + + public get data1(): number { + return (this.message & 0x000ff00) >> 8; + } + + public set data1(value: number) { + this.message &= ~0x000ff00; + this.message |= value << 8; + } + + public get data2(): number { + return (this.message & 0x0ff0000) >> 16; + } + + public set data2(value: number) { + this.message &= ~0x0ff0000; + this.message |= value << 16; + } + + /** + * Initializes a new instance of the {@link MidiEvent} class. + * @param tick The absolute midi ticks of this event.. + * @param status The status information of this event. + * @param data1 The first data component of this midi event. + * @param data2 The second data component of this midi event. + */ + public constructor(tick: number, status: number, data1: number, data2: number) { + this.tick = tick; + this.message = status | (data1 << 8) | (data2 << 16); + } + + /** + * Writes the midi event as binary into the given stream. + * @param s The stream to write to. + */ + public writeTo(s: IWriteable): void { + let b: Uint8Array = new Uint8Array([ + (this.message >> 24) & 0xff, + (this.message >> 16) & 0xff, + (this.message >> 8) & 0xff, + this.message & 0xff + ]); + s.write(b, 0, b.length); + } +} diff --git a/src/midi/MidiFile.ts b/src/midi/MidiFile.ts new file mode 100644 index 000000000..348be1bbf --- /dev/null +++ b/src/midi/MidiFile.ts @@ -0,0 +1,108 @@ +import { MidiEvent } from '@src/midi/MidiEvent'; +import { MidiUtils } from '@src/midi/MidiUtils'; +import { ByteBuffer } from '@src/io/ByteBuffer'; +import { IWriteable } from '@src/io/IWriteable'; + +/** + * Represents a midi file with a single track that can be played via {@link AlphaSynth} + */ +export class MidiFile { + /** + * Gets or sets the division per quarter notes. + */ + public division: number = MidiUtils.QuarterTime; + + /** + * Gets a list of midi events sorted by time. + */ + public readonly events: MidiEvent[] = []; + + /** + * Adds the given midi event a the correct time position into the file. + */ + public addEvent(e: MidiEvent): void { + if (this.events.length === 0) { + this.events.push(e); + } else { + let insertPos: number = this.events.length; + while (insertPos > 0) { + const prevItem: MidiEvent = this.events[insertPos - 1]; + if (prevItem.tick > e.tick) { + insertPos--; + } else { + break; + } + } + this.events.splice(insertPos, 0, e); + } + } + + /** + * Writes the midi file into a binary format. + * @returns The binary midi file. + */ + public toBinary(): Uint8Array { + let data: ByteBuffer = ByteBuffer.empty(); + this.writeTo(data); + return data.toArray(); + } + + /** + * Writes the midi file as binary into the given stream. + * @returns The stream to write to. + */ + public writeTo(s: IWriteable): void { + // magic number "MThd" (0x4D546864) + let b: Uint8Array = new Uint8Array([0x4d, 0x54, 0x68, 0x64]); + s.write(b, 0, b.length); + // Header Length 6 (0x00000006) + b = new Uint8Array([0x00, 0x00, 0x00, 0x06]); + s.write(b, 0, b.length); + // format + b = new Uint8Array([0x00, 0x00]); + s.write(b, 0, b.length); + // number of tracks + let v: number = 1; + b = new Uint8Array([(v >> 8) & 0xff, v & 0xff]); + s.write(b, 0, b.length); + v = this.division; + b = new Uint8Array([(v >> 8) & 0xff, v & 0xff]); + s.write(b, 0, b.length); + // build track data first + let trackData: ByteBuffer = ByteBuffer.empty(); + let previousTick: number = 0; + for (let midiEvent of this.events) { + let delta: number = midiEvent.tick - previousTick; + MidiFile.writeVariableInt(trackData, delta); + midiEvent.writeTo(trackData); + previousTick = midiEvent.tick; + } + // end of track + // magic number "MTrk" (0x4D54726B) + b = new Uint8Array([0x4d, 0x54, 0x72, 0x6b]); + s.write(b, 0, b.length); + // size as integer + let data: Uint8Array = trackData.toArray(); + let l: number = data.length; + b = new Uint8Array([(l >> 24) & 0xff, (l >> 16) & 0xff, (l >> 8) & 0xff, l & 0xff]); + s.write(b, 0, b.length); + s.write(data, 0, data.length); + } + + public static writeVariableInt(s: IWriteable, value: number): void { + let array: Uint8Array = new Uint8Array(4); + let n: number = 0; + do { + array[n++] = value & 0x7f; + value >>= 7; + } while (value > 0); + while (n > 0) { + n--; + if (n > 0) { + s.writeByte(array[n] | 0x80); + } else { + s.writeByte(array[n]); + } + } + } +} diff --git a/src/midi/MidiFileGenerator.ts b/src/midi/MidiFileGenerator.ts new file mode 100644 index 000000000..530f9a63a --- /dev/null +++ b/src/midi/MidiFileGenerator.ts @@ -0,0 +1,1261 @@ +import { BeatTickLookup } from '@src/midi/BeatTickLookup'; + +import { ControllerType } from '@src/midi/ControllerType'; +import { IMidiFileHandler } from '@src/midi/IMidiFileHandler'; + +import { MidiPlaybackController } from '@src/midi/MidiPlaybackController'; +import { MasterBarTickLookup } from '@src/midi/MasterBarTickLookup'; +import { MidiTickLookup } from '@src/midi/MidiTickLookup'; + +import { MidiUtils } from '@src/midi/MidiUtils'; +import { AccentuationType } from '@src/model/AccentuationType'; +import { Automation, AutomationType } from '@src/model/Automation'; +import { Bar } from '@src/model/Bar'; +import { Beat } from '@src/model/Beat'; +import { BendPoint } from '@src/model/BendPoint'; +import { BendStyle } from '@src/model/BendStyle'; +import { BendType } from '@src/model/BendType'; +import { BrushType } from '@src/model/BrushType'; +import { Duration } from '@src/model/Duration'; +import { DynamicValue } from '@src/model/DynamicValue'; +import { GraceType } from '@src/model/GraceType'; +import { MasterBar } from '@src/model/MasterBar'; +import { Note } from '@src/model/Note'; +import { PlaybackInformation } from '@src/model/PlaybackInformation'; +import { Score } from '@src/model/Score'; +import { SimileMark } from '@src/model/SimileMark'; +import { SlideInType } from '@src/model/SlideInType'; +import { SlideOutType } from '@src/model/SlideOutType'; +import { Staff } from '@src/model/Staff'; +import { Track } from '@src/model/Track'; +import { TripletFeel } from '@src/model/TripletFeel'; +import { VibratoType } from '@src/model/VibratoType'; +import { Voice } from '@src/model/Voice'; +import { WhammyType } from '@src/model/WhammyType'; +import { NotationMode } from '@src/NotationSettings'; +import { Settings } from '@src/Settings'; + +import { Logger } from '@src/Logger'; +import { SynthConstants } from '@src/synth/SynthConstants'; + +export class MidiNoteDuration { + public noteOnly: number = 0; + public untilTieOrSlideEnd: number = 0; + public letRingEnd: number = 0; +} + +class TripletFeelDurations { + public firstBeatDuration: number = 0; + public secondBeatStartOffset: number = 0; + public secondBeatDuration: number = 0; +} + +/** + * This generator creates a midi file using a score. + */ +export class MidiFileGenerator { + private static readonly DefaultDurationDead: number = 30; + private static readonly DefaultDurationPalmMute: number = 80; + + private readonly _score: Score; + private _settings: Settings; + private _handler: IMidiFileHandler; + private _currentTempo: number = 0; + private _currentBarRepeatLookup: BeatTickLookup | null = null; + + /** + * Gets a lookup object which can be used to quickly find beats and bars + * at a given midi tick position. + */ + public readonly tickLookup: MidiTickLookup = new MidiTickLookup(); + + /** + * Initializes a new instance of the {@link MidiFileGenerator} class. + * @param score The score for which the midi file should be generated. + * @param settings The settings ot use for generation. + * @param handler The handler that should be used for generating midi events. + */ + public constructor(score: Score, settings: Settings | null, handler: IMidiFileHandler) { + this._score = score; + this._settings = !settings ? new Settings() : settings; + this._currentTempo = this._score.tempo; + this._handler = handler; + } + + /** + * Starts the generation of the midi file. + */ + public generate(): void { + // initialize tracks + for (const track of this._score.tracks) { + this.generateTrack(track); + } + + Logger.debug('Midi', 'Begin midi generation'); + const controller: MidiPlaybackController = new MidiPlaybackController(this._score); + + let previousMasterBar: MasterBar | null = null; + // store the previous played bar for repeats + while (!controller.finished) { + const index: number = controller.index; + const bar: MasterBar = this._score.masterBars[index]; + const currentTick: number = controller.currentTick; + controller.processCurrent(); + + if (controller.shouldPlay) { + this.generateMasterBar(bar, previousMasterBar, currentTick); + for (const track of this._score.tracks) { + for (const staff of track.staves) { + if (index < staff.bars.length) { + this.generateBar(staff.bars[index], currentTick); + } + } + } + } + + controller.moveNext(); + previousMasterBar = bar; + } + + for (const track of this._score.tracks) { + this._handler.finishTrack(track.index, controller.currentTick); + } + + this.tickLookup.finish(); + Logger.debug('Midi', 'Midi generation done'); + } + + private generateTrack(track: Track): void { + // channel + this.generateChannel(track, track.playbackInfo.primaryChannel, track.playbackInfo); + if (track.playbackInfo.primaryChannel !== track.playbackInfo.secondaryChannel) { + this.generateChannel(track, track.playbackInfo.secondaryChannel, track.playbackInfo); + } + } + + private generateChannel(track: Track, channel: number, playbackInfo: PlaybackInformation): void { + let volume: number = MidiFileGenerator.toChannelShort(playbackInfo.volume); + let balance: number = MidiFileGenerator.toChannelShort(playbackInfo.balance); + this._handler.addControlChange(track.index, 0, channel, ControllerType.VolumeCoarse, volume); + this._handler.addControlChange(track.index, 0, channel, ControllerType.PanCoarse, balance); + this._handler.addControlChange(track.index, 0, channel, ControllerType.ExpressionControllerCoarse, 127); + + // set parameter that is being updated (0) -> PitchBendRangeCoarse + this._handler.addControlChange(track.index, 0, channel, ControllerType.RegisteredParameterFine, 0); + this._handler.addControlChange(track.index, 0, channel, ControllerType.RegisteredParameterCourse, 0); + + // Set PitchBendRangeCoarse to 12 + this._handler.addControlChange(track.index, 0, channel, ControllerType.DataEntryFine, 0); + this._handler.addControlChange( + track.index, + 0, + channel, + ControllerType.DataEntryCoarse, + MidiFileGenerator.PitchBendRangeInSemitones + ); + this._handler.addProgramChange(track.index, 0, channel, playbackInfo.program); + } + + private static toChannelShort(data: number): number { + const value: number = Math.max(-32768, Math.min(32767, data * 8 - 1)); + return Math.max(value, -1) + 1; + } + + private generateMasterBar(masterBar: MasterBar, previousMasterBar: MasterBar | null, currentTick: number): void { + // time signature + if ( + !previousMasterBar || + previousMasterBar.timeSignatureDenominator !== masterBar.timeSignatureDenominator || + previousMasterBar.timeSignatureNumerator !== masterBar.timeSignatureNumerator + ) { + this._handler.addTimeSignature( + currentTick, + masterBar.timeSignatureNumerator, + masterBar.timeSignatureDenominator + ); + } + + // tempo + if (!previousMasterBar) { + this._handler.addTempo(currentTick, masterBar.score.tempo); + this._currentTempo = masterBar.score.tempo; + } else if (masterBar.tempoAutomation) { + this._handler.addTempo(currentTick, masterBar.tempoAutomation.value); + this._currentTempo = masterBar.tempoAutomation.value; + } + + const masterBarLookup: MasterBarTickLookup = new MasterBarTickLookup(); + masterBarLookup.masterBar = masterBar; + masterBarLookup.start = currentTick; + masterBarLookup.tempo = this._currentTempo; + masterBarLookup.end = masterBarLookup.start + masterBar.calculateDuration(); + this.tickLookup.addMasterBar(masterBarLookup); + } + + private generateBar(bar: Bar, barStartTick: number): void { + let playbackBar: Bar = this.getPlaybackBar(bar); + this._currentBarRepeatLookup = null; + + for (const v of playbackBar.voices) { + this.generateVoice(v, barStartTick, bar); + } + } + + private getPlaybackBar(bar: Bar): Bar { + switch (bar.simileMark) { + case SimileMark.Simple: + if (bar.previousBar) { + bar = this.getPlaybackBar(bar.previousBar); + } + break; + case SimileMark.FirstOfDouble: + if (bar.previousBar && bar.previousBar.previousBar) { + bar = this.getPlaybackBar(bar.previousBar.previousBar); + } + break; + case SimileMark.SecondOfDouble: + if (bar.previousBar && bar.previousBar.previousBar) { + bar = this.getPlaybackBar(bar.previousBar.previousBar); + } + break; + } + return bar; + } + + private generateVoice(voice: Voice, barStartTick: number, realBar: Bar): void { + if (voice.isEmpty && (!voice.bar.isEmpty || voice.index !== 0)) { + return; + } + + for (const b of voice.beats) { + this.generateBeat(b, barStartTick, realBar); + } + } + + private _currentTripletFeel: TripletFeelDurations | null = null; + + private generateBeat(beat: Beat, barStartTick: number, realBar: Bar): void { + let beatStart: number = beat.playbackStart; + let audioDuration: number = beat.playbackDuration; + + if (beat.voice.bar.isEmpty) { + audioDuration = beat.voice.bar.masterBar.calculateDuration(); + } else if ( + beat.voice.bar.masterBar.tripletFeel !== TripletFeel.NoTripletFeel && + this._settings.player.playTripletFeel + ) { + if (this._currentTripletFeel) { + beatStart -= this._currentTripletFeel.secondBeatStartOffset; + audioDuration = this._currentTripletFeel.secondBeatDuration; + this._currentTripletFeel = null; + } else { + this._currentTripletFeel = MidiFileGenerator.calculateTripletFeelInfo(beatStart, audioDuration, beat); + if (this._currentTripletFeel) { + audioDuration = this._currentTripletFeel.firstBeatDuration; + } + } + } + + const beatLookup: BeatTickLookup = new BeatTickLookup(); + beatLookup.start = barStartTick + beatStart; + + const realTickOffset: number = !beat.nextBeat + ? audioDuration + : beat.nextBeat.absolutePlaybackStart - beat.absolutePlaybackStart; + beatLookup.end = barStartTick + beatStart; + beatLookup.highlightBeat(beat); + beatLookup.end += realTickOffset > audioDuration ? realTickOffset : audioDuration; + + // in case of normal playback register playback + if (realBar === beat.voice.bar) { + beatLookup.beat = beat; + this.tickLookup.addBeat(beatLookup); + } else { + beatLookup.isEmptyBar = true; + beatLookup.beat = realBar.voices[0].beats[0]; + if (!this._currentBarRepeatLookup) { + this._currentBarRepeatLookup = beatLookup; + this.tickLookup.addBeat(this._currentBarRepeatLookup); + } else { + this._currentBarRepeatLookup.end = beatLookup.end; + } + } + + const track: Track = beat.voice.bar.staff.track; + for (const automation of beat.automations) { + this.generateAutomation(beat, automation, barStartTick); + } + if (beat.isRest) { + this._handler.addRest(track.index, barStartTick + beatStart, track.playbackInfo.primaryChannel); + } else { + let brushInfo = this.getBrushInfo(beat); + for (const n of beat.notes) { + this.generateNote(n, barStartTick + beatStart, audioDuration, brushInfo); + } + } + + if (beat.vibrato !== VibratoType.None) { + let phaseLength: number = 240; + let bendAmplitude: number = 3; + switch (beat.vibrato) { + case VibratoType.Slight: + phaseLength = this._settings.player.vibrato.beatSlightLength; + bendAmplitude = this._settings.player.vibrato.beatSlightAmplitude; + break; + case VibratoType.Wide: + phaseLength = this._settings.player.vibrato.beatWideLength; + bendAmplitude = this._settings.player.vibrato.beatWideAmplitude; + break; + } + this.generateVibratorWithParams( + beat.voice.bar.staff.track, + barStartTick + beatStart, + beat.playbackDuration, + phaseLength, + bendAmplitude, + track.playbackInfo.secondaryChannel + ); + } + } + + private static calculateTripletFeelInfo( + beatStart: number, + audioDuration: number, + beat: Beat + ): TripletFeelDurations | null { + let initialDuration: Duration; + switch (beat.voice.bar.masterBar.tripletFeel) { + case TripletFeel.Triplet8th: + case TripletFeel.Dotted8th: + case TripletFeel.Scottish8th: + initialDuration = Duration.Eighth; + break; + case TripletFeel.Triplet16th: + case TripletFeel.Dotted16th: + case TripletFeel.Scottish16th: + initialDuration = Duration.Sixteenth; + break; + default: + // not possible + return null; + } + + const interval: number = MidiUtils.toTicks(initialDuration); + + // it must be a plain note with the expected duration + // without dots, triplets, grace notes etc. + if (audioDuration !== interval) { + return null; + } + + // check if the beat is aligned in respect to the duration + // e.g. the eighth notes on a 4/4 time signature must start exactly on the following + // times to get a triplet feel applied + // 0 480 960 1440 1920 2400 2880 3360 + if (beatStart % interval !== 0) { + return null; + } + + // ensure next beat matches spec + if (!beat.nextBeat || beat.nextBeat.voice !== beat.voice || beat.playbackDuration !== interval) { + return null; + } + + // looks like we have a triplet feel combination start here! + const durations: TripletFeelDurations = new TripletFeelDurations(); + switch (beat.voice.bar.masterBar.tripletFeel) { + case TripletFeel.Triplet8th: + durations.firstBeatDuration = MidiUtils.applyTuplet(MidiUtils.toTicks(Duration.Quarter), 3, 2); + durations.secondBeatDuration = MidiUtils.applyTuplet(MidiUtils.toTicks(Duration.Eighth), 3, 2); + break; + case TripletFeel.Dotted8th: + durations.firstBeatDuration = MidiUtils.applyDot(MidiUtils.toTicks(Duration.Eighth), false); + durations.secondBeatDuration = MidiUtils.toTicks(Duration.Sixteenth); + break; + case TripletFeel.Scottish8th: + durations.firstBeatDuration = MidiUtils.toTicks(Duration.Sixteenth); + durations.secondBeatDuration = MidiUtils.applyDot(MidiUtils.toTicks(Duration.Eighth), false); + break; + case TripletFeel.Triplet16th: + durations.firstBeatDuration = MidiUtils.applyTuplet(MidiUtils.toTicks(Duration.Eighth), 3, 2); + durations.secondBeatDuration = MidiUtils.applyTuplet(MidiUtils.toTicks(Duration.Sixteenth), 3, 2); + break; + case TripletFeel.Dotted16th: + durations.firstBeatDuration = MidiUtils.applyDot(MidiUtils.toTicks(Duration.Sixteenth), false); + durations.secondBeatDuration = MidiUtils.toTicks(Duration.ThirtySecond); + break; + case TripletFeel.Scottish16th: + durations.firstBeatDuration = MidiUtils.toTicks(Duration.ThirtySecond); + durations.secondBeatDuration = MidiUtils.applyDot(MidiUtils.toTicks(Duration.Sixteenth), false); + break; + } + // calculate the number of ticks the second beat can start earlier + durations.secondBeatStartOffset = audioDuration - durations.firstBeatDuration; + return durations; + } + + private generateNote(note: Note, beatStart: number, beatDuration: number, brushInfo: Int32Array): void { + const track: Track = note.beat.voice.bar.staff.track; + const staff: Staff = note.beat.voice.bar.staff; + const noteKey: number = note.realValue; + const brushOffset: number = note.isStringed && note.string <= brushInfo.length ? brushInfo[note.string - 1] : 0; + const noteStart: number = beatStart + brushOffset; + const noteDuration: MidiNoteDuration = this.getNoteDuration(note, beatDuration); + noteDuration.untilTieOrSlideEnd -= brushOffset; + noteDuration.noteOnly -= brushOffset; + noteDuration.letRingEnd -= brushOffset; + const dynamicValue: DynamicValue = MidiFileGenerator.getDynamicValue(note); + const channel: number = + note.hasBend || note.beat.hasWhammyBar || note.beat.vibrato !== VibratoType.None + ? track.playbackInfo.secondaryChannel + : track.playbackInfo.primaryChannel; + let initialBend: number = 0; + + if (note.hasBend) { + initialBend = MidiFileGenerator.getPitchWheel(note.bendPoints[0].value); + } else if (note.beat.hasWhammyBar) { + initialBend = MidiFileGenerator.getPitchWheel(note.beat.whammyBarPoints[0].value); + } else if ( + note.isTieDestination || + (note.slideOrigin && note.slideOrigin.slideOutType === SlideOutType.Legato) + ) { + initialBend = -1; + } else { + initialBend = MidiFileGenerator.getPitchWheel(0); + } + + if (initialBend >= 0) { + this._handler.addBend(track.index, noteStart, channel, initialBend); + } + + // + // Fade in + if (note.beat.fadeIn) { + this.generateFadeIn(note, noteStart, noteDuration); + } + + // + // Trill + if (note.isTrill && !staff.isPercussion) { + this.generateTrill(note, noteStart, noteDuration, noteKey, dynamicValue, channel); + // no further generation needed + return; + } + + // + // Tremolo Picking + if (note.beat.isTremolo) { + this.generateTremoloPicking(note, noteStart, noteDuration, noteKey, dynamicValue, channel); + // no further generation needed + return; + } + + // + // All String Bending/Variation effects + if (note.hasBend) { + this.generateBend(note, noteStart, noteDuration, channel); + } else if (note.beat.hasWhammyBar && note.index === 0) { + this.generateWhammy(note.beat, noteStart, noteDuration, channel); + } else if (note.slideInType !== SlideInType.None || note.slideOutType !== SlideOutType.None) { + this.generateSlide(note, noteStart, noteDuration, noteKey, dynamicValue, channel); + } else if (note.vibrato !== VibratoType.None) { + this.generateVibrato(note, noteStart, noteDuration, channel); + } + + // for tied notes, and target notes of legato slides we do not pick the note + // the previous one is extended + if (!note.isTieDestination && (!note.slideOrigin || note.slideOrigin.slideOutType !== SlideOutType.Legato)) { + let noteSoundDuration: number = Math.max(noteDuration.untilTieOrSlideEnd, noteDuration.letRingEnd); + this._handler.addNote(track.index, noteStart, noteSoundDuration, noteKey, dynamicValue, channel); + } + } + + private getNoteDuration(note: Note, duration: number): MidiNoteDuration { + const durationWithEffects: MidiNoteDuration = new MidiNoteDuration(); + durationWithEffects.noteOnly = duration; + durationWithEffects.untilTieOrSlideEnd = duration; + durationWithEffects.letRingEnd = duration; + if (note.isDead) { + durationWithEffects.noteOnly = this.applyStaticDuration(MidiFileGenerator.DefaultDurationDead, duration); + durationWithEffects.untilTieOrSlideEnd = durationWithEffects.noteOnly; + durationWithEffects.letRingEnd = durationWithEffects.noteOnly; + return durationWithEffects; + } + if (note.isPalmMute) { + durationWithEffects.noteOnly = this.applyStaticDuration( + MidiFileGenerator.DefaultDurationPalmMute, + duration + ); + durationWithEffects.untilTieOrSlideEnd = durationWithEffects.noteOnly; + durationWithEffects.letRingEnd = durationWithEffects.noteOnly; + return durationWithEffects; + } + if (note.isStaccato) { + durationWithEffects.noteOnly = (duration / 2) | 0; + durationWithEffects.untilTieOrSlideEnd = durationWithEffects.noteOnly; + durationWithEffects.letRingEnd = durationWithEffects.noteOnly; + return durationWithEffects; + } + if (note.isTieOrigin) { + const endNote: Note = note.tieDestination!; + // for the initial start of the tie calculate absolute duration from start to end note + if (endNote) { + if (!note.isTieDestination) { + const startTick: number = note.beat.absolutePlaybackStart; + const tieDestinationDuration: MidiNoteDuration = this.getNoteDuration( + endNote, + endNote.beat.playbackDuration + ); + const endTick: number = + endNote.beat.absolutePlaybackStart + tieDestinationDuration.untilTieOrSlideEnd; + durationWithEffects.untilTieOrSlideEnd = endTick - startTick; + } else { + // for continuing ties, take the current duration + the one from the destination + // this branch will be entered as part of the recusion of the if branch + const tieDestinationDuration: MidiNoteDuration = this.getNoteDuration( + endNote, + endNote.beat.playbackDuration + ); + durationWithEffects.untilTieOrSlideEnd = duration + tieDestinationDuration.untilTieOrSlideEnd; + } + } + } else if (note.slideOutType === SlideOutType.Legato) { + const endNote: Note = note.slideTarget!; + if (endNote) { + const startTick: number = note.beat.absolutePlaybackStart; + const slideTargetDuration: MidiNoteDuration = this.getNoteDuration( + endNote, + endNote.beat.playbackDuration + ); + const endTick: number = endNote.beat.absolutePlaybackStart + slideTargetDuration.untilTieOrSlideEnd; + durationWithEffects.untilTieOrSlideEnd = endTick - startTick; + } + } + + if (note.isLetRing && this._settings.notation.notationMode === NotationMode.GuitarPro) { + // LetRing ends when: + // - rest + let lastLetRingBeat: Beat = note.beat; + let letRingEnd: number = 0; + const maxDuration: number = note.beat.voice.bar.masterBar.calculateDuration(); + while (lastLetRingBeat.nextBeat) { + let next: Beat = lastLetRingBeat.nextBeat; + if (next.isRest) { + break; + } + // note on the same string + if (note.isStringed && next.hasNoteOnString(note.string)) { + break; + } + lastLetRingBeat = lastLetRingBeat.nextBeat; + letRingEnd = + lastLetRingBeat.absolutePlaybackStart - + note.beat.absolutePlaybackStart + + lastLetRingBeat.playbackDuration; + if (letRingEnd > maxDuration) { + letRingEnd = maxDuration; + break; + } + } + if (lastLetRingBeat === note.beat) { + durationWithEffects.letRingEnd = duration; + } else { + durationWithEffects.letRingEnd = letRingEnd; + } + } else { + durationWithEffects.letRingEnd = durationWithEffects.untilTieOrSlideEnd; + } + return durationWithEffects; + } + + private applyStaticDuration(duration: number, maximum: number): number { + const value: number = ((this._currentTempo * duration) / BendPoint.MaxPosition) | 0; + return Math.min(value, maximum); + } + + private static getDynamicValue(note: Note): DynamicValue { + let dynamicValue: DynamicValue = note.dynamics; + // more silent on hammer destination + if (!note.beat.voice.bar.staff.isPercussion && note.hammerPullOrigin) { + dynamicValue--; + } + // more silent on ghost notes + if (note.isGhost) { + dynamicValue--; + } + // louder on accent + switch (note.accentuated) { + case AccentuationType.Normal: + dynamicValue++; + break; + case AccentuationType.Heavy: + dynamicValue += 2; + break; + } + return dynamicValue; + } + + private generateFadeIn(note: Note, noteStart: number, noteDuration: MidiNoteDuration): void { + const track: Track = note.beat.voice.bar.staff.track; + const endVolume: number = MidiFileGenerator.toChannelShort(track.playbackInfo.volume); + const volumeFactor: number = endVolume / noteDuration.noteOnly; + const tickStep: number = 120; + const steps: number = (noteDuration.noteOnly / tickStep) | 0; + const endTick: number = noteStart + noteDuration.noteOnly; + for (let i: number = steps - 1; i >= 0; i--) { + const tick: number = endTick - i * tickStep; + const volume: number = (tick - noteStart) * volumeFactor; + if (i === steps - 1) { + this._handler.addControlChange( + track.index, + noteStart, + track.playbackInfo.primaryChannel, + ControllerType.VolumeCoarse, + volume + ); + this._handler.addControlChange( + track.index, + noteStart, + track.playbackInfo.secondaryChannel, + ControllerType.VolumeCoarse, + volume + ); + } + this._handler.addControlChange( + track.index, + tick, + track.playbackInfo.primaryChannel, + ControllerType.VolumeCoarse, + volume + ); + this._handler.addControlChange( + track.index, + tick, + track.playbackInfo.secondaryChannel, + ControllerType.VolumeCoarse, + volume + ); + } + } + + private generateVibrato(note: Note, noteStart: number, noteDuration: MidiNoteDuration, channel: number): void { + let phaseLength: number = 0; + let bendAmplitude: number = 0; + switch (note.vibrato) { + case VibratoType.Slight: + phaseLength = this._settings.player.vibrato.noteSlightLength; + bendAmplitude = this._settings.player.vibrato.noteSlightAmplitude; + break; + case VibratoType.Wide: + phaseLength = this._settings.player.vibrato.noteWideLength; + bendAmplitude = this._settings.player.vibrato.noteWideAmplitude; + break; + default: + return; + } + const track: Track = note.beat.voice.bar.staff.track; + this.generateVibratorWithParams(track, noteStart, noteDuration.noteOnly, phaseLength, bendAmplitude, channel); + } + + private generateVibratorWithParams( + track: Track, + noteStart: number, + noteDuration: number, + phaseLength: number, + bendAmplitude: number, + channel: number + ): void { + const resolution: number = 16; + const phaseHalf: number = (phaseLength / 2) | 0; + // 1st Phase stays at bend 0, + // then we have a sine wave with the given amplitude and phase length + noteStart += phaseLength; + const noteEnd: number = noteStart + noteDuration; + while (noteStart < noteEnd) { + let phase: number = 0; + const phaseDuration: number = noteStart + phaseLength < noteEnd ? phaseLength : noteEnd - noteStart; + while (phase < phaseDuration) { + let bend: number = bendAmplitude * Math.sin((phase * Math.PI) / phaseHalf); + this._handler.addBend( + track.index, + (noteStart + phase) | 0, + channel, + MidiFileGenerator.getPitchWheel(bend) + ); + phase += resolution; + } + noteStart += phaseLength; + } + } + + /** + * Maximum semitones that are supported in bends in one direction (up or down) + * GP has 8 full tones on whammys. + */ + private static readonly PitchBendRangeInSemitones = 8 * 2; + /** + * The value on how many pitch-values are used for one semitone + */ + private static readonly PitchValuePerSemitone: number = + SynthConstants.DefaultPitchWheel / MidiFileGenerator.PitchBendRangeInSemitones; + + /** + * The minimum number of breakpoints generated per semitone bend. + */ + private static readonly MinBreakpointsPerSemitone = 6; + + /** + * How long until a new breakpoint is generated for a bend. + */ + private static readonly MillisecondsPerBreakpoint = 150; + + /** + * Calculates the midi pitch wheel value for the give bend value. + */ + public static getPitchWheel(bendValue: number) { + // bend values are 1/4 notes therefore we only take half a semitone value per bend value + return SynthConstants.DefaultPitchWheel + (bendValue / 2) * MidiFileGenerator.PitchValuePerSemitone; + } + + private generateSlide( + note: Note, + noteStart: number, + noteDuration: MidiNoteDuration, + noteKey: number, + dynamicValue: DynamicValue, + channel: number + ) { + let duration: number = + note.slideOutType === SlideOutType.Legato ? noteDuration.noteOnly : noteDuration.untilTieOrSlideEnd; + let playedBendPoints: BendPoint[] = []; + let track: Track = note.beat.voice.bar.staff.track; + + const simpleSlidePitchOffset = this._settings.player.slide.simpleSlidePitchOffset; + const simpleSlideDurationOffset = Math.floor(BendPoint.MaxPosition * this._settings.player.slide.simpleSlideDurationRatio); + const shiftSlideDurationOffset = Math.floor(BendPoint.MaxPosition * this._settings.player.slide.shiftSlideDurationRatio); + + // Shift Slide: Play note, move up to target note, play end note + // Legato Slide: Play note, move up to target note, no pick on end note, just keep it ringing + + // 2 bend points: one on 0/0, dy/MaxPos. + + // Slide into from above/below: Play note on lower pitch, slide into it quickly at start + // Slide out above/blow: Play note on normal pitch, slide out quickly at end + + switch (note.slideInType) { + case SlideInType.IntoFromAbove: + playedBendPoints.push(new BendPoint(0, simpleSlidePitchOffset)); + playedBendPoints.push(new BendPoint(simpleSlideDurationOffset, 0)); + break; + case SlideInType.IntoFromBelow: + playedBendPoints.push(new BendPoint(0, -simpleSlidePitchOffset)); + playedBendPoints.push(new BendPoint(simpleSlideDurationOffset, 0)); + break; + } + + switch (note.slideOutType) { + case SlideOutType.Legato: + case SlideOutType.Shift: + playedBendPoints.push(new BendPoint(shiftSlideDurationOffset, 0)); + // normal note values are in 1/2 tones, bends are in 1/4 tones + const dy = (note.slideTarget!.realValue - note.realValue) * 2; + playedBendPoints.push(new BendPoint(BendPoint.MaxPosition, dy)); + break; + case SlideOutType.OutDown: + playedBendPoints.push(new BendPoint(BendPoint.MaxPosition - simpleSlideDurationOffset, 0)); + playedBendPoints.push(new BendPoint(BendPoint.MaxPosition, -simpleSlidePitchOffset)); + break; + case SlideOutType.OutUp: + playedBendPoints.push(new BendPoint(BendPoint.MaxPosition - simpleSlideDurationOffset, 0)); + playedBendPoints.push(new BendPoint(BendPoint.MaxPosition, simpleSlidePitchOffset)); + break; + } + + this.generateWhammyOrBend(noteStart, channel, duration, playedBendPoints, track); + } + + private generateBend(note: Note, noteStart: number, noteDuration: MidiNoteDuration, channel: number): void { + let bendPoints: BendPoint[] = note.bendPoints; + let track: Track = note.beat.voice.bar.staff.track; + // if bend is extended on next tied note, we directly bend to the final bend value + let finalBendValue: number | null = null; + // Bends are spread across all tied notes unless they have a bend on their own. + let duration: number; + if (note.isTieOrigin && this._settings.notation.extendBendArrowsOnTiedNotes) { + let endNote: Note = note; + while (endNote.isTieOrigin && !endNote.tieDestination!.hasBend) { + endNote = endNote.tieDestination!; + } + duration = + endNote.beat.absolutePlaybackStart - + note.beat.absolutePlaybackStart + + this.getNoteDuration(endNote, endNote.beat.playbackDuration).noteOnly; + } else if (note.isTieOrigin && note.beat.graceType !== GraceType.None) { + switch (note.tieDestination!.bendType) { + case BendType.Bend: + case BendType.BendRelease: + case BendType.PrebendBend: + finalBendValue = note.tieDestination!.bendPoints[1].value; + break; + case BendType.Prebend: + case BendType.PrebendRelease: + finalBendValue = note.tieDestination!.bendPoints[0].value; + break; + } + duration = Math.max( + noteDuration.noteOnly, + MidiUtils.millisToTicks(this._settings.player.songBookBendDuration, this._currentTempo) + ); + } else { + duration = noteDuration.noteOnly; + } + // ensure prebends are slightly before the actual note. + if (bendPoints[0].value > 0 && !note.isContinuedBend) { + noteStart--; + } + const bendDuration: number = Math.min( + duration, + MidiUtils.millisToTicks(this._settings.player.songBookBendDuration, this._currentTempo) + ); + let playedBendPoints: BendPoint[] = []; + switch (note.bendType) { + case BendType.Custom: + playedBendPoints = bendPoints; + break; + case BendType.Bend: + case BendType.Release: + switch (note.bendStyle) { + case BendStyle.Default: + playedBendPoints = bendPoints; + break; + case BendStyle.Gradual: + playedBendPoints.push(new BendPoint(0, note.bendPoints[0].value)); + if (!finalBendValue || finalBendValue < note.bendPoints[1].value) { + finalBendValue = note.bendPoints[1].value; + } + playedBendPoints.push(new BendPoint(BendPoint.MaxPosition, finalBendValue)); + break; + case BendStyle.Fast: + if (!finalBendValue || finalBendValue < note.bendPoints[1].value) { + finalBendValue = note.bendPoints[1].value; + } + if (note.beat.graceType === GraceType.BendGrace) { + this.generateSongBookWhammyOrBend( + noteStart, + channel, + duration, + track, + true, + [note.bendPoints[0].value, finalBendValue], + bendDuration + ); + } else { + this.generateSongBookWhammyOrBend( + noteStart, + channel, + duration, + track, + false, + [note.bendPoints[0].value, finalBendValue], + bendDuration + ); + } + return; + } + break; + case BendType.BendRelease: + switch (note.bendStyle) { + case BendStyle.Default: + playedBendPoints = bendPoints; + break; + case BendStyle.Gradual: + playedBendPoints.push(new BendPoint(0, note.bendPoints[0].value)); + playedBendPoints.push(new BendPoint((BendPoint.MaxPosition / 2) | 0, note.bendPoints[1].value)); + playedBendPoints.push(new BendPoint(BendPoint.MaxPosition, note.bendPoints[2].value)); + break; + case BendStyle.Fast: + this.generateSongBookWhammyOrBend( + noteStart, + channel, + duration, + track, + false, + [note.bendPoints[0].value, note.bendPoints[1].value, note.bendPoints[2].value], + bendDuration + ); + return; + } + break; + case BendType.Hold: + playedBendPoints = bendPoints; + break; + case BendType.Prebend: + playedBendPoints = bendPoints; + break; + case BendType.PrebendBend: + switch (note.bendStyle) { + case BendStyle.Default: + playedBendPoints = bendPoints; + break; + case BendStyle.Gradual: + playedBendPoints.push(new BendPoint(0, note.bendPoints[0].value)); + playedBendPoints.push(new BendPoint(BendPoint.MaxPosition, note.bendPoints[1].value)); + break; + case BendStyle.Fast: + const preBendValue: number = MidiFileGenerator.getPitchWheel(note.bendPoints[0].value); + this._handler.addBend(track.index, noteStart, channel, preBendValue | 0); + if (!finalBendValue || finalBendValue < note.bendPoints[1].value) { + finalBendValue = note.bendPoints[1].value; + } + this.generateSongBookWhammyOrBend( + noteStart, + channel, + duration, + track, + false, + [note.bendPoints[0].value, finalBendValue], + bendDuration + ); + return; + } + break; + case BendType.PrebendRelease: + switch (note.bendStyle) { + case BendStyle.Default: + playedBendPoints = bendPoints; + break; + case BendStyle.Gradual: + playedBendPoints.push(new BendPoint(0, note.bendPoints[0].value)); + playedBendPoints.push(new BendPoint(BendPoint.MaxPosition, note.bendPoints[1].value)); + break; + case BendStyle.Fast: + const preBendValue: number = MidiFileGenerator.getPitchWheel(note.bendPoints[0].value); + this._handler.addBend(track.index, noteStart, channel, preBendValue | 0); + this.generateSongBookWhammyOrBend( + noteStart, + channel, + duration, + track, + false, + [note.bendPoints[0].value, note.bendPoints[1].value], + bendDuration + ); + return; + } + break; + } + this.generateWhammyOrBend(noteStart, channel, duration, playedBendPoints, track); + } + + private generateSongBookWhammyOrBend( + noteStart: number, + channel: number, + duration: number, + track: Track, + bendAtBeginning: boolean, + bendValues: number[], + bendDuration: number + ): void { + const startTick: number = bendAtBeginning ? noteStart : noteStart + duration - bendDuration; + const ticksBetweenPoints: number = bendDuration / (bendValues.length - 1); + for (let i: number = 0; i < bendValues.length - 1; i++) { + const currentBendValue: number = MidiFileGenerator.getPitchWheel(bendValues[i]); + const nextBendValue: number = MidiFileGenerator.getPitchWheel(bendValues[i + 1]); + const tick: number = startTick + ticksBetweenPoints * i; + this.generateBendValues(tick, channel, track, ticksBetweenPoints, currentBendValue, nextBendValue); + } + } + + private generateWhammy(beat: Beat, noteStart: number, noteDuration: MidiNoteDuration, channel: number): void { + const bendPoints: BendPoint[] = beat.whammyBarPoints; + const track: Track = beat.voice.bar.staff.track; + const duration: number = noteDuration.noteOnly; + // ensure prebends are slightly before the actual note. + if (bendPoints[0].value > 0 && !beat.isContinuedWhammy) { + noteStart--; + } + + let playedBendPoints: BendPoint[] = []; + switch (beat.whammyBarType) { + case WhammyType.Custom: + playedBendPoints = bendPoints; + break; + case WhammyType.Dive: + switch (beat.whammyStyle) { + case BendStyle.Default: + playedBendPoints = bendPoints; + break; + case BendStyle.Gradual: + playedBendPoints.push(new BendPoint(0, bendPoints[0].value)); + playedBendPoints.push(new BendPoint(BendPoint.MaxPosition, bendPoints[1].value)); + break; + case BendStyle.Fast: + const whammyDuration: number = Math.min( + duration, + MidiUtils.millisToTicks(this._settings.player.songBookBendDuration, this._currentTempo) + ); + this.generateSongBookWhammyOrBend( + noteStart, + channel, + duration, + track, + false, + [bendPoints[0].value, bendPoints[1].value], + whammyDuration + ); + return; + } + break; + case WhammyType.Dip: + switch (beat.whammyStyle) { + case BendStyle.Default: + playedBendPoints = bendPoints; + break; + case BendStyle.Gradual: + playedBendPoints.push(new BendPoint(0, bendPoints[0].value)); + playedBendPoints.push(new BendPoint((BendPoint.MaxPosition / 2) | 0, bendPoints[1].value)); + playedBendPoints.push(new BendPoint(BendPoint.MaxPosition, bendPoints[2].value)); + break; + case BendStyle.Fast: + const whammyDuration: number = Math.min( + duration, + MidiUtils.millisToTicks(this._settings.player.songBookDipDuration, this._currentTempo) + ); + this.generateSongBookWhammyOrBend( + noteStart, + channel, + duration, + track, + true, + [bendPoints[0].value, bendPoints[1].value, bendPoints[2].value], + whammyDuration + ); + return; + } + break; + case WhammyType.Hold: + playedBendPoints = bendPoints; + break; + case WhammyType.Predive: + playedBendPoints = bendPoints; + break; + case WhammyType.PrediveDive: + switch (beat.whammyStyle) { + case BendStyle.Default: + playedBendPoints = bendPoints; + break; + case BendStyle.Gradual: + playedBendPoints.push(new BendPoint(0, bendPoints[0].value)); + playedBendPoints.push(new BendPoint((BendPoint.MaxPosition / 2) | 0, bendPoints[0].value)); + playedBendPoints.push(new BendPoint(BendPoint.MaxPosition, bendPoints[1].value)); + break; + case BendStyle.Fast: + const preDiveValue: number = MidiFileGenerator.getPitchWheel(bendPoints[0].value); + this._handler.addBend(track.index, noteStart, channel, preDiveValue | 0); + const whammyDuration: number = Math.min( + duration, + MidiUtils.millisToTicks(this._settings.player.songBookBendDuration, this._currentTempo) + ); + this.generateSongBookWhammyOrBend( + noteStart, + channel, + duration, + track, + false, + [bendPoints[0].value, bendPoints[1].value], + whammyDuration + ); + return; + } + break; + } + this.generateWhammyOrBend(noteStart, channel, duration, playedBendPoints, track); + } + + private generateWhammyOrBend( + noteStart: number, + channel: number, + duration: number, + playedBendPoints: BendPoint[], + track: Track + ): void { + const ticksPerPosition: number = duration / BendPoint.MaxPosition; + for (let i: number = 0; i < playedBendPoints.length - 1; i++) { + const currentPoint: BendPoint = playedBendPoints[i]; + const nextPoint: BendPoint = playedBendPoints[i + 1]; + // calculate the midi pitchbend values start and end values + const currentBendValue: number = MidiFileGenerator.getPitchWheel(currentPoint.value); + const nextBendValue: number = MidiFileGenerator.getPitchWheel(nextPoint.value); + // how many midi ticks do we have to spend between this point and the next one? + const ticksBetweenPoints: number = ticksPerPosition * (nextPoint.offset - currentPoint.offset); + // we will generate one pitchbend message for each value + // for this we need to calculate how many ticks to offset per value + const tick: number = noteStart + ticksPerPosition * currentPoint.offset; + this.generateBendValues(tick, channel, track, ticksBetweenPoints, currentBendValue, nextBendValue); + } + } + + private generateBendValues( + currentTick: number, + channel: number, + track: Track, + ticksBetweenPoints: number, + currentBendValue: number, + nextBendValue: number + ): void { + const millisBetweenPoints = MidiUtils.ticksToMillis(ticksBetweenPoints, this._currentTempo); + const numberOfSemitones = Math.abs(nextBendValue - currentBendValue) / MidiFileGenerator.PitchValuePerSemitone; + const numberOfSteps = Math.max( + MidiFileGenerator.MinBreakpointsPerSemitone * numberOfSemitones, + millisBetweenPoints / MidiFileGenerator.MillisecondsPerBreakpoint + ); + const ticksPerBreakpoint: number = ticksBetweenPoints / numberOfSteps; + const pitchPerBreakpoint = (nextBendValue - currentBendValue) / numberOfSteps; + + for (let i = 0; i < numberOfSteps; i++) { + this._handler.addBend(track.index, currentTick | 0, channel, Math.round(currentBendValue)); + currentBendValue += pitchPerBreakpoint; + currentTick += ticksPerBreakpoint; + } + + // final bend value if needed + if (currentBendValue < nextBendValue) { + this._handler.addBend(track.index, currentTick | 0, channel, nextBendValue); + } + } + + private generateTrill( + note: Note, + noteStart: number, + noteDuration: MidiNoteDuration, + noteKey: number, + dynamicValue: DynamicValue, + channel: number + ): void { + const track: Track = note.beat.voice.bar.staff.track; + const trillKey: number = note.stringTuning + note.trillFret; + let trillLength: number = MidiUtils.toTicks(note.trillSpeed); + let realKey: boolean = true; + let tick: number = noteStart; + let end: number = noteStart + noteDuration.untilTieOrSlideEnd; + while (tick + 10 < end) { + // only the rest on last trill play + if (tick + trillLength >= end) { + trillLength = end - tick; + } + this._handler.addNote(track.index, tick, trillLength, realKey ? trillKey : noteKey, dynamicValue, channel); + realKey = !realKey; + tick += trillLength; + } + } + + private generateTremoloPicking( + note: Note, + noteStart: number, + noteDuration: MidiNoteDuration, + noteKey: number, + dynamicValue: DynamicValue, + channel: number + ): void { + const track: Track = note.beat.voice.bar.staff.track; + let tpLength: number = MidiUtils.toTicks(note.beat.tremoloSpeed!); + let tick: number = noteStart; + const end: number = noteStart + noteDuration.untilTieOrSlideEnd; + while (tick + 10 < end) { + // only the rest on last trill play + if (tick + tpLength >= end) { + tpLength = end - tick; + } + this._handler.addNote(track.index, tick, tpLength, noteKey, dynamicValue, channel); + tick += tpLength; + } + } + + private getBrushInfo(beat: Beat): Int32Array { + const brushInfo = new Int32Array(beat.voice.bar.staff.tuning.length); + if (beat.brushType !== BrushType.None) { + // + // calculate the number of + // a mask where the single bits indicate the strings used + let stringUsed: number = 0; + let stringCount: number = 0; + for (const n of beat.notes) { + if (n.isTieDestination) { + continue; + } + stringUsed |= 0x01 << (n.string - 1); + stringCount++; + } + // + // calculate time offset for all strings + if (beat.notes.length > 0) { + let brushMove: number = 0; + const brushIncrement: number = (beat.brushDuration / (stringCount - 1)) | 0; + for (let i: number = 0; i < beat.voice.bar.staff.tuning.length; i++) { + let index: number = + beat.brushType === BrushType.ArpeggioDown || beat.brushType === BrushType.BrushDown + ? i + : brushInfo.length - 1 - i; + if ((stringUsed & (0x01 << index)) !== 0) { + brushInfo[index] = brushMove; + brushMove += brushIncrement; + } + } + } + } + return brushInfo; + } + + private generateAutomation(beat: Beat, automation: Automation, startMove: number): void { + switch (automation.type) { + case AutomationType.Instrument: + this._handler.addProgramChange( + beat.voice.bar.staff.track.index, + beat.playbackStart + startMove, + beat.voice.bar.staff.track.playbackInfo.primaryChannel, + (automation.value | 0) & 0xff + ); + this._handler.addProgramChange( + beat.voice.bar.staff.track.index, + beat.playbackStart + startMove, + beat.voice.bar.staff.track.playbackInfo.secondaryChannel, + (automation.value | 0) & 0xff + ); + break; + case AutomationType.Balance: + let balance: number = MidiFileGenerator.toChannelShort(automation.value); + this._handler.addControlChange( + beat.voice.bar.staff.track.index, + beat.playbackStart + startMove, + beat.voice.bar.staff.track.playbackInfo.primaryChannel, + ControllerType.PanCoarse, + balance + ); + this._handler.addControlChange( + beat.voice.bar.staff.track.index, + beat.playbackStart + startMove, + beat.voice.bar.staff.track.playbackInfo.secondaryChannel, + ControllerType.PanCoarse, + balance + ); + break; + case AutomationType.Volume: + let volume: number = MidiFileGenerator.toChannelShort(automation.value); + this._handler.addControlChange( + beat.voice.bar.staff.track.index, + beat.playbackStart + startMove, + beat.voice.bar.staff.track.playbackInfo.primaryChannel, + ControllerType.VolumeCoarse, + volume + ); + this._handler.addControlChange( + beat.voice.bar.staff.track.index, + beat.playbackStart + startMove, + beat.voice.bar.staff.track.playbackInfo.secondaryChannel, + ControllerType.VolumeCoarse, + volume + ); + break; + } + } +} diff --git a/src/midi/MidiPlaybackController.ts b/src/midi/MidiPlaybackController.ts new file mode 100644 index 000000000..3f111921d --- /dev/null +++ b/src/midi/MidiPlaybackController.ts @@ -0,0 +1,75 @@ +import { MasterBar } from '@src/model/MasterBar'; +import { Score } from '@src/model/Score'; + +export class MidiPlaybackController { + private _score: Score; + private _repeatStartIndex: number = 0; + private _repeatNumber: number = 0; + private _repeatOpen: boolean = false; + + public shouldPlay: boolean = true; + public index: number = 0; + public currentTick: number = 0; + + public get finished(): boolean { + return this.index >= this._score.masterBars.length; + } + + public constructor(score: Score) { + this._score = score; + } + + public processCurrent(): void { + const masterBar: MasterBar = this._score.masterBars[this.index]; + const masterBarAlternateEndings: number = masterBar.alternateEndings; + // if the repeat group wasn't closed we reset the repeating + // on the last group opening + if ( + !masterBar.repeatGroup.isClosed && + masterBar.repeatGroup.openings[masterBar.repeatGroup.openings.length - 1] === masterBar + ) { + this._repeatNumber = 0; + this._repeatOpen = false; + } + if ((masterBar.isRepeatStart || masterBar.index === 0) && this._repeatNumber === 0) { + this._repeatStartIndex = this.index; + this._repeatOpen = true; + } else if (masterBar.isRepeatStart) { + this.shouldPlay = true; + } + // if we encounter an alternate ending + if (this._repeatOpen && masterBarAlternateEndings > 0) { + // do we need to skip this section? + if ((masterBarAlternateEndings & (1 << this._repeatNumber)) === 0) { + this.shouldPlay = false; + } else { + this.shouldPlay = true; + } + } + if (this.shouldPlay) { + this.currentTick += masterBar.calculateDuration(); + } + } + + public moveNext(): void { + const masterBar: MasterBar = this._score.masterBars[this.index]; + const masterBarRepeatCount: number = masterBar.repeatCount - 1; + // if we encounter a repeat end + if (this._repeatOpen && masterBarRepeatCount > 0) { + // more repeats required? + if (this._repeatNumber < masterBarRepeatCount) { + // jump to start + this.index = this._repeatStartIndex; + this._repeatNumber++; + } else { + // no repeats anymore, jump after repeat end + this._repeatNumber = 0; + this._repeatOpen = false; + this.shouldPlay = true; + this.index++; + } + } else { + this.index++; + } + } +} diff --git a/src/midi/MidiTickLookup.ts b/src/midi/MidiTickLookup.ts new file mode 100644 index 000000000..d6151f153 --- /dev/null +++ b/src/midi/MidiTickLookup.ts @@ -0,0 +1,233 @@ +import { BeatTickLookup } from '@src/midi/BeatTickLookup'; +import { MasterBarTickLookup } from '@src/midi/MasterBarTickLookup'; +import { MidiUtils } from '@src/midi/MidiUtils'; +import { Beat } from '@src/model/Beat'; +import { MasterBar } from '@src/model/MasterBar'; +import { Track } from '@src/model/Track'; + +/** + * Represents the results of searching the currently played beat. + * @see MidiTickLookup.FindBeat + */ +export class MidiTickLookupFindBeatResult { + /** + * Gets or sets the beat that is currently played. + */ + public currentBeat!: Beat; + + /** + * Gets or sets the beat that will be played next. + */ + public nextBeat: Beat | null = null; + + /** + * Gets or sets the duration in milliseconds how long this beat is playing. + */ + public duration: number = 0; + + /** + * Gets or sets the beats ot highlight along the current beat. + */ + public beatsToHighlight!: Beat[]; +} + +/** + * This class holds all information about when {@link MasterBar}s and {@link Beat}s are played. + */ +export class MidiTickLookup { + private _currentMasterBar: MasterBarTickLookup | null = null; + + /** + * Gets a dictionary of all master bars played. The index is the index equals to {@link MasterBar.index}. + * This lookup only contains the first time a MasterBar is played. For a whole sequence of the song refer to {@link MasterBars}. + */ + public readonly masterBarLookup: Map = new Map(); + + /** + * Gets a list of all {@link MasterBarTickLookup} sorted by time. + */ + public readonly masterBars: MasterBarTickLookup[] = []; + + /** + * Performs the neccessary finalization steps after all information was written. + */ + public finish(): void { + let previous: MasterBarTickLookup | null = null; + let activeBeats: BeatTickLookup[] = []; + + for (let bar of this.masterBars) { + bar.finish(); + if (previous) { + previous.nextMasterBar = bar; + } + + for (const beat of bar.beats) { + // 1. calculate newly which beats are still active + const newActiveBeats: BeatTickLookup[] = []; + // TODO: only create new list if current position changed + for (let activeBeat of activeBeats) { + if (activeBeat.end > beat.start) { + newActiveBeats.push(activeBeat); + // 2. remember for current beat which active beats to highlight + beat.highlightBeat(activeBeat.beat); + // 3. ensure that active beat highlights current beat if they match the range + if (beat.start <= activeBeat.start) { + activeBeat.highlightBeat(beat.beat); + } + } + } + newActiveBeats.push(beat); + activeBeats = newActiveBeats; + } + previous = bar; + } + } + + /** + * Finds the currently played beat given a list of tracks and the current time. + * @param tracks The tracks in which to search the played beat for. + * @param tick The current time in midi ticks. + * @returns The information about the current beat or null if no beat could be found. + */ + public findBeat(tracks: Track[], tick: number): MidiTickLookupFindBeatResult | null { + // get all beats within the masterbar + const masterBar = this.findMasterBar(tick); + if (!masterBar) { + return null; + } + + const trackLookup: Map = new Map(); + for (const track of tracks) { + trackLookup.set(track.index, true); + } + + let beat: BeatTickLookup | null = null; + let index: number = 0; + let beats: BeatTickLookup[] = masterBar.beats; + for (let b: number = 0; b < beats.length; b++) { + // is the current beat played on the given tick? + let currentBeat: BeatTickLookup = beats[b]; + // skip non relevant beats + if (!trackLookup.has(currentBeat.beat.voice.bar.staff.track.index)) { + continue; + } + + if (currentBeat.start <= tick && tick < currentBeat.end) { + // take the latest played beat we can find. (most right) + if (!beat || beat.start < currentBeat.start) { + beat = beats[b]; + index = b; + } + } else if (currentBeat.end > tick) { + break; + } + } + + if (!beat) { + return null; + } + + // search for next relevant beat in masterbar + let nextBeat: BeatTickLookup | null = null; + for (let b: number = index + 1; b < beats.length; b++) { + const currentBeat: BeatTickLookup = beats[b]; + if ( + currentBeat.start > beat.start && + trackLookup.has(currentBeat.beat.voice.bar.staff.track.index) + ) { + nextBeat = currentBeat; + break; + } + } + + // first relevant beat in next bar + if (!nextBeat && masterBar.nextMasterBar) { + beats = masterBar.nextMasterBar.beats; + for (let b: number = 0; b < beats.length; b++) { + const currentBeat: BeatTickLookup = beats[b]; + if ( + trackLookup.has(currentBeat.beat.voice.bar.staff.track.index) + ) { + nextBeat = currentBeat; + break; + } + } + } + + const result: MidiTickLookupFindBeatResult = new MidiTickLookupFindBeatResult(); + result.currentBeat = beat.beat; + result.nextBeat = !nextBeat ? null : nextBeat.beat; + result.duration = !nextBeat + ? MidiUtils.ticksToMillis(beat.end - beat.start, masterBar.tempo) + : MidiUtils.ticksToMillis(nextBeat.start - beat.start, masterBar.tempo); + result.beatsToHighlight = beat.beatsToHighlight; + return result; + } + + private findMasterBar(tick: number): MasterBarTickLookup | null { + const bars: MasterBarTickLookup[] = this.masterBars; + let bottom: number = 0; + let top: number = bars.length - 1; + while (bottom <= top) { + const middle: number = ((top + bottom) / 2) | 0; + const bar: MasterBarTickLookup = bars[middle]; + // found? + if (tick >= bar.start && tick < bar.end) { + return bar; + } + // search in lower half + if (tick < bar.start) { + top = middle - 1; + } else { + bottom = middle + 1; + } + } + return null; + } + + /** + * Gets the {@link MasterBarTickLookup} for a given masterbar at which the masterbar is played the first time. + * @param bar The masterbar to find the time period for. + * @returns A {@link MasterBarTickLookup} containing the details about the first time the {@link MasterBar} is played. + */ + public getMasterBar(bar: MasterBar): MasterBarTickLookup { + if (!this.masterBarLookup.has(bar.index)) { + const fallback = new MasterBarTickLookup(); + fallback.masterBar = bar; + return fallback; + } + return this.masterBarLookup.get(bar.index)!; + } + + /** + * Gets the start time in midi ticks for a given masterbar at which the masterbar is played the first time. + * @param bar The masterbar to find the time period for. + * @returns The time in midi ticks at which the masterbar is played the first time or 0 if the masterbar is not contained + */ + public getMasterBarStart(bar: MasterBar): number { + if (!this.masterBarLookup.has(bar.index)) { + return 0; + } + return this.masterBarLookup.get(bar.index)!.start; + } + + /** + * Adds a new {@link MasterBarTickLookup} to the lookup table. + * @param masterBar The item to add. + */ + public addMasterBar(masterBar: MasterBarTickLookup): void { + this.masterBars.push(masterBar); + this._currentMasterBar = masterBar; + if (!this.masterBarLookup.has(masterBar.masterBar.index)) { + this.masterBarLookup.set(masterBar.masterBar.index, masterBar); + } + } + + /** + * Adds the given {@link BeatTickLookup} to the current {@link MidiTickLookup}. + * @param beat The lookup to add. + */ + public addBeat(beat: BeatTickLookup): void { + this._currentMasterBar?.addBeat(beat); + } +} diff --git a/src/midi/MidiUtils.ts b/src/midi/MidiUtils.ts new file mode 100644 index 000000000..de37315b6 --- /dev/null +++ b/src/midi/MidiUtils.ts @@ -0,0 +1,66 @@ +import { Duration } from '@src/model/Duration'; +import { DynamicValue } from '@src/model/DynamicValue'; + +export class MidiUtils { + public static readonly QuarterTime: number = 960; + private static readonly MinVelocity: number = 15; + private static readonly VelocityIncrement: number = 16; + + /** + * 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. + */ + public static ticksToMillis(ticks: number, tempo: number): number { + 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. + */ + public static millisToTicks(millis: number, tempo: number): number { + return (millis / (60000.0 / (tempo * MidiUtils.QuarterTime))) | 0; + } + + /** + * Converts a duration value to its ticks equivalent. + */ + public static toTicks(duration: Duration): number { + 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,...) + */ + public static valueToTicks(duration: number): number { + let denomninator: number = duration; + if (denomninator < 0) { + denomninator = 1 / -denomninator; + } + return (MidiUtils.QuarterTime * (4.0 / denomninator)) | 0; + } + + public static applyDot(ticks: number, doubleDotted: boolean): number { + if (doubleDotted) { + return ticks + ((ticks / 4) | 0) * 3; + } + return ticks + ((ticks / 2) | 0); + } + + public static applyTuplet(ticks: number, numerator: number, denominator: number): number { + return ((ticks * denominator) / numerator) | 0; + } + + public static removeTuplet(ticks: number, numerator: number, denominator: number): number { + return ((ticks * numerator) / denominator) | 0; + } + + public static dynamicToVelocity(dyn: DynamicValue): number { + return MidiUtils.MinVelocity + dyn * MidiUtils.VelocityIncrement; + } +} diff --git a/src/midi/SystemCommonEvent.ts b/src/midi/SystemCommonEvent.ts new file mode 100644 index 000000000..fc14f51fc --- /dev/null +++ b/src/midi/SystemCommonEvent.ts @@ -0,0 +1,24 @@ +import { MidiEvent, MidiEventType } from '@src/midi/MidiEvent'; + +export enum SystemCommonType { + SystemExclusive = 0xF0, + MtcQuarterFrame = 0xF1, + SongPosition = 0xF2, + SongSelect = 0xF3, + TuneRequest = 0xF6, + SystemExclusive2 = 0xF7 +} + +export class SystemCommonEvent extends MidiEvent { + public get channel(): number { + return -1; + } + + public get command(): MidiEventType { + return (this.message & 0x00000ff) as MidiEventType; + } + + protected constructor(delta: number, status: number, data1: number, data2: number) { + super(delta, status, data1, data2); + } +} diff --git a/src/midi/SystemExclusiveEvent.ts b/src/midi/SystemExclusiveEvent.ts new file mode 100644 index 000000000..b5ea58d2d --- /dev/null +++ b/src/midi/SystemExclusiveEvent.ts @@ -0,0 +1,24 @@ +import { SystemCommonEvent } from '@src/midi/SystemCommonEvent'; +import { IWriteable } from '@src/io/IWriteable'; + +export class SystemExclusiveEvent extends SystemCommonEvent { + public data: Uint8Array; + + public get manufacturerId(): number { + return this.message >> 8; + } + + public constructor(delta: number, status: number, id: number, data: Uint8Array) { + super(delta, status, id & 0x00ff, (id >> 8) & 0xff); + this.data = data; + } + + public writeTo(s: IWriteable): void { + s.writeByte(0xf0); + let l: number = this.data.length + 2; + s.writeByte(this.manufacturerId); + let b: Uint8Array = new Uint8Array([(l >> 24) & 0xff, (l >> 16) & 0xff, (l >> 8) & 0xff, l & 0xff]); + s.write(b, 0, b.length); + s.writeByte(0xf7); + } +} diff --git a/src/model/AccentuationType.ts b/src/model/AccentuationType.ts new file mode 100644 index 000000000..8988933f7 --- /dev/null +++ b/src/model/AccentuationType.ts @@ -0,0 +1,17 @@ +/** + * Lists all types of note acceuntations + */ +export enum AccentuationType { + /** + * No accentuation + */ + None, + /** + * Normal accentuation + */ + Normal, + /** + * Heavy accentuation + */ + Heavy +} diff --git a/src/model/AccidentalType.ts b/src/model/AccidentalType.ts new file mode 100644 index 000000000..0cafde47d --- /dev/null +++ b/src/model/AccidentalType.ts @@ -0,0 +1,33 @@ +/** + * Defines all possible accidentals for notes. + */ +export enum AccidentalType { + /** + * No accidental + */ + None, + /** + * Naturalize + */ + Natural, + /** + * Sharp + */ + Sharp, + /** + * Flat + */ + Flat, + /** + * Natural for smear bends + */ + NaturalQuarterNoteUp, + /** + * Sharp for smear bends + */ + SharpQuarterNoteUp, + /** + * Flat for smear bends + */ + FlatQuarterNoteUp +} diff --git a/src/model/Automation.ts b/src/model/Automation.ts new file mode 100644 index 000000000..8f3b1d094 --- /dev/null +++ b/src/model/Automation.ts @@ -0,0 +1,83 @@ +/** + * This public enumeration lists all types of automations. + */ +export enum AutomationType { + /** + * Tempo change. + */ + Tempo, + /** + * Colume change. + */ + Volume, + /** + * Instrument change. + */ + Instrument, + /** + * Balance change. + */ + Balance +} + +/** + * Automations are used to change the behaviour of a song. + */ +export class Automation { + /** + * Gets or sets whether the automation is applied linear. + */ + public isLinear: boolean = false; + + /** + * Gets or sets the type of the automation. + */ + public type: AutomationType = AutomationType.Tempo; + + /** + * Gets or sets the target value of the automation. + */ + public value: number = 0; + + /** + * Gets or sets the relative position of of the automation. + */ + public ratioPosition: number = 0; + + /** + * Gets or sets the additional text of the automation. + */ + public text: string = ''; + + public static buildTempoAutomation( + isLinear: boolean, + ratioPosition: number, + value: number, + reference: number + ): Automation { + if (reference < 1 || reference > 5) { + reference = 2; + } + let references: Float32Array = new Float32Array([1, 0.5, 1.0, 1.5, 2.0, 3.0]); + let automation: Automation = new Automation(); + automation.type = AutomationType.Tempo; + automation.isLinear = isLinear; + automation.ratioPosition = ratioPosition; + automation.value = value * references[reference]; + return automation; + } + + public static copyTo(src: Automation, dst: Automation): void { + dst.isLinear = src.isLinear; + dst.ratioPosition = src.ratioPosition; + dst.text = src.text; + dst.type = src.type; + dst.value = src.value; + } + + public clone(): Automation { + let a: Automation = new Automation(); + Automation.copyTo(this, a); + return a; + } +} diff --git a/src/model/Bar.ts b/src/model/Bar.ts new file mode 100644 index 000000000..ab480c83a --- /dev/null +++ b/src/model/Bar.ts @@ -0,0 +1,104 @@ +import { Clef } from '@src/model/Clef'; +import { MasterBar } from '@src/model/MasterBar'; +import { Ottavia } from '@src/model/Ottavia'; +import { SimileMark } from '@src/model/SimileMark'; +import { Staff } from '@src/model/Staff'; +import { Voice } from '@src/model/Voice'; +import { Settings } from '@src/Settings'; + +/** + * A bar is a single block within a track, also known as Measure. + */ +export class Bar { + private static _globalBarId: number = 0; + + /** + * Gets or sets the unique id of this bar. + */ + public id: number = Bar._globalBarId++; + + /** + * Gets or sets the zero-based index of this bar within the staff. + */ + public index: number = 0; + + /** + * Gets or sets the next bar that comes after this bar. + */ + public nextBar: Bar | null = null; + + /** + * Gets or sets the previous bar that comes before this bar. + */ + public previousBar: Bar | null = null; + + /** + * Gets or sets the clef on this bar. + */ + public clef: Clef = Clef.G2; + + /** + * Gets or sets the ottava applied to the clef. + */ + public clefOttava: Ottavia = Ottavia.Regular; + + /** + * Gets or sets the reference to the parent staff. + */ + public staff!: Staff; + + /** + * Gets or sets the list of voices contained in this bar. + */ + public voices: Voice[] = []; + + /** + * Gets or sets the simile mark on this bar. + */ + public simileMark: SimileMark = SimileMark.None; + + public get masterBar(): MasterBar { + return this.staff.track.score.masterBars[this.index]; + } + + public get isEmpty(): boolean { + for (let i: number = 0, j: number = this.voices.length; i < j; i++) { + if (!this.voices[i].isEmpty) { + return false; + } + } + return true; + } + + public static copyTo(src: Bar, dst: Bar): void { + dst.id = src.id; + dst.index = src.index; + dst.clef = src.clef; + dst.clefOttava = src.clefOttava; + dst.simileMark = src.simileMark; + } + + public addVoice(voice: Voice): void { + voice.bar = this; + voice.index = this.voices.length; + this.voices.push(voice); + } + + public finish(settings: Settings): void { + for (let i: number = 0, j: number = this.voices.length; i < j; i++) { + let voice: Voice = this.voices[i]; + voice.finish(settings); + } + } + + public calculateDuration(): number { + let duration: number = 0; + for (let voice of this.voices) { + let voiceDuration: number = voice.calculateDuration(); + if (voiceDuration > duration) { + duration = voiceDuration; + } + } + return duration; + } +} diff --git a/src/model/Beat.ts b/src/model/Beat.ts new file mode 100644 index 000000000..ba6b94f37 --- /dev/null +++ b/src/model/Beat.ts @@ -0,0 +1,795 @@ +import { MidiUtils } from '@src/midi/MidiUtils'; +import { Automation, AutomationType } from '@src/model/Automation'; +import { BendPoint } from '@src/model/BendPoint'; +import { BendStyle } from '@src/model/BendStyle'; +import { BendType } from '@src/model/BendType'; +import { BrushType } from '@src/model/BrushType'; +import { Chord } from '@src/model/Chord'; +import { CrescendoType } from '@src/model/CrescendoType'; +import { Duration } from '@src/model/Duration'; +import { DynamicValue } from '@src/model/DynamicValue'; +import { Fermata } from '@src/model/Fermata'; +import { GraceType } from '@src/model/GraceType'; +import { Note } from '@src/model/Note'; +import { Ottavia } from '@src/model/Ottavia'; +import { PickStroke } from '@src/model/PickStroke'; +import { TupletGroup } from '@src/model/TupletGroup'; +import { VibratoType } from '@src/model/VibratoType'; +import { Voice } from '@src/model/Voice'; +import { WhammyType } from '@src/model/WhammyType'; +import { NotationMode } from '@src/NotationSettings'; +import { Settings } from '@src/Settings'; +import { Logger } from '@src/Logger'; +import { BeamDirection } from '@src/rendering/utils/BeamDirection'; + +/** + * A beat is a single block within a bar. A beat is a combination + * of several notes played at the same time. + */ +export class Beat { + private static _globalBeatId: number = 0; + + /** + * Gets or sets the unique id of this beat. + */ + public id: number = Beat._globalBeatId++; + + /** + * Gets or sets the zero-based index of this beat within the voice. + */ + public index: number = 0; + + /** + * Gets or sets the previous beat within the whole song. + */ + public previousBeat: Beat | null = null; + + /** + * Gets or sets the next beat within the whole song. + */ + public nextBeat: Beat | null = null; + + public get isLastOfVoice(): boolean { + return this.index === this.voice.beats.length - 1; + } + + /** + * Gets or sets the reference to the parent voice this beat belongs to. + */ + public voice!: Voice; + + /** + * Gets or sets the list of notes contained in this beat. + */ + public notes: Note[] = []; + + /** + * Gets the lookup where the notes per string are registered. + * If this staff contains string based notes this lookup allows fast access. + */ + public readonly noteStringLookup: Map = new Map(); + + /** + * Gets the lookup where the notes per value are registered. + * If this staff contains string based notes this lookup allows fast access. + */ + public readonly noteValueLookup: Map = new Map(); + + /** + * Gets or sets a value indicating whether this beat is considered empty. + */ + public isEmpty: boolean = false; + + /** + * Gets or sets which whammy bar style should be used for this bar. + */ + public whammyStyle: BendStyle = BendStyle.Default; + + /** + * Gets or sets the ottava applied to this beat. + */ + public ottava: Ottavia = Ottavia.Regular; + + /** + * Gets or sets the fermata applied to this beat. + */ + public fermata: Fermata | null = null; + + /** + * Gets a value indicating whether this beat starts a legato slur. + */ + public isLegatoOrigin: boolean = false; + + public get isLegatoDestination(): boolean { + return !!this.previousBeat && this.previousBeat.isLegatoOrigin; + } + + /** + * Gets or sets the note with the lowest pitch in this beat. Only visible notes are considered. + */ + public minNote: Note | null = null; + + /** + * Gets or sets the note with the highest pitch in this beat. Only visible notes are considered. + */ + public maxNote: Note | null = null; + + /** + * Gets or sets the note with the highest string number in this beat. Only visible notes are considered. + */ + public maxStringNote: Note | null = null; + + /** + * Gets or sets the note with the lowest string number in this beat. Only visible notes are considered. + */ + public minStringNote: Note | null = null; + + /** + * Gets or sets the duration of this beat. + */ + public duration: Duration = Duration.Quarter; + + public get isRest(): boolean { + return this.isEmpty || this.notes.length === 0; + } + + /** + * Gets or sets whether any note in this beat has a let-ring applied. + */ + public isLetRing: boolean = false; + + /** + * Gets or sets whether any note in this beat has a palm-mute paplied. + */ + public isPalmMute: boolean = false; + + /** + * Gets or sets a list of all automations on this beat. + */ + public automations: Automation[] = []; + + /** + * Gets or sets the number of dots applied to the duration of this beat. + */ + public dots: number = 0; + + /** + * Gets or sets a value indicating whether this beat is fade-in. + */ + public fadeIn: boolean = false; + + /** + * Gets or sets the lyrics shown on this beat. + */ + public lyrics: string[] | null = null; + + /** + * Gets or sets a value indicating whether the beat is played in rasgueado style. + */ + public hasRasgueado: boolean = false; + + /** + * Gets or sets a value indicating whether the notes on this beat are played with a pop-style (bass). + */ + public pop: boolean = false; + + /** + * Gets or sets a value indicating whether the notes on this beat are played with a slap-style (bass). + */ + public slap: boolean = false; + + /** + * Gets or sets a value indicating whether the notes on this beat are played with a tap-style (bass). + */ + public tap: boolean = false; + + /** + * Gets or sets the text annotation shown on this beat. + */ + public text: string | null = null; + + /** + * Gets or sets the brush type applied to the notes of this beat. + */ + public brushType: BrushType = BrushType.None; + + /** + * Gets or sets the duration of the brush between the notes in midi ticks. + */ + public brushDuration: number = 0; + + /** + * Gets or sets the tuplet denominator. + */ + public tupletDenominator: number = -1; + + /** + * Gets or sets the tuplet numerator. + */ + public tupletNumerator: number = -1; + + public get hasTuplet(): boolean { + return ( + !(this.tupletDenominator === -1 && this.tupletNumerator === -1) && + !(this.tupletDenominator === 1 && this.tupletNumerator === 1) + ); + } + + public tupletGroup: TupletGroup | null = null; + + /** + * Gets or sets whether this beat continues a whammy effect. + */ + public isContinuedWhammy: boolean = false; + + /** + * Gets or sets the whammy bar style of this beat. + */ + public whammyBarType: WhammyType = WhammyType.None; + + /** + * Gets or sets the points defining the whammy bar usage. + */ + public whammyBarPoints: BendPoint[] = []; + + /** + * Gets or sets the highest point with for the highest whammy bar value. + */ + public maxWhammyPoint: BendPoint | null = null; + + /** + * Gets or sets the highest point with for the lowest whammy bar value. + */ + public minWhammyPoint: BendPoint | null = null; + + public get hasWhammyBar(): boolean { + return this.whammyBarType !== WhammyType.None; + } + + /** + * Gets or sets the vibrato effect used on this beat. + */ + public vibrato: VibratoType = VibratoType.None; + + /** + * Gets or sets the ID of the chord used on this beat. + */ + public chordId: string | null = null; + + public get hasChord(): boolean { + return !!this.chordId; + } + + public get chord(): Chord | null { + return this.chordId ? this.voice.bar.staff.chords.get(this.chordId)! : null; + } + + /** + * Gets or sets the grace style of this beat. + */ + public graceType: GraceType = GraceType.None; + + /** + * Gets or sets the pickstroke applied on this beat. + */ + public pickStroke: PickStroke = PickStroke.None; + + public get isTremolo(): boolean { + return !!this.tremoloSpeed; + } + + /** + * Gets or sets the speed of the tremolo effect. + */ + public tremoloSpeed: Duration | null = null; + + /** + * Gets or sets whether a crescendo/decrescendo is applied on this beat. + */ + public crescendo: CrescendoType = CrescendoType.None; + + /** + * The timeline position of the voice within the current bar as it is displayed. (unit: midi ticks) + * This might differ from the actual playback time due to special grace types. + */ + public displayStart: number = 0; + + /** + * The timeline position of the voice within the current bar as it is played. (unit: midi ticks) + * This might differ from the actual playback time due to special grace types. + */ + public playbackStart: number = 0; + + /** + * Gets or sets the duration that is used for the display of this beat. It defines the size/width of the beat in + * the music sheet. (unit: midi ticks). + */ + public displayDuration: number = 0; + + /** + * Gets or sets the duration that the note is played during the audio generation. + */ + public playbackDuration: number = 0; + + public get absoluteDisplayStart(): number { + return this.voice.bar.masterBar.start + this.displayStart; + } + + public get absolutePlaybackStart(): number { + return this.voice.bar.masterBar.start + this.playbackStart; + } + + /** + * Gets or sets the dynamics applied to this beat. + */ + public dynamics: DynamicValue = DynamicValue.F; + + /** + * Gets or sets a value indicating whether the beam direction should be inverted. + */ + public invertBeamDirection: boolean = false; + + /** + * Gets or sets the preferred beam direction as specified in the input source. + */ + public preferredBeamDirection: BeamDirection | null = null; + + public isEffectSlurOrigin: boolean = false; + + public get isEffectSlurDestination(): boolean { + return !!this.effectSlurOrigin; + } + + public effectSlurOrigin: Beat | null = null; + + public effectSlurDestination: Beat | null = null; + + public static copyTo(src: Beat, dst: Beat): void { + dst.id = src.id; + dst.index = src.index; + dst.isEmpty = src.isEmpty; + dst.duration = src.duration; + dst.dots = src.dots; + dst.fadeIn = src.fadeIn; + if (src.lyrics) { + dst.lyrics = new Array(src.lyrics.length); + for (let i: number = 0; i < src.lyrics.length; i++) { + dst.lyrics[i] = src.lyrics[i]; + } + } + dst.pop = src.pop; + dst.hasRasgueado = src.hasRasgueado; + dst.slap = src.slap; + dst.tap = src.tap; + dst.text = src.text; + dst.brushType = src.brushType; + dst.brushDuration = src.brushDuration; + dst.tupletDenominator = src.tupletDenominator; + dst.tupletNumerator = src.tupletNumerator; + dst.vibrato = src.vibrato; + dst.chordId = src.chordId; + dst.graceType = src.graceType; + dst.pickStroke = src.pickStroke; + dst.tremoloSpeed = src.tremoloSpeed; + dst.crescendo = src.crescendo; + dst.displayStart = src.displayStart; + dst.displayDuration = src.displayDuration; + dst.playbackStart = src.playbackStart; + dst.playbackDuration = src.playbackDuration; + dst.dynamics = src.dynamics; + dst.isLegatoOrigin = src.isLegatoOrigin; + dst.invertBeamDirection = src.invertBeamDirection; + dst.preferredBeamDirection = src.preferredBeamDirection; + dst.whammyBarType = src.whammyBarType; + dst.isContinuedWhammy = src.isContinuedWhammy; + dst.ottava = src.ottava; + dst.whammyStyle = src.whammyStyle; + } + + public clone(): Beat { + let beat: Beat = new Beat(); + let id: number = beat.id; + for (const p of this.whammyBarPoints) { + beat.addWhammyBarPoint(p.clone()); + } + for (const n of this.notes) { + beat.addNoteInternal(n.clone(), n.realValue); + } + Beat.copyTo(this, beat); + for (const a of this.automations) { + beat.automations.push(a.clone()); + } + beat.id = id; + return beat; + } + + public addWhammyBarPoint(point: BendPoint): void { + this.whammyBarPoints.push(point); + if (!this.maxWhammyPoint || point.value > this.maxWhammyPoint.value) { + this.maxWhammyPoint = point; + } + if (!this.minWhammyPoint || point.value < this.minWhammyPoint.value) { + this.minWhammyPoint = point; + } + if (this.whammyBarType === WhammyType.None) { + this.whammyBarType = WhammyType.Custom; + } + } + + public removeWhammyBarPoint(index: number): void { + // check index + if (index < 0 || index >= this.whammyBarPoints.length) { + return; + } + + // remove point + this.whammyBarPoints.splice(index, 1); + let point: BendPoint = this.whammyBarPoints[index]; + + // update maxWhammy point if required + if (point === this.maxWhammyPoint) { + this.maxWhammyPoint = null; + for (let currentPoint of this.whammyBarPoints) { + if (!this.maxWhammyPoint || currentPoint.value > this.maxWhammyPoint.value) { + this.maxWhammyPoint = currentPoint; + } + } + } + + if (point === this.minWhammyPoint) { + this.minWhammyPoint = null; + for (let currentPoint of this.whammyBarPoints) { + if (!this.minWhammyPoint || currentPoint.value < this.minWhammyPoint.value) { + this.minWhammyPoint = currentPoint; + } + } + } + } + + public addNote(note: Note): void { + this.addNoteInternal(note, -1); + } + + private addNoteInternal(note: Note, realValue: number = -1): void { + note.beat = this; + note.index = this.notes.length; + this.notes.push(note); + if (note.isStringed) { + this.noteStringLookup.set(note.string, note); + } + if (realValue === -1) { + realValue = note.realValue; + } + this.noteValueLookup.set(realValue, note); + } + + public removeNote(note: Note): void { + let index: number = this.notes.indexOf(note); + if (index >= 0) { + this.notes.splice(index, 1); + } + } + + public getAutomation(type: AutomationType): Automation | null { + for (let i: number = 0, j: number = this.automations.length; i < j; i++) { + let automation: Automation = this.automations[i]; + if (automation.type === type) { + return automation; + } + } + return null; + } + + public getNoteOnString(noteString: number): Note | null { + if (this.noteStringLookup.has(noteString)) { + return this.noteStringLookup.get(noteString)!; + } + return null; + } + + private calculateDuration(): number { + let ticks: number = MidiUtils.toTicks(this.duration); + if (this.dots === 2) { + ticks = MidiUtils.applyDot(ticks, true); + } else if (this.dots === 1) { + ticks = MidiUtils.applyDot(ticks, false); + } + if (this.tupletDenominator > 0 && this.tupletNumerator >= 0) { + ticks = MidiUtils.applyTuplet(ticks, this.tupletNumerator, this.tupletDenominator); + } + return ticks; + } + + public updateDurations(): void { + let ticks: number = this.calculateDuration(); + this.playbackDuration = ticks; + this.displayDuration = ticks; + switch (this.graceType) { + case GraceType.BeforeBeat: + case GraceType.OnBeat: + switch (this.duration) { + case Duration.Sixteenth: + this.playbackDuration = MidiUtils.toTicks(Duration.SixtyFourth); + break; + case Duration.ThirtySecond: + this.playbackDuration = MidiUtils.toTicks(Duration.OneHundredTwentyEighth); + break; + default: + this.playbackDuration = MidiUtils.toTicks(Duration.ThirtySecond); + break; + } + break; + case GraceType.BendGrace: + this.playbackDuration /= 2; + break; + default: + let previous: Beat | null = this.previousBeat; + if (previous && previous.graceType === GraceType.BendGrace) { + this.playbackDuration = previous.playbackDuration; + } else { + while (previous && previous.graceType === GraceType.OnBeat) { + // if the previous beat is a on-beat grace it steals the duration from this beat + this.playbackDuration -= previous.playbackDuration; + previous = previous.previousBeat; + } + } + break; + } + } + + public finishTuplet(): void { + let previousBeat: Beat | null = this.previousBeat; + let currentTupletGroup: TupletGroup | null = previousBeat ? previousBeat.tupletGroup : null; + if (this.hasTuplet || (this.graceType !== GraceType.None && currentTupletGroup)) { + if (!previousBeat || !currentTupletGroup || !currentTupletGroup.check(this)) { + currentTupletGroup = new TupletGroup(this.voice); + currentTupletGroup.check(this); + } + this.tupletGroup = currentTupletGroup; + } + } + + public finish(settings: Settings): void { + let displayMode: NotationMode = !settings ? NotationMode.GuitarPro : settings.notation.notationMode; + let isGradual: boolean = this.text === 'grad' || this.text === 'grad.'; + if (isGradual && displayMode === NotationMode.SongBook) { + this.text = ''; + } + let needCopyBeatForBend: boolean = false; + this.minNote = null; + this.maxNote = null; + this.minStringNote = null; + this.maxStringNote = null; + let visibleNotes: number = 0; + let isEffectSlurBeat: boolean = false; + for (let i: number = 0, j: number = this.notes.length; i < j; i++) { + let note: Note = this.notes[i]; + note.finish(settings); + if (note.isLetRing) { + this.isLetRing = true; + } + if (note.isPalmMute) { + this.isPalmMute = true; + } + if (displayMode === NotationMode.SongBook && note.hasBend && this.graceType !== GraceType.BendGrace) { + if (!note.isTieOrigin) { + switch (note.bendType) { + case BendType.Bend: + case BendType.PrebendRelease: + case BendType.PrebendBend: + needCopyBeatForBend = true; + break; + } + } + if (isGradual || note.bendStyle === BendStyle.Gradual) { + isGradual = true; + note.bendStyle = BendStyle.Gradual; + needCopyBeatForBend = false; + } else { + note.bendStyle = BendStyle.Fast; + } + } + if (note.isVisible) { + visibleNotes++; + if (!this.minNote || note.realValue < this.minNote.realValue) { + this.minNote = note; + } + if (!this.maxNote || note.realValue > this.maxNote.realValue) { + this.maxNote = note; + } + if (!this.minStringNote || note.string < this.minStringNote.string) { + this.minStringNote = note; + } + if (!this.maxStringNote || note.string > this.maxStringNote.string) { + this.maxStringNote = note; + } + if (note.hasEffectSlur) { + isEffectSlurBeat = true; + } + } + } + if (isEffectSlurBeat) { + if (this.effectSlurOrigin) { + this.effectSlurOrigin.effectSlurDestination = this.nextBeat; + if (this.effectSlurOrigin.effectSlurDestination) { + this.effectSlurOrigin.effectSlurDestination.effectSlurOrigin = this.effectSlurOrigin; + } + this.effectSlurOrigin = null; + } else { + this.isEffectSlurOrigin = true; + this.effectSlurDestination = this.nextBeat; + if (this.effectSlurDestination) { + this.effectSlurDestination.effectSlurOrigin = this; + } + } + } + if (this.notes.length > 0 && visibleNotes === 0) { + this.isEmpty = true; + } + // we need to clean al letring/palmmute flags for rests + // in case the effect is not continued on this beat + if (!this.isRest && (!this.isLetRing || !this.isPalmMute)) { + let currentBeat: Beat | null = this.previousBeat; + while (currentBeat && currentBeat.isRest) { + if (!this.isLetRing) { + currentBeat.isLetRing = false; + } + if (!this.isPalmMute) { + currentBeat.isPalmMute = false; + } + currentBeat = currentBeat.previousBeat; + } + } else if ( + this.isRest && + this.previousBeat && + settings && + settings.notation.notationMode === NotationMode.GuitarPro + ) { + if (this.previousBeat.isLetRing) { + this.isLetRing = true; + } + if (this.previousBeat.isPalmMute) { + this.isPalmMute = true; + } + } + // try to detect what kind of bend was used and cleans unneeded points if required + // Guitar Pro 6 and above (gpif.xml) uses exactly 4 points to define all whammys + if (this.whammyBarPoints.length > 0 && this.whammyBarType === WhammyType.Custom) { + if (displayMode === NotationMode.SongBook) { + this.whammyStyle = isGradual ? BendStyle.Gradual : BendStyle.Fast; + } + let isContinuedWhammy: boolean = !!this.previousBeat && this.previousBeat.hasWhammyBar; + this.isContinuedWhammy = isContinuedWhammy; + if (this.whammyBarPoints.length === 4) { + let origin: BendPoint = this.whammyBarPoints[0]; + let middle1: BendPoint = this.whammyBarPoints[1]; + let middle2: BendPoint = this.whammyBarPoints[2]; + let destination: BendPoint = this.whammyBarPoints[3]; + // the middle points are used for holds, anything else is a new feature we do not support yet + if (middle1.value === middle2.value) { + // constant decrease or increase + if ( + (origin.value < middle1.value && middle1.value < destination.value) || + (origin.value > middle1.value && middle1.value > destination.value) + ) { + if (origin.value !== 0 && !isContinuedWhammy) { + this.whammyBarType = WhammyType.PrediveDive; + } else { + this.whammyBarType = WhammyType.Dive; + } + this.whammyBarPoints.splice(2, 1); + this.whammyBarPoints.splice(1, 1); + } else if ( + (origin.value > middle1.value && middle1.value < destination.value) || + (origin.value < middle1.value && middle1.value > destination.value) + ) { + this.whammyBarType = WhammyType.Dip; + if (middle1.offset === middle2.offset || displayMode === NotationMode.SongBook) { + this.whammyBarPoints.splice(2, 1); + } + } else if (origin.value === middle1.value && middle1.value === destination.value) { + if (origin.value !== 0 && !isContinuedWhammy) { + this.whammyBarType = WhammyType.Predive; + } else { + this.whammyBarType = WhammyType.Hold; + } + this.whammyBarPoints.splice(2, 1); + this.whammyBarPoints.splice(1, 1); + } else { + Logger.warning('Model', 'Unsupported whammy type detected, fallback to custom', null); + } + } else { + Logger.warning('Model', 'Unsupported whammy type detected, fallback to custom', null); + } + } + } + this.updateDurations(); + if (needCopyBeatForBend) { + // if this beat is a simple bend convert it to a grace beat + // and generate a placeholder beat with tied notes + let cloneBeat: Beat = this.clone(); + cloneBeat.id = Beat._globalBeatId++; + cloneBeat.pickStroke = PickStroke.None; + for (let i: number = 0, j: number = cloneBeat.notes.length; i < j; i++) { + let cloneNote: Note = cloneBeat.notes[i]; + let note: Note = this.notes[i]; + + // remove bend on cloned note + cloneNote.bendType = BendType.None; + cloneNote.maxBendPoint = null; + cloneNote.bendPoints = []; + cloneNote.bendStyle = BendStyle.Default; + cloneNote.id = Note.GlobalNoteId++; + + // fix ties + if(note.isTieOrigin) { + cloneNote.tieDestination = note.tieDestination!; + note.tieDestination!.tieOrigin = cloneNote; + } + if(note.isTieDestination) { + cloneNote.tieOrigin = note.tieOrigin; + note.tieOrigin!.tieDestination = cloneNote; + } + + // if the note has a bend which is continued on the next note + // we need to convert this note into a hold bend + if (note.hasBend && note.isTieOrigin) { + let tieDestination: Note | null = Note.findTieOrigin(note); + if (tieDestination && tieDestination.hasBend) { + cloneNote.bendType = BendType.Hold; + let lastPoint: BendPoint = note.bendPoints[note.bendPoints.length - 1]; + cloneNote.addBendPoint(new BendPoint(0, lastPoint.value)); + cloneNote.addBendPoint(new BendPoint(BendPoint.MaxPosition, lastPoint.value)); + } + } + // mark as tied note + cloneNote.isTieDestination = true; + } + this.graceType = GraceType.BendGrace; + this.updateDurations(); + this.voice.insertBeat(this, cloneBeat); + } + this.fermata = this.voice.bar.masterBar.getFermata(this); + } + + /** + * Checks whether the current beat is timewise before the given beat. + * @param beat + * @returns + */ + public isBefore(beat: Beat): boolean { + return ( + this.voice.bar.index < beat.voice.bar.index || + (beat.voice.bar.index === this.voice.bar.index && this.index < beat.index) + ); + } + + /** + * Checks whether the current beat is timewise after the given beat. + * @param beat + * @returns + */ + public isAfter(beat: Beat): boolean { + return ( + this.voice.bar.index > beat.voice.bar.index || + (beat.voice.bar.index === this.voice.bar.index && this.index > beat.index) + ); + } + + public hasNoteOnString(noteString: number): boolean { + return this.noteStringLookup.has(noteString); + } + + public getNoteWithRealValue(noteRealValue: number): Note | null { + if (this.noteValueLookup.has(noteRealValue)) { + return this.noteValueLookup.get(noteRealValue)!; + } + return null; + } + + public chain() { + for(const n of this.notes) { + n.chain(); + } + } +} diff --git a/src/model/BendPoint.ts b/src/model/BendPoint.ts new file mode 100644 index 000000000..e76b27a0b --- /dev/null +++ b/src/model/BendPoint.ts @@ -0,0 +1,39 @@ +/** + * A single point of a bending graph. Used to + * describe WhammyBar and String Bending effects. + */ +export class BendPoint { + public static readonly MaxPosition: number = 60; + public static readonly MaxValue: number = 12; + + /** + * Gets or sets offset of the point relative to the note duration (0-60) + */ + public offset: number; + + /** + * Gets or sets the 1/4 note value offsets for the bend. + */ + public value: number; + + /** + * Initializes a new instance of the {@link BendPoint} class. + * @param offset The offset. + * @param value The value. + */ + public constructor(offset: number = 0, value: number = 0) { + this.offset = offset; + this.value = value; + } + + public static copyTo(src: BendPoint, dst: BendPoint): void { + dst.offset = src.offset; + dst.value = src.value; + } + + public clone(): BendPoint { + let point: BendPoint = new BendPoint(0, 0); + BendPoint.copyTo(this, point); + return point; + } +} diff --git a/src/model/BendStyle.ts b/src/model/BendStyle.ts new file mode 100644 index 000000000..248cca79a --- /dev/null +++ b/src/model/BendStyle.ts @@ -0,0 +1,17 @@ +/** + * Lists the different bend styles + */ +export enum BendStyle { + /** + * The bends are as described by the bend points + */ + Default, + /** + * The bends are gradual over the beat duration. + */ + Gradual, + /** + * The bends are done fast before the next note. + */ + Fast, +} diff --git a/src/model/BendType.ts b/src/model/BendType.ts new file mode 100644 index 000000000..840bb9fa8 --- /dev/null +++ b/src/model/BendType.ts @@ -0,0 +1,45 @@ +/** + * Lists all types of bends + */ +export enum BendType { + /** + * No bend at all + */ + None, + /** + * Individual points define the bends in a flexible manner. + * This system was mainly used in Guitar Pro 3-5 + */ + Custom, + /** + * Simple Bend from an unbended string to a higher note. + */ + Bend, + /** + * Release of a bend that was started on an earlier note. + */ + Release, + /** + * A bend that starts from an unbended string, + * and also releases the bend after some time. + */ + BendRelease, + /** + * Holds a bend that was started on an earlier note + */ + Hold, + /** + * A bend that is already started before the note is played then it is held until the end. + */ + Prebend, + /** + * A bend that is already started before the note is played and + * bends even further, then it is held until the end. + */ + PrebendBend, + /** + * A bend that is already started before the note is played and + * then releases the bend to a lower note where it is held until the end. + */ + PrebendRelease +} diff --git a/src/model/BrushType.ts b/src/model/BrushType.ts new file mode 100644 index 000000000..6af2356a7 --- /dev/null +++ b/src/model/BrushType.ts @@ -0,0 +1,25 @@ +/** + * Lists all types of how to brush multiple notes on a beat. + */ +export enum BrushType { + /** + * No brush. + */ + None, + /** + * Normal brush up. + */ + BrushUp, + /** + * Normal brush down. + */ + BrushDown, + /** + * Arpeggio up. + */ + ArpeggioUp, + /** + * Arpeggio down. + */ + ArpeggioDown +} diff --git a/src/model/Chord.ts b/src/model/Chord.ts new file mode 100644 index 000000000..775cc6e6e --- /dev/null +++ b/src/model/Chord.ts @@ -0,0 +1,58 @@ +import { Staff } from '@src/model/Staff'; + +/** + * A chord definition. + */ +export class Chord { + /** + * Gets or sets the name of the chord + */ + public name: string = ''; + + /** + * Indicates the first fret of the chord diagram. + */ + public firstFret: number = 1; + + /** + * Gets or sets the frets played on the individual strings for this chord. + * - The order in this list goes from the highest string to the lowest string. + * - -1 indicates that the string is not played. + */ + public strings: number[] = []; + + /** + * Gets or sets a list of frets where the finger should hold a barre + */ + public barreFrets: number[] = []; + + /** + * Gets or sets the staff the chord belongs to. + */ + public staff!: Staff; + + /** + * Gets or sets whether the chord name is shown above the chord diagram. + */ + public showName: boolean = true; + + /** + * Gets or sets whether the chord diagram is shown. + */ + public showDiagram: boolean = true; + + /** + * Gets or sets whether the fingering is shown below the chord diagram. + */ + public showFingering: boolean = true; + + public static copyTo(src: Chord, dst: Chord): void { + dst.firstFret = src.firstFret; + dst.name = src.name; + dst.strings = src.strings.slice(0); + dst.barreFrets = src.barreFrets.slice(0); + dst.showName = src.showName; + dst.showDiagram = src.showDiagram; + dst.showFingering = src.showFingering; + } +} diff --git a/src/model/Clef.ts b/src/model/Clef.ts new file mode 100644 index 000000000..3540b10fb --- /dev/null +++ b/src/model/Clef.ts @@ -0,0 +1,25 @@ +/** + * This public enumeration lists all supported Clefs. + */ +export enum Clef { + /** + * Neutral clef. + */ + Neutral, + /** + * C3 clef + */ + C3, + /** + * C4 clef + */ + C4, + /** + * F4 clef + */ + F4, + /** + * G2 clef + */ + G2 +} diff --git a/src/model/Color.ts b/src/model/Color.ts new file mode 100644 index 000000000..2e41f7c7a --- /dev/null +++ b/src/model/Color.ts @@ -0,0 +1,154 @@ +import { FormatError } from '@src/FormatError'; +import { ModelUtils } from './ModelUtils'; + +/** + * @json_immutable + */ +export class Color { + public static readonly BlackRgb: string = '#000000'; + + /** + * Initializes a new instance of the {@link Color} class. + * @param r The red component. + * @param g The green component. + * @param b The blue component. + * @param a The alpha component. + */ + public constructor(r: number, g: number, b: number, a: number = 0xff) { + this.raw = 0; + this.raw = ((a & 0xff) << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff); + this.updateRgba(); + } + + public updateRgba(): void { + if (this.a === 0xff) { + this.rgba = + '#' + + ModelUtils.toHexString(this.r, 2) + + ModelUtils.toHexString(this.g, 2) + + ModelUtils.toHexString(this.b, 2); + } else { + this.rgba = `rgba(${this.r},${this.g},${this.b},${this.a / 255.0})`; + } + } + + /** + * Gets or sets the raw RGBA value. + */ + public raw: number = 0; + + public get a(): number { + return (this.raw >> 24) & 0xff; + } + + public get r(): number { + return (this.raw >> 16) & 0xff; + } + + public get g(): number { + return (this.raw >> 8) & 0xff; + } + + public get b(): number { + return this.raw & 0xff; + } + + /** + * Gets the RGBA hex string to use in CSS areas. + */ + public rgba!: string; + + public static random(opacity: number = 100): Color { + return new Color((Math.random() * 255) | 0, (Math.random() * 255) | 0, (Math.random() * 255) | 0, opacity); + } + + /** + * @target web + */ + public static fromJson(json: unknown): Color | null { + if (!json) { + return null; + } + if (json instanceof Color) { + return json; + } + + switch (typeof json) { + case 'number': + let c: Color = new Color(0, 0, 0, 0); + c.raw = json | 0; + c.updateRgba(); + return c; + + case 'string': + if (json.startsWith('#')) { + if (json.length === 4) { + // #RGB + return new Color( + parseInt(json.substring(1, 1), 16) * 17, + parseInt(json.substring(2, 1), 16) * 17, + parseInt(json.substring(3, 1), 16) * 17 + ); + } + + if (json.length === 5) { + // #RGBA + return new Color( + parseInt(json.substring(1, 1), 16) * 17, + parseInt(json.substring(2, 1), 16) * 17, + parseInt(json.substring(3, 1), 16) * 17, + parseInt(json.substring(4, 1), 16) * 17 + ); + } + + if (json.length === 7) { + // #RRGGBB + return new Color( + parseInt(json.substring(1, 2), 16), + parseInt(json.substring(3, 2), 16), + parseInt(json.substring(5, 2), 16) + ); + } + + if (json.length === 9) { + // #RRGGBBAA + return new Color( + parseInt(json.substring(1, 2), 16), + parseInt(json.substring(3, 2), 16), + parseInt(json.substring(5, 2), 16), + parseInt(json.substring(7, 2), 16) + ); + } + } else if (json.startsWith('rgba') || json.startsWith('rgb')) { + const start = json.indexOf('('); + const end = json.lastIndexOf(')'); + if (start === -1 || end === -1) { + throw new FormatError('No values specified for rgb/rgba function'); + } + + const numbers = json.substring(start + 1, end - start - 1).split(','); + if (numbers.length === 3) { + return new Color(parseInt(numbers[0]), parseInt(numbers[1]), parseInt(numbers[2])); + } + + if (numbers.length === 4) { + return new Color( + parseInt(numbers[0]), + parseInt(numbers[1]), + parseInt(numbers[2]), + parseFloat(numbers[3]) * 255 + ); + } + } + break; + } + throw new FormatError('Unsupported format for color'); + } + + /** + * @target web + */ + public static toJson(obj: Color): unknown { + return obj.raw; + } +} diff --git a/src/model/CrescendoType.ts b/src/model/CrescendoType.ts new file mode 100644 index 000000000..d06775d6e --- /dev/null +++ b/src/model/CrescendoType.ts @@ -0,0 +1,17 @@ +/** + * Lists all Crescendo and Decrescendo types. + */ +export enum CrescendoType { + /** + * No crescendo applied. + */ + None, + /** + * Normal crescendo applied. + */ + Crescendo, + /** + * Normal decrescendo applied. + */ + Decrescendo +} diff --git a/src/model/Duration.ts b/src/model/Duration.ts new file mode 100644 index 000000000..0a27afee3 --- /dev/null +++ b/src/model/Duration.ts @@ -0,0 +1,49 @@ +/** + * Lists all durations of a beat. + */ +export enum Duration { + /** + * A quadruple whole note duration + */ + QuadrupleWhole = -4, + /** + * A double whole note duration + */ + DoubleWhole = -2, + /** + * A whole note duration + */ + Whole = 1, + /** + * A 1/2 note duration + */ + Half = 2, + /** + * A 1/4 note duration + */ + Quarter = 4, + /** + * A 1/8 note duration + */ + Eighth = 8, + /** + * A 1/16 note duration + */ + Sixteenth = 16, + /** + * A 1/32 note duration + */ + ThirtySecond = 32, + /** + * A 1/64 note duration + */ + SixtyFourth = 64, + /** + * A 1/128 note duration + */ + OneHundredTwentyEighth = 128, + /** + * A 1/256 note duration + */ + TwoHundredFiftySixth = 256 +} diff --git a/src/model/DynamicValue.ts b/src/model/DynamicValue.ts new file mode 100644 index 000000000..f3a88f610 --- /dev/null +++ b/src/model/DynamicValue.ts @@ -0,0 +1,37 @@ +/** + * Lists all dynamics. + */ +export enum DynamicValue { + /** + * pianississimo (very very soft) + */ + PPP, + /** + * pianissimo (very soft) + */ + PP, + /** + * piano (soft) + */ + P, + /** + * mezzo-piano (half soft) + */ + MP, + /** + * mezzo-forte (half loud) + */ + MF, + /** + * forte (loud) + */ + F, + /** + * fortissimo (very loud) + */ + FF, + /** + * fortississimo (very very loud) + */ + FFF +} diff --git a/src/model/Fermata.ts b/src/model/Fermata.ts new file mode 100644 index 000000000..c95d17307 --- /dev/null +++ b/src/model/Fermata.ts @@ -0,0 +1,37 @@ +/** + * Lists all types of fermatas + */ +export enum FermataType { + /** + * A short fermata (triangle symbol) + */ + Short, + /** + * A medium fermata (round symbol) + */ + Medium, + /** + * A long fermata (rectangular symbol) + */ + Long +} + +/** + * Represents a fermata. + */ +export class Fermata { + /** + * Gets or sets the type of fermata. + */ + public type: FermataType = FermataType.Short; + + /** + * Gets or sets the actual lenght of the fermata. + */ + public length: number = 0; + + public static copyTo(src: Fermata, dst: Fermata): void { + dst.type = src.type; + dst.length = src.length; + } +} diff --git a/src/model/Fingers.ts b/src/model/Fingers.ts new file mode 100644 index 000000000..3ac62b7df --- /dev/null +++ b/src/model/Fingers.ts @@ -0,0 +1,33 @@ +/** + * Lists all fingers. + */ +export enum Fingers { + /** + * Unknown type (not documented) + */ + Unknown = -2, + /** + * No finger, dead note + */ + NoOrDead = -1, + /** + * The thumb + */ + Thumb = 0, + /** + * The index finger + */ + IndexFinger = 1, + /** + * The middle finger + */ + MiddleFinger = 2, + /** + * The annular finger + */ + AnnularFinger = 3, + /** + * The little finger + */ + LittleFinger = 4 +} diff --git a/src/model/Font.ts b/src/model/Font.ts new file mode 100644 index 000000000..d6ca14cca --- /dev/null +++ b/src/model/Font.ts @@ -0,0 +1,189 @@ +import { Environment } from '@src/Environment'; + +/** + * Lists all flags for font styles. + */ +export enum FontStyle { + /** + * No flags. + */ + Plain, + /** + * Font is bold + */ Bold, + /** + * Font is italic. + */ Italic +} + +/** + * @json_immutable + */ +export class Font { + private _css: string; + private _cssScale: number = 0.0; + + /** + * Gets or sets the font family name. + */ + public family: string; + + /** + * Gets or sets the font size in pixels. + */ + public size: number; + + /** + * Gets or sets the font style. + */ + public style: FontStyle; + + public get isBold(): boolean { + return (this.style & FontStyle.Bold) !== 0; + } + + public get isItalic(): boolean { + return (this.style & FontStyle.Italic) !== 0; + } + + /** + * Initializes a new instance of the {@link Font} class. + * @param family The family. + * @param size The size. + * @param style The style. + */ + public constructor(family: string, size: number, style: FontStyle = FontStyle.Plain) { + this.family = family; + this.size = size; + this.style = style; + this._css = this.toCssString(1); + } + + public clone(): Font { + return new Font(this.family, this.size, this.style); + } + + public toCssString(scale: number): string { + if (!this._css || !(Math.abs(scale - this._cssScale) < 0.01)) { + let buf: string = ''; + if (this.isBold) { + buf += 'bold '; + } + if (this.isItalic) { + buf += 'italic '; + } + buf += this.size * scale; + buf += 'px '; + buf += "'"; + buf += this.family; + buf += "'"; + this._css = buf; + this._cssScale = scale; + } + return this._css; + } + + /** + * @target web + */ + public static fromJson(value: unknown): Font | null { + if (!value) { + return null; + } + + if (value instanceof Font) { + return value; + } + + if (typeof value === 'object' && (value as any).family) { + return new Font((value as any).family, (value as any).size, (value as any).style); + } + + if (typeof value === 'string' && !Environment.isRunningInWorker) { + let el: HTMLElement = document.createElement('span'); + el.setAttribute('style', 'font: ' + value); + let style: CSSStyleDeclaration = el.style; + if (!style.fontFamily) { + style.fontFamily = 'sans-serif'; + } + + let family: string = style.fontFamily; + if ((family.startsWith("'") && family.endsWith("'")) || (family.startsWith('"') && family.endsWith('"'))) { + family = family.substr(1, family.length - 2); + } + + let fontSizeString: string = style.fontSize.toLowerCase(); + let fontSize: number = 0; + // as per https://websemantics.uk/articles/font-size-conversion/ + switch (fontSizeString) { + case 'xx-small': + fontSize = 7; + break; + case 'x-small': + fontSize = 10; + break; + case 'small': + case 'smaller': + fontSize = 13; + break; + case 'medium': + fontSize = 16; + break; + case 'large': + case 'larger': + fontSize = 18; + break; + case 'x-large': + fontSize = 24; + break; + case 'xx-large': + fontSize = 32; + break; + default: + try { + if (fontSizeString.endsWith('em')) { + fontSize = parseFloat(fontSizeString.substr(0, fontSizeString.length - 2)) * 16; + } else if (fontSizeString.endsWith('pt')) { + fontSize = (parseFloat(fontSizeString.substr(0, fontSizeString.length - 2)) * 16.0) / 12.0; + } else if (fontSizeString.endsWith('px')) { + fontSize = parseFloat(fontSizeString.substr(0, fontSizeString.length - 2)); + } else { + fontSize = 12; + } + } catch (e) { + fontSize = 12; + } + break; + } + + let fontStyle: FontStyle = FontStyle.Plain; + if (style.fontStyle === 'italic') { + fontStyle |= FontStyle.Italic; + } + let fontWeightString: string = style.fontWeight.toLowerCase(); + switch (fontWeightString) { + case 'normal': + case 'lighter': + break; + default: + fontStyle |= FontStyle.Bold; + break; + } + + return new Font(family, fontSize, fontStyle); + } + + return null; + } + + /** + * @target web + */ + public static toJson(font: Font): unknown { + return { + family: font.family, + size: font.size, + style: font.style + }; + } +} diff --git a/src/model/GraceType.ts b/src/model/GraceType.ts new file mode 100644 index 000000000..cdc8bddb1 --- /dev/null +++ b/src/model/GraceType.ts @@ -0,0 +1,21 @@ +/** + * Lists all types of grace notes + */ +export enum GraceType { + /** + * No grace, normal beat. + */ + None, + /** + * The beat contains on-beat grace notes. + */ + OnBeat, + /** + * The beat contains before-beat grace notes. + */ + BeforeBeat, + /** + * The beat contains very special bend-grace notes used in SongBook style displays. + */ + BendGrace +} diff --git a/src/model/HarmonicType.ts b/src/model/HarmonicType.ts new file mode 100644 index 000000000..13da56f9b --- /dev/null +++ b/src/model/HarmonicType.ts @@ -0,0 +1,33 @@ +/** + * Lists all harmonic types. + */ +export enum HarmonicType { + /** + * No harmonics. + */ + None, + /** + * Natural harmonic + */ + Natural, + /** + * Artificial harmonic + */ + Artificial, + /** + * Pinch harmonics + */ + Pinch, + /** + * Tap harmonics + */ + Tap, + /** + * Semi harmonics + */ + Semi, + /** + * Feedback harmonics + */ + Feedback +} diff --git a/src/model/JsonConverter.ts b/src/model/JsonConverter.ts new file mode 100644 index 000000000..82c76df37 --- /dev/null +++ b/src/model/JsonConverter.ts @@ -0,0 +1,493 @@ +import { MetaDataEvent } from '@src/midi/MetaDataEvent'; +import { MetaNumberEvent } from '@src/midi/MetaNumberEvent'; +import { MidiEvent } from '@src/midi/MidiEvent'; +import { SystemExclusiveEvent } from '@src/midi/SystemExclusiveEvent'; +import { MidiFile } from '@src/midi/MidiFile'; +import { Automation } from '@src/model/Automation'; +import { Bar } from '@src/model/Bar'; +import { Beat } from '@src/model/Beat'; +import { BendPoint } from '@src/model/BendPoint'; +import { Chord } from '@src/model/Chord'; +import { Fermata } from '@src/model/Fermata'; +import { MasterBar } from '@src/model/MasterBar'; +import { Note } from '@src/model/Note'; +import { PlaybackInformation } from '@src/model/PlaybackInformation'; +import { RenderStylesheet } from '@src/model/RenderStylesheet'; +import { Score } from '@src/model/Score'; +import { Section } from '@src/model/Section'; +import { Staff } from '@src/model/Staff'; +import { Track } from '@src/model/Track'; +import { Voice } from '@src/model/Voice'; +import { Settings } from '@src/Settings'; + +interface SerializedNote { + tieOriginId?: number; + tieDestinationId?: number; + slurOriginId?: number; + slurDestinationId?: number; + hammerPullOriginId?: number; + hammerPullDestinationId?: number; +} + +/** + * This class can convert a full {@link Score} instance to a simple JavaScript object and back for further + * JSON serialization. + * @target web + */ +export class JsonConverter { + /** + * Converts the given score into a JSON encoded string. + * @param score The score to serialize. + * @returns A JSON encoded string that can be used togehter with for conversion. + */ + public static scoreToJson(score: Score): string { + let obj: unknown = JsonConverter.scoreToJsObject(score); + return JSON.stringify(obj, (_, v) => { + // patch arraybuffer to serialize as array + if (ArrayBuffer.isView(v)) { + return Array.apply([], [v]); + } + return v; + }); + } + + /** + * Converts the score into a JavaScript object without circular dependencies. + * @param score The score object to serialize + * @returns A serialized score object without ciruclar dependencies that can be used for further serializations. + */ + public static scoreToJsObject(score: Score): unknown { + let score2: Score = {} as any; + Score.copyTo(score, score2); + score2.masterBars = []; + score2.tracks = []; + + score2.stylesheet = {} as any; + RenderStylesheet.copyTo(score.stylesheet, score2.stylesheet); + + JsonConverter.masterBarsToJsObject(score, score2); + JsonConverter.tracksToJsObject(score, score2); + + return score2; + } + + private static tracksToJsObject(score: Score, score2: Score) { + for (let t: number = 0; t < score.tracks.length; t++) { + let track: Track = score.tracks[t]; + let track2: Track = {} as any; + track2.color = {} as any; + Track.copyTo(track, track2); + + track2.playbackInfo = {} as any; + PlaybackInformation.copyTo(track.playbackInfo, track2.playbackInfo); + + JsonConverter.stavesToJsObject(track, track2); + score2.tracks.push(track2); + } + } + + private static stavesToJsObject(track: Track, track2: Track) { + track2.staves = []; + for (let s: number = 0; s < track.staves.length; s++) { + let staff: Staff = track.staves[s]; + let staff2: Staff = {} as any; + Staff.copyTo(staff, staff2); + staff2.chords = new Map(); + staff.chords.forEach((chord, chordId) => { + let chord2: Chord = {} as any; + Chord.copyTo(chord, chord2); + staff2.chords.set(chordId, chord2); + }); + + JsonConverter.barsToJsObject(staff, staff2); + track2.staves.push(staff2); + } + } + + private static barsToJsObject(staff: Staff, staff2: Staff) { + staff2.bars = []; + for (let b: number = 0; b < staff.bars.length; b++) { + let bar: Bar = staff.bars[b]; + let bar2: Bar = {} as any; + Bar.copyTo(bar, bar2); + + JsonConverter.voicesToJsObject(bar, bar2); + + staff2.bars.push(bar2); + } + } + + private static voicesToJsObject(bar: Bar, bar2: Bar) { + bar2.voices = []; + for (let v: number = 0; v < bar.voices.length; v++) { + let voice: Voice = bar.voices[v]; + let voice2: Voice = {} as any; + Voice.copyTo(voice, voice2); + + JsonConverter.beatsToJsObject(voice, voice2); + + bar2.voices.push(voice2); + } + } + + private static beatsToJsObject(voice: Voice, voice2: Voice) { + voice2.beats = []; + for (let bb: number = 0; bb < voice.beats.length; bb++) { + let beat: Beat = voice.beats[bb]; + let dynamicBeat2: any = {} as any; + let beat2: Beat = dynamicBeat2; + Beat.copyTo(beat, beat2); + + beat2.automations = []; + for (let a: number = 0; a < beat.automations.length; a++) { + let automation: Automation = {} as any; + Automation.copyTo(beat.automations[a], automation); + beat2.automations.push(automation); + } + + beat2.whammyBarPoints = []; + for (let i: number = 0; i < beat.whammyBarPoints.length; i++) { + let point: BendPoint = {} as any; + BendPoint.copyTo(beat.whammyBarPoints[i], point); + beat2.whammyBarPoints.push(point); + } + + JsonConverter.notesToJsObject(beat, beat2); + + voice2.beats.push(beat2); + } + } + + private static notesToJsObject(beat: Beat, beat2: Beat) { + beat2.notes = []; + for (let n: number = 0; n < beat.notes.length; n++) { + let note: Note = beat.notes[n]; + let dynamicNote2: any = {} as any; + let note2: Note = dynamicNote2; + Note.copyTo(note, note2); + + if (note.isTieDestination) { + dynamicNote2.tieOriginId = note.tieOrigin!.id; + } + + if (note.isTieOrigin) { + dynamicNote2.tieDestinationId = note.tieDestination!.id; + } + + if (note.isSlurDestination) { + dynamicNote2.slurOriginId = note.slurOrigin!.id; + } + + if (note.isSlurOrigin) { + dynamicNote2.slurDestinationId = note.slurDestination!.id; + } + + if (note.isHammerPullDestination) { + dynamicNote2.hammerPullOriginId = note.hammerPullOrigin!.id; + } + + if (note.isHammerPullOrigin) { + dynamicNote2.hammerPullDestinationId = note.hammerPullDestination!.id; + } + + note2.bendPoints = []; + for (let i: number = 0; i < note.bendPoints.length; i++) { + let point: BendPoint = {} as any; + BendPoint.copyTo(note.bendPoints[i], point); + note2.bendPoints.push(point); + } + beat2.notes.push(note2); + } + } + + private static masterBarsToJsObject(score: Score, score2: Score) { + for (let i: number = 0; i < score.masterBars.length; i++) { + let masterBar: MasterBar = score.masterBars[i]; + let masterBar2: MasterBar = {} as any; + MasterBar.copyTo(masterBar, masterBar2); + if (masterBar.tempoAutomation) { + masterBar2.tempoAutomation = {} as any; + Automation.copyTo(masterBar.tempoAutomation, masterBar2.tempoAutomation!); + } + + if (masterBar.section) { + masterBar2.section = {} as any; + Section.copyTo(masterBar.section, masterBar2.section!); + } + + masterBar2.fermata = new Map(); + masterBar.fermata.forEach((fermata, fermataId) => { + let fermata2: any = {} as any; + masterBar2.fermata.set(fermataId, fermata2); + Fermata.copyTo(fermata, fermata2); + }); + + score2.masterBars.push(masterBar2); + } + } + + /** + * Converts the given JSON string back to a {@link Score} object. + * @param json The JSON string that was created via {@link Score} + * @param settings The settings to use during conversion. + * @returns The converted score object. + */ + public static jsonToScore(json: string, settings?: Settings): Score { + return JsonConverter.jsObjectToScore(JsonConverter.jsObjectToScore(JSON.parse(json), settings), settings); + } + + /** + * Converts the given JavaScript object into a score object. + * @param jsObject The javascript object created via {@link Score} + * @param settings The settings to use during conversion. + * @returns The converted score object. + */ + public static jsObjectToScore(jsObject: unknown, settings?: Settings): Score { + let score: Score = jsObject as Score; + let score2: Score = new Score(); + + Score.copyTo(score, score2); + RenderStylesheet.copyTo(score.stylesheet, score2.stylesheet); + + let allNotes: Map = new Map(); + let notesToLink: Note[] = []; + + JsonConverter.jsObjectToMasterBars(score, score2); + + JsonConverter.jsObjectToTracks(score, score2, allNotes, notesToLink); + + for (let note of notesToLink) { + let serializedNote = note as SerializedNote; + + if (serializedNote.tieOriginId !== undefined) { + note.tieOrigin = allNotes.get(serializedNote.tieOriginId)!; + } + if (serializedNote.tieDestinationId !== undefined) { + note.tieDestination = allNotes.get(serializedNote.tieDestinationId)!; + } + if (serializedNote.slurOriginId !== undefined) { + note.slurOrigin = allNotes.get(serializedNote.slurOriginId)!; + } + if (serializedNote.slurDestinationId !== undefined) { + note.slurDestination = allNotes.get(serializedNote.slurDestinationId)!; + } + if (serializedNote.hammerPullOriginId !== undefined) { + note.hammerPullOrigin = allNotes.get(serializedNote.hammerPullOriginId)!; + } + if (serializedNote.hammerPullDestinationId !== undefined) { + note.hammerPullDestination = allNotes.get(serializedNote.hammerPullDestinationId)!; + } + } + score2.finish(settings ?? new Settings()); + return score2; + } + + private static jsObjectToTracks(score: Score, score2: Score, allNotes: Map, notesToLink: Note[]) { + for (let t: number = 0; t < score.tracks.length; t++) { + let track: Track = score.tracks[t]; + let track2: Track = new Track(); + track2.ensureStaveCount(track.staves.length); + Track.copyTo(track, track2); + score2.addTrack(track2); + PlaybackInformation.copyTo(track.playbackInfo, track2.playbackInfo); + + JsonConverter.jsObjectToStaves(track, track2, allNotes, notesToLink); + } + } + + private static jsObjectToStaves(track: Track, track2: Track, allNotes: Map, notesToLink: Note[]) { + for (let s: number = 0; s < track.staves.length; s++) { + let staff: Staff = track.staves[s]; + let staff2: Staff = track2.staves[s]; + Staff.copyTo(staff, staff2); + JsonConverter.jsObjectMapForEach(staff.chords, (chord, chordId) => { + let chord2: Chord = new Chord(); + Chord.copyTo(chord, chord2); + staff2.addChord(chordId, chord2); + }); + + JsonConverter.jsObjectToBars(staff, staff2, allNotes, notesToLink); + } + } + + private static jsObjectMapForEach(obj: any, callback: (value: any, key: any) => void) { + if ('forEach' in obj) { + obj.forEach(callback); + } else { + for (let x in obj) { + if (obj.hasOwnProperty(x)) { + callback(obj[x], x); + } + } + } + } + + private static jsObjectToBars(staff: Staff, staff2: Staff, allNotes: Map, notesToLink: Note[]) { + for (let b: number = 0; b < staff.bars.length; b++) { + let bar: Bar = staff.bars[b]; + let bar2: Bar = new Bar(); + Bar.copyTo(bar, bar2); + staff2.addBar(bar2); + + JsonConverter.jsObjectToVoices(bar, bar2, allNotes, notesToLink); + } + } + + private static jsObjectToVoices(bar: Bar, bar2: Bar, allNotes: Map, notesToLink: Note[]) { + for (let v: number = 0; v < bar.voices.length; v++) { + let voice: Voice = bar.voices[v]; + let voice2: Voice = new Voice(); + Voice.copyTo(voice, voice2); + bar2.addVoice(voice2); + + JsonConverter.jsObjectToBeats(voice, voice2, allNotes, notesToLink); + } + } + + private static jsObjectToBeats(voice: Voice, voice2: Voice, allNotes: Map, notesToLink: Note[]) { + for (let bb: number = 0; bb < voice.beats.length; bb++) { + let beat: Beat = voice.beats[bb]; + let beat2: Beat = new Beat(); + Beat.copyTo(beat, beat2); + voice2.addBeat(beat2); + + for (let a: number = 0; a < beat.automations.length; a++) { + let automation: Automation = new Automation(); + Automation.copyTo(beat.automations[a], automation); + beat2.automations.push(automation); + } + + for (let i: number = 0; i < beat.whammyBarPoints.length; i++) { + let point: BendPoint = new BendPoint(0, 0); + BendPoint.copyTo(beat.whammyBarPoints[i], point); + beat2.addWhammyBarPoint(point); + } + + JsonConverter.jsObjectToNotes(beat, beat2, allNotes, notesToLink); + } + } + + private static jsObjectToNotes(beat: Beat, beat2: Beat, allNotes: Map, notesToLink: Note[]) { + for (let n: number = 0; n < beat.notes.length; n++) { + let note: Note = beat.notes[n]; + let note2: Note = new Note(); + Note.copyTo(note, note2); + beat2.addNote(note2); + allNotes.set(note2.id, note2); + + let serializedNote = note as SerializedNote; + let serializedNote2 = note2 as SerializedNote; + + if (serializedNote.tieOriginId !== undefined) { + serializedNote2.tieOriginId = serializedNote.tieOriginId; + notesToLink.push(note2); + } + if (serializedNote.tieDestinationId !== undefined) { + serializedNote2.tieDestinationId = serializedNote.tieDestinationId; + notesToLink.push(note2); + } + if (serializedNote.slurOriginId !== undefined) { + serializedNote2.slurOriginId = serializedNote.slurOriginId; + notesToLink.push(note2); + } + if (serializedNote.slurDestinationId !== undefined) { + serializedNote2.slurDestinationId = serializedNote.slurDestinationId; + notesToLink.push(note2); + } + if (serializedNote.hammerPullOriginId !== undefined) { + serializedNote2.hammerPullOriginId = serializedNote.hammerPullOriginId; + notesToLink.push(note2); + } + if (serializedNote.hammerPullDestinationId !== undefined) { + serializedNote2.hammerPullDestinationId = serializedNote.hammerPullDestinationId; + notesToLink.push(note2); + } + + for (let i: number = 0; i < note.bendPoints.length; i++) { + let point: BendPoint = new BendPoint(0, 0); + BendPoint.copyTo(note.bendPoints[i], point); + note2.addBendPoint(point); + } + } + } + + private static jsObjectToMasterBars(score: Score, score2: Score) { + for (let i: number = 0; i < score.masterBars.length; i++) { + let masterBar: MasterBar = score.masterBars[i]; + let masterBar2: MasterBar = new MasterBar(); + MasterBar.copyTo(masterBar, masterBar2); + + if (masterBar.tempoAutomation) { + masterBar2.tempoAutomation = new Automation(); + Automation.copyTo(masterBar.tempoAutomation, masterBar2.tempoAutomation); + } + + if (masterBar.section) { + masterBar2.section = new Section(); + Section.copyTo(masterBar.section, masterBar2.section); + } + + JsonConverter.jsObjectMapForEach(masterBar.fermata, (fermata, key) => { + let fermata2: Fermata = new Fermata(); + Fermata.copyTo(fermata, fermata2); + masterBar2.addFermata(typeof key === 'string' ? parseInt(key) : key, fermata2); + }); + score2.addMasterBar(masterBar2); + } + } + + public static jsObjectToMidiFile(midi: any): MidiFile { + let midi2: MidiFile = new MidiFile(); + midi2.division = midi.division; + let midiEvents: any[] = midi.events; + for (let midiEvent of midiEvents) { + let tick: number = midiEvent.tick; + let message: number = midiEvent.message; + let midiEvent2: MidiEvent; + switch (midiEvent.type) { + case 'SystemExclusiveEvent': + midiEvent2 = new SystemExclusiveEvent(tick, 0, 0, midiEvent.data); + midiEvent2.message = message; + break; + case 'MetaDataEvent': + midiEvent2 = new MetaDataEvent(tick, 0, 0, midiEvent.data); + midiEvent2.message = message; + break; + case 'MetaNumberEvent': + midiEvent2 = new MetaNumberEvent(tick, 0, 0, midiEvent.value); + midiEvent2.message = message; + break; + default: + midiEvent2 = new MidiEvent(tick, 0, 0, 0); + midiEvent2.message = message; + break; + } + midi2.events.push(midiEvent2); + } + return midi2; + } + + public static midiFileToJsObject(midi: MidiFile): unknown { + let midi2: any = {} as any; + midi2.division = midi.division; + let midiEvents: unknown[] = []; + midi2.events = midiEvents; + for (let midiEvent of midi.events) { + let midiEvent2: any = {} as any; + midiEvents.push(midiEvent2); + midiEvent2.tick = midiEvent.tick; + midiEvent2.message = midiEvent.message; + if (midiEvent instanceof SystemExclusiveEvent) { + midiEvent2.type = 'SystemExclusiveEvent'; + midiEvent2.data = midiEvent.data; + } else if (midiEvent instanceof MetaDataEvent) { + midiEvent2.type = 'MetaDataEvent'; + midiEvent2.data = midiEvent.data; + } else if (midiEvent instanceof MetaNumberEvent) { + midiEvent2.type = 'MetaNumberEvent'; + midiEvent2.value = midiEvent.value; + } + } + return midi2; + } +} diff --git a/src/model/KeySignature.ts b/src/model/KeySignature.ts new file mode 100644 index 000000000..9d0739065 --- /dev/null +++ b/src/model/KeySignature.ts @@ -0,0 +1,65 @@ +/** + * This public enumeration lists all available key signatures + */ +export enum KeySignature { + /** + * Cb (7 flats) + */ + Cb = -7, + /** + * Gb (6 flats) + */ + Gb = -6, + /** + * Db (5 flats) + */ + Db = -5, + /** + * Ab (4 flats) + */ + Ab = -4, + /** + * Eb (3 flats) + */ + Eb = -3, + /** + * Bb (2 flats) + */ + Bb = -2, + /** + * F (1 flat) + */ + F = -1, + /** + * C (no signs) + */ + C = 0, + /** + * G (1 sharp) + */ + G = 1, + /** + * D (2 sharp) + */ + D = 2, + /** + * A (3 sharp) + */ + A = 3, + /** + * E (4 sharp) + */ + E = 4, + /** + * B (5 sharp) + */ + B = 5, + /** + * F# (6 sharp) + */ + FSharp = 6, + /** + * C# (8 sharp) + */ + CSharp = 7 +} diff --git a/src/model/KeySignatureType.ts b/src/model/KeySignatureType.ts new file mode 100644 index 000000000..3d8bb4b56 --- /dev/null +++ b/src/model/KeySignatureType.ts @@ -0,0 +1,13 @@ +/** + * This public enumeration lists all available types of KeySignatures + */ +export enum KeySignatureType { + /** + * Major + */ + Major, + /** + * Minor + */ + Minor +} diff --git a/src/model/Lyrics.ts b/src/model/Lyrics.ts new file mode 100644 index 000000000..ef534471c --- /dev/null +++ b/src/model/Lyrics.ts @@ -0,0 +1,133 @@ +enum LyricsState { + IgnoreSpaces, + Begin, + Text, + Comment, + Dash +} + +/** + * Represents the lyrics of a song. + */ +export class Lyrics { + private static readonly CharCodeLF: number = 10; + private static readonly CharCodeTab: number = 9; + private static readonly CharCodeCR: number = 13; + private static readonly CharCodeSpace: number = 32; + private static readonly CharCodeBrackedClose: number = 93; + private static readonly CharCodeBrackedOpen: number = 91; + private static readonly CharCodeDash: number = 45; + + /** + * Gets or sets he start bar on which the lyrics should begin. + */ + public startBar: number = 0; + + /** + * Gets or sets the raw lyrics text in Guitar Pro format. + * (spaces split word syllables, plus merge syllables, [..] are comments) + */ + public text: string = ''; + + /** + * Gets or sets the prepared chunks of the lyrics to apply to beats. + */ + public chunks!: string[]; + + public finish(): void { + this.chunks = []; + this.parse(this.text, 0, this.chunks); + } + + private parse(str: string, p: number, chunks: string[]): void { + if (!str) { + return; + } + + let state: LyricsState = LyricsState.Begin; + let next: LyricsState = LyricsState.Begin; + let skipSpace: boolean = false; + let start: number = 0; + + while (p < str.length) { + let c: number = str.charCodeAt(p); + switch (state) { + case LyricsState.IgnoreSpaces: + switch (c) { + case Lyrics.CharCodeLF: + case Lyrics.CharCodeCR: + case Lyrics.CharCodeTab: + break; + case Lyrics.CharCodeSpace: + if (!skipSpace) { + state = next; + continue; + } + break; + default: + skipSpace = false; + state = next; + continue; + } + break; + case LyricsState.Begin: + switch (c) { + case Lyrics.CharCodeBrackedOpen: + state = LyricsState.Comment; + break; + default: + start = p; + state = LyricsState.Text; + continue; + } + break; + case LyricsState.Comment: + switch (c) { + case Lyrics.CharCodeBrackedClose: + state = LyricsState.Begin; + break; + } + break; + case LyricsState.Text: + switch (c) { + case Lyrics.CharCodeDash: + state = LyricsState.Dash; + break; + case Lyrics.CharCodeCR: + case Lyrics.CharCodeLF: + case Lyrics.CharCodeSpace: + let txt: string = str.substr(start, p - start); + chunks.push(this.prepareChunk(txt)); + state = LyricsState.IgnoreSpaces; + next = LyricsState.Begin; + break; + } + break; + case LyricsState.Dash: + switch (c) { + case Lyrics.CharCodeDash: + break; + default: + let txt: string = str.substr(start, p - start); + chunks.push(this.prepareChunk(txt)); + skipSpace = true; + state = LyricsState.IgnoreSpaces; + next = LyricsState.Begin; + continue; + } + break; + } + p++; + } + + if (state === LyricsState.Text) { + if (p !== start) { + chunks.push(str.substr(start, p - start)); + } + } + } + + private prepareChunk(txt: string): string { + return txt.split('+').join(' '); + } +} diff --git a/src/model/MasterBar.ts b/src/model/MasterBar.ts new file mode 100644 index 000000000..c7e61c47d --- /dev/null +++ b/src/model/MasterBar.ts @@ -0,0 +1,182 @@ +import { MidiUtils } from '@src/midi/MidiUtils'; +import { Automation } from '@src/model/Automation'; +import { Beat } from '@src/model/Beat'; +import { Fermata } from '@src/model/Fermata'; +import { KeySignature } from '@src/model/KeySignature'; +import { KeySignatureType } from '@src/model/KeySignatureType'; +import { RepeatGroup } from '@src/model/RepeatGroup'; +import { Score } from '@src/model/Score'; +import { Section } from '@src/model/Section'; +import { TripletFeel } from '@src/model/TripletFeel'; + +/** + * The MasterBar stores information about a bar which affects + * all tracks. + */ +export class MasterBar { + public static readonly MaxAlternateEndings: number = 8; + /** + * Gets or sets the bitflag for the alternate endings. Each bit defines for which repeat counts + * the bar is played. + */ + public alternateEndings: number = 0; + + /** + * Gets or sets the next masterbar in the song. + */ + public nextMasterBar: MasterBar | null = null; + + /** + * Gets or sets the next masterbar in the song. + */ + public previousMasterBar: MasterBar | null = null; + + /** + * Gets the zero based index of the masterbar. + */ + public index: number = 0; + + /** + * Gets or sets the key signature used on all bars. + */ + public keySignature: KeySignature = KeySignature.C; + + /** + * Gets or sets the type of key signature (major/minor) + */ + public keySignatureType: KeySignatureType = KeySignatureType.Major; + + /** + * Gets or sets whether a double bar is shown for this masterbar. + */ + public isDoubleBar: boolean = false; + + /** + * Gets or sets whether a repeat section starts on this masterbar. + */ + public isRepeatStart: boolean = false; + + public get isRepeatEnd(): boolean { + return this.repeatCount > 0; + } + + /** + * Gets or sets the number of repeats for the current repeat section. + */ + public repeatCount: number = 0; + + /** + * Gets or sets the repeat group this bar belongs to. + */ + public repeatGroup!: RepeatGroup; + + /** + * Gets or sets the time signature numerator. + */ + public timeSignatureNumerator: number = 4; + + /** + * Gets or sets the time signature denominiator. + */ + public timeSignatureDenominator: number = 4; + + /** + * Gets or sets whether this is bar has a common time signature. + */ + public timeSignatureCommon: boolean = false; + + /** + * Gets or sets the triplet feel that is valid for this bar. + */ + public tripletFeel: TripletFeel = TripletFeel.NoTripletFeel; + + /** + * Gets or sets the new section information for this bar. + */ + public section: Section | null = null; + + public get isSectionStart(): boolean { + return !!this.section; + } + + /** + * Gets or sets the tempo automation for this bar. + */ + public tempoAutomation: Automation | null = null; + + /** + * Gets or sets the reference to the score this song belongs to. + */ + public score!: Score; + + /** + * Gets or sets the fermatas for this bar. The key is the offset of the fermata in midi ticks. + */ + public fermata: Map = new Map(); + + /** + * The timeline position of the voice within the whole score. (unit: midi ticks) + */ + public start: number = 0; + + /** + * Gets or sets a value indicating whether the master bar is an anacrusis (aka. pickup bar) + */ + public isAnacrusis: boolean = false; + + public static copyTo(src: MasterBar, dst: MasterBar): void { + dst.isAnacrusis = src.isAnacrusis; + dst.alternateEndings = src.alternateEndings; + dst.index = src.index; + dst.keySignature = src.keySignature; + dst.keySignatureType = src.keySignatureType; + dst.isDoubleBar = src.isDoubleBar; + dst.isRepeatStart = src.isRepeatStart; + dst.repeatCount = src.repeatCount; + dst.timeSignatureNumerator = src.timeSignatureNumerator; + dst.timeSignatureDenominator = src.timeSignatureDenominator; + dst.timeSignatureCommon = src.timeSignatureCommon; + dst.tripletFeel = src.tripletFeel; + dst.start = src.start; + } + + /** + * Calculates the time spent in this bar. (unit: midi ticks) + */ + public calculateDuration(): number { + if (this.isAnacrusis) { + let duration: number = 0; + for (let track of this.score.tracks) { + for (let staff of track.staves) { + let barDuration: number = staff.bars[0].calculateDuration(); + if (barDuration > duration) { + duration = barDuration; + } + } + } + return duration; + } + return this.timeSignatureNumerator * MidiUtils.valueToTicks(this.timeSignatureDenominator); + } + + /** + * Adds a fermata to the masterbar. + * @param offset The offset of the fermata within the bar in midi ticks. + * @param fermata The fermata. + */ + public addFermata(offset: number, fermata: Fermata): void { + this.fermata.set(offset, fermata); + } + + /** + * Gets the fermata for a given beat. + * @param beat The beat to get the fermata for. + * @returns + */ + public getFermata(beat: Beat): Fermata | null { + if (this.fermata.has(beat.playbackStart)) { + return this.fermata.get(beat.playbackStart)!; + } + return null; + } +} diff --git a/src/model/ModelUtils.ts b/src/model/ModelUtils.ts new file mode 100644 index 000000000..6f97a1db9 --- /dev/null +++ b/src/model/ModelUtils.ts @@ -0,0 +1,264 @@ +import { GeneralMidi } from '@src/midi/GeneralMidi'; +import { Beat } from '@src/model/Beat'; +import { Duration } from '@src/model/Duration'; +import { Fingers } from '@src/model/Fingers'; +import { Score } from '@src/model/Score'; +import { FingeringMode } from '@src/NotationSettings'; +import { Settings } from '@src/Settings'; + +export class TuningParseResult { + public note: string | null = null; + public noteValue: number = 0; + public octave: number = 0; + + public get realValue(): number { + return this.octave * 12 + this.noteValue; + } +} + +/** + * This public class contains some utilities for working with model public classes + */ +export class ModelUtils { + public static getIndex(duration: Duration): number { + let index: number = 0; + let value: number = duration; + if (value < 0) { + return index; + } + return Math.log2(duration) | 0; + } + + public static keySignatureIsFlat(ks: number): boolean { + return ks < 0; + } + + public static keySignatureIsNatural(ks: number): boolean { + return ks === 0; + } + + public static keySignatureIsSharp(ks: number): boolean { + return ks > 0; + } + + public static applyPitchOffsets(settings: Settings, score: Score): void { + for (let i: number = 0; i < score.tracks.length; i++) { + if (i < settings.notation.displayTranspositionPitches.length) { + for (let staff of score.tracks[i].staves) { + staff.displayTranspositionPitch = -settings.notation.displayTranspositionPitches[i]; + } + } + if (i < settings.notation.transpositionPitches.length) { + for (let staff of score.tracks[i].staves) { + staff.transpositionPitch = -settings.notation.transpositionPitches[i]; + } + } + } + } + + public static fingerToString(settings: Settings, beat: Beat, finger: Fingers, leftHand: boolean): string | null { + if ( + settings.notation.fingeringMode === FingeringMode.ScoreForcePiano || + settings.notation.fingeringMode === FingeringMode.SingleNoteEffectBandForcePiano || + GeneralMidi.isPiano(beat.voice.bar.staff.track.playbackInfo.program) + ) { + switch (finger) { + case Fingers.Unknown: + case Fingers.NoOrDead: + return null; + case Fingers.Thumb: + return '1'; + case Fingers.IndexFinger: + return '2'; + case Fingers.MiddleFinger: + return '3'; + case Fingers.AnnularFinger: + return '4'; + case Fingers.LittleFinger: + return '5'; + default: + return null; + } + } + if (leftHand) { + switch (finger) { + case Fingers.Unknown: + case Fingers.NoOrDead: + return '0'; + case Fingers.Thumb: + return 'T'; + case Fingers.IndexFinger: + return '1'; + case Fingers.MiddleFinger: + return '2'; + case Fingers.AnnularFinger: + return '3'; + case Fingers.LittleFinger: + return '4'; + default: + return null; + } + } + switch (finger) { + case Fingers.Unknown: + case Fingers.NoOrDead: + return null; + case Fingers.Thumb: + return 'p'; + case Fingers.IndexFinger: + return 'i'; + case Fingers.MiddleFinger: + return 'm'; + case Fingers.AnnularFinger: + return 'a'; + case Fingers.LittleFinger: + return 'c'; + default: + return null; + } + } + + /** + * Checks if the given string is a tuning inticator. + * @param name + * @returns + */ + public static isTuning(name: string): boolean { + return !!ModelUtils.parseTuning(name); + } + + public static parseTuning(name: string): TuningParseResult | null { + let note: string = ''; + let octave: string = ''; + for (let i: number = 0; i < name.length; i++) { + let c: number = name.charCodeAt(i); + if (c >= 0x30 && c <= 0x39) { + // number without note? + if (!note) { + return null; + } + octave += String.fromCharCode(c); + } else if ((c >= 0x41 && c <= 0x5a) || (c >= 0x61 && c <= 0x7a) || c === 0x23) { + note += String.fromCharCode(c); + } else { + return null; + } + } + if (!octave || !note) { + return null; + } + let result: TuningParseResult = new TuningParseResult(); + result.octave = parseInt(octave) + 1; + result.note = note.toLowerCase(); + result.noteValue = ModelUtils.getToneForText(result.note); + return result; + } + + public static getTuningForText(str: string): number { + let result: TuningParseResult | null = ModelUtils.parseTuning(str); + if (!result) { + return -1; + } + return result.realValue; + } + + public static getToneForText(note: string): number { + let b: number = 0; + switch (note.toLowerCase()) { + case 'c': + b = 0; + break; + case 'c#': + case 'db': + b = 1; + break; + case 'd': + b = 2; + break; + case 'd#': + case 'eb': + b = 3; + break; + case 'e': + b = 4; + break; + case 'f': + b = 5; + break; + case 'f#': + case 'gb': + b = 6; + break; + case 'g': + b = 7; + break; + case 'g#': + case 'ab': + b = 8; + break; + case 'a': + b = 9; + break; + case 'a#': + case 'bb': + b = 10; + break; + case 'b': + b = 11; + break; + default: + return 0; + } + return b; + } + + public static newGuid(): string { + return ( + Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1) + + Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1) + + '-' + + Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1) + + '-' + + Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1) + + '-' + + Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1) + + '-' + + Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1) + + Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1) + + Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1) + ); + } + + public static isAlmostEqualTo(a: number, b: number): boolean { + return Math.abs(a - b) < 0.00001; + } + + public static toHexString(n: number, digits: number = 0): string { + let s: string = ''; + let hexChars: string = '0123456789ABCDEF'; + do { + s = String.fromCharCode(hexChars.charCodeAt(n & 15)) + s; + n = n >> 4; + } while (n > 0); + while (s.length < digits) { + s = '0' + s; + } + return s; + } +} diff --git a/src/model/Note.ts b/src/model/Note.ts new file mode 100644 index 000000000..ea663e913 --- /dev/null +++ b/src/model/Note.ts @@ -0,0 +1,838 @@ +import { AccentuationType } from '@src/model/AccentuationType'; +import { Beat } from '@src/model/Beat'; +import { BendPoint } from '@src/model/BendPoint'; +import { BendStyle } from '@src/model/BendStyle'; +import { BendType } from '@src/model/BendType'; +import { Duration } from '@src/model/Duration'; +import { DynamicValue } from '@src/model/DynamicValue'; +import { Fingers } from '@src/model/Fingers'; +import { HarmonicType } from '@src/model/HarmonicType'; +import { NoteAccidentalMode } from '@src/model/NoteAccidentalMode'; +import { Ottavia } from '@src/model/Ottavia'; +import { SlideInType } from '@src/model/SlideInType'; +import { SlideOutType } from '@src/model/SlideOutType'; +import { Staff } from '@src/model/Staff'; +import { VibratoType } from '@src/model/VibratoType'; +import { NotationMode } from '@src/NotationSettings'; +import { PercussionMapper } from '@src/rendering/utils/PercussionMapper'; +import { Settings } from '@src/Settings'; +import { Lazy } from '@src/util/Lazy'; +import { Logger } from '@src/Logger'; +import { ModelUtils } from '@src/model/ModelUtils'; +import { PickStroke } from './PickStroke'; + +/** + * A note is a single played sound on a fretted instrument. + * It consists of a fret offset and a string on which the note is played on. + * It also can be modified by a lot of different effects. + */ +export class Note { + public static GlobalNoteId: number = 0; + /** + * Gets or sets the unique id of this note. + */ + public id: number = Note.GlobalNoteId++; + + /** + * Gets or sets the zero-based index of this note within the beat. + */ + public index: number = 0; + + /** + * Gets or sets the accentuation of this note. + */ + public accentuated: AccentuationType = AccentuationType.None; + + /** + * Gets or sets the bend type for this note. + */ + public bendType: BendType = BendType.None; + + /** + * Gets or sets the bend style for this note. + */ + public bendStyle: BendStyle = BendStyle.Default; + + /** + * Gets or sets the note from which this note continues the bend. + */ + public bendOrigin: Note | null = null; + + /** + * Gets or sets whether this note continues a bend from a previous note. + */ + public isContinuedBend: boolean = false; + + /** + * Gets or sets a list of the points defining the bend behavior. + */ + public bendPoints: BendPoint[] = []; + + /** + * Gets or sets the bend point with the highest bend value. + */ + public maxBendPoint: BendPoint | null = null; + + public get hasBend(): boolean { + return this.bendType !== BendType.None; + } + + public get isStringed(): boolean { + return this.string >= 0; + } + + /** + * Gets or sets the fret on which this note is played on the instrument. + */ + public fret: number = -1; + + /** + * Gets or sets the string number where the note is placed. + * 1 is the lowest string on the guitar and the bottom line on the tablature. + * It then increases the the number of strings on available on the track. + */ + public string: number = -1; + + public get isPiano(): boolean { + return !this.isStringed && this.octave >= 0 && this.tone >= 0; + } + + /** + * Gets or sets the octave on which this note is played. + */ + public octave: number = -1; + + /** + * Gets or sets the tone of this note within the octave. + */ + public tone: number = -1; + + public get isPercussion(): boolean { + return !this.isStringed && this.element >= 0 && this.variation >= 0; + } + + /** + * Gets or sets the percusson element. + */ + public element: number = -1; + + /** + * Gets or sets the variation of this note. + */ + public variation: number = -1; + + /** + * Gets or sets whether this note is visible on the music sheet. + */ + public isVisible: boolean = true; + + /** + * Gets a value indicating whether the note is left hand tapped. + */ + public isLeftHandTapped: boolean = false; + + /** + * Gets or sets whether this note starts a hammeron or pulloff. + */ + public isHammerPullOrigin: boolean = false; + + public get isHammerPullDestination(): boolean { + return !!this.hammerPullOrigin; + } + + /** + * Gets the origin of the hammeron/pulloff of this note. + */ + public hammerPullOrigin: Note | null = null; + + /** + * Gets the destination for the hammeron/pullof started by this note. + */ + public hammerPullDestination: Note | null = null; + + public get isSlurOrigin(): boolean { + return !!this.slurDestination; + } + + /** + * Gets or sets whether this note finishes a slur. + */ + public isSlurDestination: boolean = false; + + /** + * Gets or sets the note where the slur of this note starts. + */ + public slurOrigin: Note | null = null; + + /** + * Gets or sets the note where the slur of this note ends. + */ + public slurDestination: Note | null = null; + + public get isHarmonic(): boolean { + return this.harmonicType !== HarmonicType.None; + } + + /** + * Gets or sets the harmonic type applied to this note. + */ + public harmonicType: HarmonicType = HarmonicType.None; + + /** + * Gets or sets the value defining the harmonic pitch. + */ + public harmonicValue: number = 0; + + /** + * Gets or sets whether the note is a ghost note and shown in parenthesis. Also this will make the note a bit more silent. + */ + public isGhost: boolean = false; + + /** + * Gets or sets whether this note has a let-ring effect. + */ + public isLetRing: boolean = false; + + /** + * Gets or sets the destination note for the let-ring effect. + */ + public letRingDestination: Note | null = null; + + /** + * Gets or sets whether this note has a palm-mute effect. + */ + public isPalmMute: boolean = false; + + /** + * Gets or sets the destination note for the palm-mute effect. + */ + public palmMuteDestination: Note | null = null; + + /** + * Gets or sets whether the note is shown and played as dead note. + */ + public isDead: boolean = false; + + /** + * Gets or sets whether the note is played as staccato. + */ + public isStaccato: boolean = false; + + /** + * Gets or sets the slide-in type this note is played with. + */ + public slideInType: SlideInType = SlideInType.None; + + /** + * Gets or sets the slide-out type this note is played with. + */ + public slideOutType: SlideOutType = SlideOutType.None; + + /** + * Gets or sets the target note for several slide types. + */ + public slideTarget: Note | null = null; + + /** + * Gets or sets the source note for several slide types. + */ + public slideOrigin: Note | null = null; + + /** + * Gets or sets whether a vibrato is played on the note. + */ + public vibrato: VibratoType = VibratoType.None; + + /** + * Gets or sets the origin of the tied if this note is tied. + */ + public tieOrigin: Note | null = null; + + /** + * Gets or sets the desination of the tie. + */ + public tieDestination: Note | null = null; + + /** + * Gets or sets whether this note is ends a tied note. + */ + public isTieDestination: boolean = false; + + public get isTieOrigin(): boolean { + return !!this.tieDestination; + } + + /** + * Gets or sets the fingers used for this note on the left hand. + */ + public leftHandFinger: Fingers = Fingers.Unknown; + + /** + * Gets or sets the fingers used for this note on the right hand. + */ + public rightHandFinger: Fingers = Fingers.Unknown; + + /** + * Gets or sets whether this note has fingering defined. + */ + public isFingering: boolean = false; + + /** + * Gets or sets the target note value for the trill effect. + */ + public trillValue: number = -1; + + public get trillFret(): number { + return this.trillValue - this.stringTuning; + } + + public get isTrill(): boolean { + return this.trillValue >= 0; + } + + /** + * Gets or sets the speed of the trill effect. + */ + public trillSpeed: Duration = Duration.ThirtySecond; + + /** + * Gets or sets the percentual duration of the note relative to the overall beat duration . + */ + public durationPercent: number = 1; + + /** + * Gets or sets how accidetnals for this note should be handled. + */ + public accidentalMode: NoteAccidentalMode = NoteAccidentalMode.Default; + + /** + * Gets or sets the reference to the parent beat to which this note belongs to. + */ + public beat!: Beat; + + /** + * Gets or sets the dynamics for this note. + */ + public dynamics: DynamicValue = DynamicValue.F; + + public isEffectSlurOrigin: boolean = false; + + public hasEffectSlur: boolean = false; + + public get isEffectSlurDestination(): boolean { + return !!this.effectSlurOrigin; + } + + public effectSlurOrigin: Note | null = null; + + public effectSlurDestination: Note | null = null; + + public get stringTuning(): number { + return this.beat.voice.bar.staff.capo + Note.getStringTuning(this.beat.voice.bar.staff, this.string); + } + + public static getStringTuning(staff: Staff, noteString: number): number { + if (staff.tuning.length > 0) { + return staff.tuning[staff.tuning.length - (noteString - 1) - 1]; + } + return 0; + } + + public get realValue(): number { + if (this.isPercussion) { + return PercussionMapper.midiFromElementVariation(this); + } + if (this.isStringed) { + if (this.harmonicType === HarmonicType.Natural) { + return this.harmonicPitch + this.stringTuning - this.beat.voice.bar.staff.transpositionPitch; + } + return this.fret + this.stringTuning - this.beat.voice.bar.staff.transpositionPitch + this.harmonicPitch; + } + if (this.isPiano) { + return this.octave * 12 + this.tone - this.beat.voice.bar.staff.transpositionPitch; + } + return 0; + } + + public get harmonicPitch(): number { + if (this.harmonicType === HarmonicType.None || !this.isStringed) { + return 0; + } + let value: number = this.harmonicValue; + // add semitones to reach corresponding harmonic frets + if (ModelUtils.isAlmostEqualTo(value, 2.4)) { + return 36; + } + if (ModelUtils.isAlmostEqualTo(value, 2.7)) { + // Fret 3 2nd octave + minor seventh + return 34; + } + if (value < 3) { + // no natural harmonics below fret 3 + return 0; + } + if (value <= 3.5) { + // Fret 3 2nd octave + fifth + return 31; + } + if (value <= 4) { + return 28; + } + if (value <= 5) { + return 24; + } + if (value <= 6) { + return 34; + } + if (value <= 7) { + return 19; + } + if (value <= 8.5) { + return 36; + } + if (value <= 9) { + return 28; + } + if (value <= 10) { + return 34; + } + if (value <= 11) { + return 0; + } + if (value <= 12) { + return 12; + } + if (value < 14) { + // fret 13,14 stay + return 0; + } + if (value <= 15) { + return 34; + } + if (value <= 16) { + return 28; + } + if (value <= 17) { + return 36; + } + if (value <= 18) { + return 0; + } + if (value <= 19) { + return 19; + } + if (value <= 21) { + // 20,21 stay + return 0; + } + if (value <= 22) { + return 36; + } + if (value <= 24) { + return 24; + } + return 0; + } + + public get displayValue(): number { + let noteValue: number = this.displayValueWithoutBend; + if (this.hasBend) { + noteValue += (this.bendPoints[0].value / 2) | 0; + } else if (this.bendOrigin) { + noteValue += (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; + } else if (this.beat.hasWhammyBar) { + noteValue += (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 noteValue; + } + + public get displayValueWithoutBend(): number { + let noteValue: number = this.realValue; + if (this.harmonicType !== HarmonicType.Natural && this.harmonicType !== HarmonicType.None) { + noteValue -= this.harmonicPitch; + } + switch (this.beat.ottava) { + case Ottavia._15ma: + noteValue -= 24; + break; + case Ottavia._8va: + noteValue -= 12; + break; + case Ottavia.Regular: + break; + case Ottavia._8vb: + noteValue += 12; + break; + case Ottavia._15mb: + noteValue += 24; + break; + } + switch (this.beat.voice.bar.clefOttava) { + case Ottavia._15ma: + noteValue -= 24; + break; + case Ottavia._8va: + noteValue -= 12; + break; + case Ottavia.Regular: + break; + case Ottavia._8vb: + noteValue += 12; + break; + case Ottavia._15mb: + noteValue += 24; + break; + } + return noteValue - this.beat.voice.bar.staff.displayTranspositionPitch; + } + + public get hasQuarterToneOffset(): boolean { + if (this.hasBend) { + return this.bendPoints[0].value % 2 !== 0; + } + if (this.bendOrigin) { + return this.bendOrigin.bendPoints[this.bendOrigin.bendPoints.length - 1].value % 2 !== 0; + } + if (this.beat.hasWhammyBar) { + return this.beat.whammyBarPoints[0].value % 2 !== 0; + } + if (this.beat.isContinuedWhammy) { + return ( + this.beat.previousBeat!.whammyBarPoints[this.beat.previousBeat!.whammyBarPoints.length - 1].value % + 2 !== + 0 + ); + } + return false; + } + + public static copyTo(src: Note, dst: Note): void { + dst.id = src.id; + dst.accentuated = src.accentuated; + dst.fret = src.fret; + dst.string = src.string; + dst.harmonicValue = src.harmonicValue; + dst.harmonicType = src.harmonicType; + dst.isGhost = src.isGhost; + dst.isLetRing = src.isLetRing; + dst.isPalmMute = src.isPalmMute; + dst.isDead = src.isDead; + dst.isStaccato = src.isStaccato; + dst.slideInType = src.slideInType; + dst.slideOutType = src.slideOutType; + dst.vibrato = src.vibrato; + dst.isTieDestination = src.isTieDestination; + dst.isSlurDestination = src.isSlurDestination; + dst.isHammerPullOrigin = src.isHammerPullOrigin; + dst.leftHandFinger = src.leftHandFinger; + dst.rightHandFinger = src.rightHandFinger; + dst.isFingering = src.isFingering; + dst.trillValue = src.trillValue; + dst.trillSpeed = src.trillSpeed; + dst.durationPercent = src.durationPercent; + dst.accidentalMode = src.accidentalMode; + dst.dynamics = src.dynamics; + dst.octave = src.octave; + dst.tone = src.tone; + dst.element = src.element; + dst.variation = src.variation; + dst.bendType = src.bendType; + dst.bendStyle = src.bendStyle; + dst.isContinuedBend = src.isContinuedBend; + dst.isVisible = src.isVisible; + dst.isLeftHandTapped = src.isLeftHandTapped; + } + + public clone(): Note { + let n: Note = new Note(); + let id: number = n.id; + Note.copyTo(this, n); + for (let i: number = 0, j: number = this.bendPoints.length; i < j; i++) { + n.addBendPoint(this.bendPoints[i].clone()); + } + n.id = id; + return n; + } + + public addBendPoint(point: BendPoint): void { + this.bendPoints.push(point); + if (!this.maxBendPoint || point.value > this.maxBendPoint.value) { + this.maxBendPoint = point; + } + if (this.bendType === BendType.None) { + this.bendType = BendType.Custom; + } + } + + public finish(settings: Settings): void { + let nextNoteOnLine: Lazy = new Lazy(() => Note.nextNoteOnSameLine(this)); + let isSongBook: boolean = settings && settings.notation.notationMode === NotationMode.SongBook; + // connect ties + if (this.isTieDestination) { + this.chain(); + // implicit let ring + if (isSongBook && this.tieOrigin && this.tieOrigin.isLetRing) { + this.isLetRing = true; + } + } + // connect letring + if (this.isLetRing) { + if (!nextNoteOnLine.value || !nextNoteOnLine.value.isLetRing) { + this.letRingDestination = this; + } else { + this.letRingDestination = nextNoteOnLine.value; + } + if (isSongBook && this.isTieDestination && !this.tieOrigin!.hasBend) { + this.isVisible = false; + } + } + // connect palmmute + if (this.isPalmMute) { + if (!nextNoteOnLine.value || !nextNoteOnLine.value.isPalmMute) { + this.palmMuteDestination = this; + } else { + this.palmMuteDestination = nextNoteOnLine.value; + } + } + // set hammeron/pulloffs + if (this.isHammerPullOrigin) { + this.hammerPullDestination = Note.findHammerPullDestination(this); + + if (!this.hammerPullDestination) { + this.isHammerPullOrigin = false; + } else { + this.hammerPullDestination.hammerPullOrigin = this; + } + } + // set slides + switch (this.slideOutType) { + case SlideOutType.Shift: + case SlideOutType.Legato: + this.slideTarget = nextNoteOnLine.value; + if (!this.slideTarget) { + this.slideOutType = SlideOutType.None; + } else { + this.slideTarget.slideOrigin = this; + } + break; + } + let effectSlurDestination: Note | null = null; + if (this.isHammerPullOrigin && this.hammerPullDestination) { + effectSlurDestination = this.hammerPullDestination; + } else if (this.slideOutType === SlideOutType.Legato && this.slideTarget) { + effectSlurDestination = this.slideTarget; + } + if (effectSlurDestination) { + this.hasEffectSlur = true; + if (this.effectSlurOrigin && this.beat.pickStroke === PickStroke.None) { + this.effectSlurOrigin.effectSlurDestination = effectSlurDestination; + this.effectSlurOrigin.effectSlurDestination.effectSlurOrigin = this.effectSlurOrigin; + this.effectSlurOrigin = null; + } else { + this.isEffectSlurOrigin = true; + this.effectSlurDestination = effectSlurDestination; + this.effectSlurDestination.effectSlurOrigin = this; + } + } + // try to detect what kind of bend was used and cleans unneeded points if required + // Guitar Pro 6 and above (gpif.xml) uses exactly 4 points to define all bends + if (this.bendPoints.length > 0 && this.bendType === BendType.Custom) { + let isContinuedBend: boolean = (this.isContinuedBend = !!this.tieOrigin && this.tieOrigin.hasBend); + if (this.bendPoints.length === 4) { + let origin: BendPoint = this.bendPoints[0]; + let middle1: BendPoint = this.bendPoints[1]; + let middle2: BendPoint = this.bendPoints[2]; + let destination: BendPoint = this.bendPoints[3]; + // the middle points are used for holds, anything else is a new feature we do not support yet + if (middle1.value === middle2.value) { + // bend higher? + if (destination.value > origin.value) { + if (middle1.value > destination.value) { + this.bendType = BendType.BendRelease; + } else if (!isContinuedBend && origin.value > 0) { + this.bendType = BendType.PrebendBend; + this.bendPoints.splice(2, 1); + this.bendPoints.splice(1, 1); + } else { + this.bendType = BendType.Bend; + this.bendPoints.splice(2, 1); + this.bendPoints.splice(1, 1); + } + } else if (destination.value < origin.value) { + // origin must be > 0 otherwise it's no release, we cannot bend negative + if (isContinuedBend) { + this.bendType = BendType.Release; + this.bendPoints.splice(2, 1); + this.bendPoints.splice(1, 1); + } else { + this.bendType = BendType.PrebendRelease; + this.bendPoints.splice(2, 1); + this.bendPoints.splice(1, 1); + } + } else { + if (middle1.value > origin.value) { + this.bendType = BendType.BendRelease; + } else if (origin.value > 0 && !isContinuedBend) { + this.bendType = BendType.Prebend; + this.bendPoints.splice(2, 1); + this.bendPoints.splice(1, 1); + } else { + this.bendType = BendType.Hold; + this.bendPoints.splice(2, 1); + this.bendPoints.splice(1, 1); + } + } + } else { + Logger.warning('Model', 'Unsupported bend type detected, fallback to custom', null); + } + } else if (this.bendPoints.length === 2) { + let origin: BendPoint = this.bendPoints[0]; + let destination: BendPoint = this.bendPoints[1]; + // bend higher? + if (destination.value > origin.value) { + if (!isContinuedBend && origin.value > 0) { + this.bendType = BendType.PrebendBend; + } else { + this.bendType = BendType.Bend; + } + } else if (destination.value < origin.value) { + // origin must be > 0 otherwise it's no release, we cannot bend negative + if (isContinuedBend) { + this.bendType = BendType.Release; + } else { + this.bendType = BendType.PrebendRelease; + } + } else { + this.bendType = BendType.Hold; + } + } + } else if (this.bendPoints.length === 0) { + this.bendType = BendType.None; + } + } + + private static readonly MaxOffsetForSameLineSearch: number = 3; + + public static nextNoteOnSameLine(note: Note): Note | null { + let nextBeat: Beat | null = note.beat.nextBeat; + // keep searching in same bar + while (nextBeat && nextBeat.voice.bar.index <= note.beat.voice.bar.index + Note.MaxOffsetForSameLineSearch) { + let noteOnString: Note | null = nextBeat.getNoteOnString(note.string); + if (noteOnString) { + return noteOnString; + } + nextBeat = nextBeat.nextBeat; + } + return null; + } + + static findHammerPullDestination(note: Note): Note | null { + // For Hammer-Pull destinations we have 2 potential candidates + // 1. A note on the same string + // 2. A note on a different string, but with a left-hand-tapping applied + + // for the second case we have a special logic to search for notes: + // 1. We first search on lower strings, then on higher strings + // 2. If we find a note with a left-hand-tap applied it becomes the target + // 3. If we find a note without a left-hand-tap we stop searching in this direction + + let nextBeat: Beat | null = note.beat.nextBeat; + // keep searching in same bar + while (nextBeat && nextBeat.voice.bar.index <= note.beat.voice.bar.index + Note.MaxOffsetForSameLineSearch) { + // 1. same string first + let noteOnString: Note | null = nextBeat.getNoteOnString(note.string); + if (noteOnString) { + return noteOnString; + } + + // 2. search toward lower strings + for (let str = note.string; str > 0; str--) { + noteOnString = nextBeat.getNoteOnString(str); + if (noteOnString) { + if (noteOnString.isLeftHandTapped) { + return noteOnString; + } else { + break; + } + } + } + + // 3. search toward higher strings + for (let str = note.string; str <= note.beat.voice.bar.staff.tuning.length; str++) { + noteOnString = nextBeat.getNoteOnString(str); + if (noteOnString) { + if (noteOnString.isLeftHandTapped) { + return noteOnString; + } else { + break; + } + } + } + + // nothing found, search on next beat + nextBeat = nextBeat.nextBeat; + } + return null; + } + + public static findTieOrigin(note: Note): Note | null { + let previousBeat: Beat | null = note.beat.previousBeat; + // keep searching in same bar + while ( + previousBeat && + previousBeat.voice.bar.index >= note.beat.voice.bar.index - Note.MaxOffsetForSameLineSearch + ) { + if (note.isStringed) { + let noteOnString: Note | null = previousBeat.getNoteOnString(note.string); + if (noteOnString) { + return noteOnString; + } + } else { + if (note.octave === -1 && note.tone === -1) { + // if the note has no value (e.g. alphaTex dash tie), we try to find a matching + // note on the previous beat by index. + if (note.index < previousBeat.notes.length) { + return previousBeat.notes[note.index]; + } + } else { + let noteWithValue: Note | null = previousBeat.getNoteWithRealValue(note.realValue); + if (noteWithValue) { + return noteWithValue; + } + } + } + previousBeat = previousBeat.previousBeat; + } + return null; + } + + public chain() { + if (!this.isTieDestination) { + return; + } + + if (!this.tieOrigin) { + this.tieOrigin = Note.findTieOrigin(this); + } + + if (!this.tieOrigin) { + this.isTieDestination = false; + } else { + this.tieOrigin.tieDestination = this; + this.fret = this.tieOrigin.fret; + this.octave = this.tieOrigin.octave; + this.tone = this.tieOrigin.tone; + if (this.tieOrigin.hasBend) { + this.bendOrigin = this.tieOrigin; + } + } + } +} diff --git a/src/model/NoteAccidentalMode.ts b/src/model/NoteAccidentalMode.ts new file mode 100644 index 000000000..0e7d9a19f --- /dev/null +++ b/src/model/NoteAccidentalMode.ts @@ -0,0 +1,25 @@ +/** + * Lists the modes how accidentals are handled for notes + */ +export enum NoteAccidentalMode { + /** + * Accidentals are calculated automatically. + */ + Default, + /** + * If the default behavior calculates a Sharp, use flat instead (and vice versa). + */ + SwapAccidentals, + /** + * This will move the note one line down and applies a Naturalize. + */ + ForceNatural, + /** + * This will move the note one line down and applies a Sharp. + */ + ForceSharp, + /** + * This will move the note one line up and applies a Flat. + */ + ForceFlat +} diff --git a/src/model/Ottavia.ts b/src/model/Ottavia.ts new file mode 100644 index 000000000..3c40d0162 --- /dev/null +++ b/src/model/Ottavia.ts @@ -0,0 +1,25 @@ +/** + * Lists all ottavia. + */ +export enum Ottavia { + /** + * 2 octaves higher + */ + _15ma, + /** + * 1 octave higher + */ + _8va, + /** + * Normal + */ + Regular, + /** + * 1 octave lower + */ + _8vb, + /** + * 2 octaves lower. + */ + _15mb +} diff --git a/src/model/PickStroke.ts b/src/model/PickStroke.ts new file mode 100644 index 000000000..10f1525ac --- /dev/null +++ b/src/model/PickStroke.ts @@ -0,0 +1,17 @@ +/** + * Lists all types of pick strokes. + */ +export enum PickStroke { + /** + * No pickstroke used. + */ + None, + /** + * Pickstroke up. + */ + Up, + /** + * Pickstroke down + */ + Down +} diff --git a/src/model/PlaybackInformation.ts b/src/model/PlaybackInformation.ts new file mode 100644 index 000000000..882599d46 --- /dev/null +++ b/src/model/PlaybackInformation.ts @@ -0,0 +1,56 @@ +/** + * This public class stores the midi specific information of a track needed + * for playback. + */ +export class PlaybackInformation { + /** + * Gets or sets the volume (0-16) + */ + public volume: number = 15; + + /** + * Gets or sets the balance (0-16; 8=center) + */ + public balance: number = 8; + + /** + * Gets or sets the midi port to use. + */ + public port: number = 1; + + /** + * Gets or sets the midi program to use. + */ + public program: number = 0; + + /** + * Gets or sets the primary channel for all normal midi events. + */ + public primaryChannel: number = 0; + + /** + * Gets or sets the secondary channel for special midi events. + */ + public secondaryChannel: number = 0; + + /** + * Gets or sets whether the track is muted. + */ + public isMute: boolean = false; + + /** + * Gets or sets whether the track is playing alone. + */ + public isSolo: boolean = false; + + public static copyTo(src: PlaybackInformation, dst: PlaybackInformation): void { + dst.volume = src.volume; + dst.balance = src.balance; + dst.port = src.port; + dst.program = src.program; + dst.primaryChannel = src.primaryChannel; + dst.secondaryChannel = src.secondaryChannel; + dst.isMute = src.isMute; + dst.isSolo = src.isSolo; + } +} diff --git a/src/model/RenderStylesheet.ts b/src/model/RenderStylesheet.ts new file mode 100644 index 000000000..7dae16ecb --- /dev/null +++ b/src/model/RenderStylesheet.ts @@ -0,0 +1,14 @@ +/** + * This class represents the rendering stylesheet. + * It contains settings which control the display of the score when rendered. + */ +export class RenderStylesheet { + /** + * Gets or sets whether dynamics are hidden. + */ + public hideDynamics: boolean = false; + + public static copyTo(src: RenderStylesheet, dst: RenderStylesheet): void { + dst.hideDynamics = src.hideDynamics; + } +} diff --git a/src/model/RepeatGroup.ts b/src/model/RepeatGroup.ts new file mode 100644 index 000000000..8e1c51766 --- /dev/null +++ b/src/model/RepeatGroup.ts @@ -0,0 +1,50 @@ +import { MasterBar } from '@src/model/MasterBar'; + +/** + * This public class can store the information about a group of measures which are repeated + */ +export class RepeatGroup { + /** + * All masterbars repeated within this group + */ + public masterBars: MasterBar[] = []; + + /** + * a list of masterbars which open the group. + */ + public openings: MasterBar[] = []; + + /** + * a list of masterbars which close the group. + */ + public closings: MasterBar[] = []; + + /** + * true if the repeat group was opened well + */ + public isOpened: boolean = false; + + /** + * true if the repeat group was closed well + */ + public isClosed: boolean = false; + + public addMasterBar(masterBar: MasterBar): void { + if (this.openings.length === 0) { + this.openings.push(masterBar); + } + this.masterBars.push(masterBar); + masterBar.repeatGroup = this; + if (masterBar.isRepeatEnd) { + this.closings.push(masterBar); + this.isClosed = true; + if (!this.isOpened) { + this.masterBars[0].isRepeatStart = true; + this.isOpened = true; + } + } else if (this.isClosed) { + this.isClosed = false; + this.openings.push(masterBar); + } + } +} diff --git a/src/model/Score.ts b/src/model/Score.ts new file mode 100644 index 000000000..bc3bc5a31 --- /dev/null +++ b/src/model/Score.ts @@ -0,0 +1,147 @@ +import { MasterBar } from '@src/model/MasterBar'; +import { RenderStylesheet } from '@src/model/RenderStylesheet'; +import { RepeatGroup } from '@src/model/RepeatGroup'; +import { Track } from '@src/model/Track'; +import { Settings } from '@src/Settings'; + +/** + * The score is the root node of the complete + * model. It stores the basic information of + * a song and stores the sub components. + */ +export class Score { + private _currentRepeatGroup: RepeatGroup = new RepeatGroup(); + + /** + * The album of this song. + */ + public album: string = ''; + + /** + * The artist who performs this song. + */ + public artist: string = ''; + + /** + * The owner of the copyright of this song. + */ + public copyright: string = ''; + + /** + * Additional instructions + */ + public instructions: string = ''; + + /** + * The author of the music. + */ + public music: string = ''; + + /** + * Some additional notes about the song. + */ + public notices: string = ''; + + /** + * The subtitle of the song. + */ + public subTitle: string = ''; + + /** + * The title of the song. + */ + public title: string = ''; + + /** + * The author of the song lyrics + */ + public words: string = ''; + + /** + * The author of this tablature. + */ + public tab: string = ''; + + /** + * Gets or sets the global tempo of the song in BPM. The tempo might change via {@link MasterBar.tempo}. + */ + public tempo: number = 120; + + /** + * Gets or sets the name/label of the tempo. + */ + public tempoLabel: string = ''; + + /** + * Gets or sets a list of all masterbars contained in this song. + */ + public masterBars: MasterBar[] = []; + + /** + * Gets or sets a list of all tracks contained in this song. + */ + public tracks: Track[] = []; + + /** + * Gets or sets the rendering stylesheet for this song. + */ + public stylesheet: RenderStylesheet = new RenderStylesheet(); + + public static copyTo(src: Score, dst: Score): void { + dst.album = src.album; + dst.artist = src.artist; + dst.copyright = src.copyright; + dst.instructions = src.instructions; + dst.music = src.music; + dst.notices = src.notices; + dst.subTitle = src.subTitle; + dst.title = src.title; + dst.words = src.words; + dst.tab = src.tab; + dst.tempo = src.tempo; + dst.tempoLabel = src.tempoLabel; + } + + public rebuildRepeatGroups(): void { + let currentGroup: RepeatGroup = new RepeatGroup(); + for (let bar of this.masterBars) { + // if the group is closed only the next upcoming header can + // reopen the group in case of a repeat alternative, so we + // remove the current group + if (bar.isRepeatStart || (this._currentRepeatGroup.isClosed && bar.alternateEndings <= 0)) { + currentGroup = new RepeatGroup(); + } + currentGroup.addMasterBar(bar); + } + } + + public addMasterBar(bar: MasterBar): void { + bar.score = this; + bar.index = this.masterBars.length; + if (this.masterBars.length !== 0) { + bar.previousMasterBar = this.masterBars[this.masterBars.length - 1]; + bar.previousMasterBar.nextMasterBar = bar; + bar.start = bar.previousMasterBar.start + bar.previousMasterBar.calculateDuration(); + } + // if the group is closed only the next upcoming header can + // reopen the group in case of a repeat alternative, so we + // remove the current group + if (bar.isRepeatStart || (this._currentRepeatGroup.isClosed && bar.alternateEndings <= 0)) { + this._currentRepeatGroup = new RepeatGroup(); + } + this._currentRepeatGroup.addMasterBar(bar); + this.masterBars.push(bar); + } + + public addTrack(track: Track): void { + track.score = this; + track.index = this.tracks.length; + this.tracks.push(track); + } + + public finish(settings: Settings): void { + for (let i: number = 0, j: number = this.tracks.length; i < j; i++) { + this.tracks[i].finish(settings); + } + } +} diff --git a/src/model/Section.ts b/src/model/Section.ts new file mode 100644 index 000000000..fbebe9370 --- /dev/null +++ b/src/model/Section.ts @@ -0,0 +1,20 @@ +/** + * This public class is used to describe the beginning of a + * section within a song. It acts like a marker. + */ +export class Section { + /** + * Gets or sets the marker ID for this section. + */ + public marker: string = ''; + + /** + * Gets or sets the descriptional text of this section. + */ + public text: string = ''; + + public static copyTo(src: Section, dst: Section): void { + dst.marker = src.marker; + dst.text = src.text; + } +} diff --git a/src/model/SimileMark.ts b/src/model/SimileMark.ts new file mode 100644 index 000000000..f6c98896c --- /dev/null +++ b/src/model/SimileMark.ts @@ -0,0 +1,23 @@ +/** + * Lists all simile mark types as they are assigned to bars. + */ +export enum SimileMark { + /** + * No simile mark is applied + */ + None, + /** + * A simple simile mark. The previous bar is repeated. + */ + Simple, + /** + * A double simile mark. This value is assigned to the first + * bar of the 2 repeat bars. + */ + FirstOfDouble, + /** + * A double simile mark. This value is assigned to the second + * bar of the 2 repeat bars. + */ + SecondOfDouble +} diff --git a/src/model/SlideInType.ts b/src/model/SlideInType.ts new file mode 100644 index 000000000..479adfd46 --- /dev/null +++ b/src/model/SlideInType.ts @@ -0,0 +1,17 @@ +/** + * This public enum lists all different types of finger slide-ins on a string. + */ +export enum SlideInType { + /** + * No slide. + */ + None, + /** + * Slide into the note from below on the same string. + */ + IntoFromBelow, + /** + * Slide into the note from above on the same string. + */ + IntoFromAbove +} diff --git a/src/model/SlideOutType.ts b/src/model/SlideOutType.ts new file mode 100644 index 000000000..c1f5b333b --- /dev/null +++ b/src/model/SlideOutType.ts @@ -0,0 +1,33 @@ +/** + * This public enum lists all different types of finger slide-outs on a string. + */ +export enum SlideOutType { + /** + * No slide. + */ + None, + /** + * Shift slide to next note on same string + */ + Shift, + /** + * Legato slide to next note on same string. + */ + Legato, + /** + * Slide out from the note from upwards on the same string. + */ + OutUp, + /** + * Slide out from the note from downwards on the same string. + */ + OutDown, + /** + * Pickslide down on this note + */ + PickSlideDown, + /** + * Pickslide up on this note + */ + PickSlideUp +} diff --git a/src/model/Staff.ts b/src/model/Staff.ts new file mode 100644 index 000000000..ed0b61e83 --- /dev/null +++ b/src/model/Staff.ts @@ -0,0 +1,111 @@ +import { Bar } from '@src/model/Bar'; +import { Chord } from '@src/model/Chord'; +import { Track } from '@src/model/Track'; +import { Settings } from '@src/Settings'; + +/** + * This class describes a single staff within a track. There are instruments like pianos + * where a single track can contain multiple staffs. + */ +export class Staff { + /** + * Gets or sets the zero-based index of this staff within the track. + */ + public index: number = 0; + + /** + * Gets or sets the reference to the track this staff belongs to. + */ + public track!: Track; + + /** + * Gets or sets a list of all bars contained in this staff. + */ + public bars: Bar[] = []; + + /** + * Gets or sets a list of all chords defined for this staff. {@link Beat.chordId} refers to entries in this lookup. + */ + public chords: Map = new Map(); + + /** + * Gets or sets the fret on which a capo is set. s + */ + public capo: number = 0; + + /** + * Gets or sets the number of semitones this track should be + * transposed. This applies to rendering and playback. + */ + public transpositionPitch: number = 0; + + /** + * Gets or sets the number of semitones this track should be + * transposed. This applies only to rendering. + */ + public displayTranspositionPitch: number = 0; + + /** + * Get or set the guitar tuning of the guitar. This tuning also indicates the number of strings shown in the + * guitar tablature. Unlike the {@link Note.string} property this array directly represents + * the order of the tracks shown in the tablature. The first item is the most top tablature line. + */ + public tuning: number[] = []; + + /** + * Gets or sets the name of the tuning. + */ + public tuningName: string = ""; + + public get isStringed(): boolean { + return this.tuning.length > 0; + } + + /** + * Gets or sets whether the tabs are shown. + */ + public showTablature: boolean = true; + + /** + * Gets or sets whether the standard notation is shown. + */ + public showStandardNotation: boolean = true; + + /** + * Gets or sets whether the staff contains percussion notation + */ + public isPercussion: boolean = false; + + public static copyTo(src: Staff, dst: Staff): void { + dst.capo = src.capo; + dst.index = src.index; + dst.tuning = src.tuning.slice(); + dst.transpositionPitch = src.transpositionPitch; + dst.displayTranspositionPitch = src.displayTranspositionPitch; + dst.showStandardNotation = src.showStandardNotation; + dst.showTablature = src.showTablature; + dst.isPercussion = src.isPercussion; + } + + public finish(settings: Settings): void { + for (let i: number = 0, j: number = this.bars.length; i < j; i++) { + this.bars[i].finish(settings); + } + } + + public addChord(chordId: string, chord: Chord): void { + chord.staff = this; + this.chords.set(chordId, chord); + } + + public addBar(bar: Bar): void { + let bars: Bar[] = this.bars; + bar.staff = this; + bar.index = bars.length; + if (bars.length > 0) { + bar.previousBar = bars[bars.length - 1]; + bar.previousBar.nextBar = bar; + } + bars.push(bar); + } +} diff --git a/src/model/Track.ts b/src/model/Track.ts new file mode 100644 index 000000000..4c1655e5a --- /dev/null +++ b/src/model/Track.ts @@ -0,0 +1,110 @@ +import { Beat } from '@src/model/Beat'; +import { Color } from '@src/model/Color'; +import { Lyrics } from '@src/model/Lyrics'; +import { PlaybackInformation } from '@src/model/PlaybackInformation'; +import { Score } from '@src/model/Score'; +import { Staff } from '@src/model/Staff'; +import { Settings } from '@src/Settings'; + +/** + * This public class describes a single track or instrument of score. + * It is bascially a list of staffs containing individual music notation kinds. + */ +export class Track { + private static readonly ShortNameMaxLength: number = 10; + /** + * Gets or sets the zero-based index of this track. + */ + public index: number = 0; + + /** + * Gets or sets the reference this track belongs to. + */ + public score!: Score; + + /** + * Gets or sets the list of staffs that are defined for this track. + */ + public staves: Staff[] = []; + + /** + * Gets or sets the playback information for this track. + */ + public playbackInfo: PlaybackInformation = new PlaybackInformation(); + + /** + * Gets or sets the display color defined for this track. + */ + public color: Color = new Color(200, 0, 0, 255); + + /** + * Gets or sets the long name of this track. + */ + public name: string = ''; + + /** + * Gets or sets the short name of this track. + */ + public shortName: string = ''; + + public ensureStaveCount(staveCount: number): void { + while (this.staves.length < staveCount) { + this.addStaff(new Staff()); + } + } + + public addStaff(staff: Staff): void { + staff.index = this.staves.length; + staff.track = this; + this.staves.push(staff); + } + + public static copyTo(src: Track, dst: Track): void { + dst.name = src.name; + dst.shortName = src.shortName; + dst.index = src.index; + dst.color.raw = src.color.raw; + dst.color.rgba = src.color.rgba; + } + + public finish(settings: Settings): void { + if (!this.shortName) { + this.shortName = this.name; + if (this.shortName.length > Track.ShortNameMaxLength) { + this.shortName = this.shortName.substr(0, Track.ShortNameMaxLength); + } + } + for (let i: number = 0, j: number = this.staves.length; i < j; i++) { + this.staves[i].finish(settings); + } + } + + public applyLyrics(lyrics: Lyrics[]): void { + for (let lyric of lyrics) { + lyric.finish(); + } + let staff: Staff = this.staves[0]; + for (let li: number = 0; li < lyrics.length; li++) { + let lyric: Lyrics = lyrics[li]; + if (lyric.startBar >= 0) { + let beat: Beat | null = staff.bars[lyric.startBar].voices[0].beats[0]; + for (let ci: number = 0; ci < lyric.chunks.length && beat; ci++) { + // skip rests and empty beats + while (beat && (beat.isEmpty || beat.isRest)) { + beat = beat.nextBeat; + } + // mismatch between chunks and beats might lead to missing beats + if (beat) { + // initialize lyrics list for beat if required + if (!beat.lyrics) { + beat.lyrics = new Array(lyrics.length); + } + // assign chunk + beat.lyrics[li] = lyric.chunks[ci]; + beat = beat.nextBeat; + } + } + } + } + } +} diff --git a/src/model/TripletFeel.ts b/src/model/TripletFeel.ts new file mode 100644 index 000000000..e0db927e0 --- /dev/null +++ b/src/model/TripletFeel.ts @@ -0,0 +1,33 @@ +/** + * This public enumeration lists all feels of triplets. + */ +export enum TripletFeel { + /** + * No triplet feel + */ + NoTripletFeel, + /** + * Triplet 16th + */ + Triplet16th, + /** + * Triplet 8th + */ + Triplet8th, + /** + * Dotted 16th + */ + Dotted16th, + /** + * Dotted 8th + */ + Dotted8th, + /** + * Scottish 16th + */ + Scottish16th, + /** + * Scottish 8th + */ + Scottish8th +} diff --git a/src/model/Tuning.ts b/src/model/Tuning.ts new file mode 100644 index 000000000..d8f56f8b5 --- /dev/null +++ b/src/model/Tuning.ts @@ -0,0 +1,182 @@ +/** + * This public class represents a predefined string tuning. + */ +export class Tuning { + private static _sevenStrings: Tuning[] = []; + private static _sixStrings: Tuning[] = []; + private static _fiveStrings: Tuning[] = []; + private static _fourStrings: Tuning[] = []; + private static _defaultTunings: Map = new Map(); + + public static getTextForTuning(tuning: number, includeOctave: boolean): string { + let octave: number = (tuning / 12) | 0; + let note: number = tuning % 12; + let notes: string[] = ['C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab', 'A', 'Bb', 'B']; + let result: string = notes[note]; + if (includeOctave) { + result += octave - 1; + } + return result; + } + + /** + * Gets the default tuning for the given string count. + * @param stringCount The string count. + * @returns The tuning for the given string count or null if the string count is not defined. + */ + public static getDefaultTuningFor(stringCount: number): Tuning | null { + if (Tuning._defaultTunings.has(stringCount)) { + return Tuning._defaultTunings.get(stringCount)!; + } + return null; + } + + /** + * Gets a list of all tuning presets for a given stirng count. + * @param stringCount The string count. + * @returns The list of known tunings for the given string count or an empty list if the string count is not defined. + */ + public static getPresetsFor(stringCount: number): Tuning[] { + switch (stringCount) { + case 7: + return Tuning._sevenStrings; + case 6: + return Tuning._sixStrings; + case 5: + return Tuning._fiveStrings; + case 4: + return Tuning._fourStrings; + } + return []; + } + + public static initialize(): void { + Tuning._defaultTunings.set( + 7, + new Tuning('Guitar 7 strings', [64, 59, 55, 50, 45, 40, 35], true) + ); + + Tuning._sevenStrings.push(Tuning._defaultTunings.get(7)!); + Tuning._defaultTunings.set( + 6, + new Tuning('Guitar Standard Tuning', [64, 59, 55, 50, 45, 40], true) + ); + + Tuning._sixStrings.push(Tuning._defaultTunings.get(6)!); + Tuning._sixStrings.push(new Tuning('Guitar Tune down ½ step', [63, 58, 54, 49, 44, 39], false)); + Tuning._sixStrings.push(new Tuning('Guitar Tune down 1 step', [62, 57, 53, 48, 43, 38], false)); + Tuning._sixStrings.push(new Tuning('Guitar Tune down 2 step', [60, 55, 51, 46, 41, 36], false)); + Tuning._sixStrings.push(new Tuning('Guitar Dropped D Tuning', [64, 59, 55, 50, 45, 38], false)); + Tuning._sixStrings.push( + new Tuning('Guitar Dropped D Tuning variant', [64, 57, 55, 50, 45, 38], false) + ); + Tuning._sixStrings.push( + new Tuning('Guitar Double Dropped D Tuning', [62, 59, 55, 50, 45, 38], false) + ); + Tuning._sixStrings.push(new Tuning('Guitar Dropped E Tuning', [66, 61, 57, 52, 47, 40], false)); + Tuning._sixStrings.push(new Tuning('Guitar Dropped C Tuning', [62, 57, 53, 48, 43, 36], false)); + Tuning._sixStrings.push(new Tuning('Guitar Open C Tuning', [64, 60, 55, 48, 43, 36], false)); + Tuning._sixStrings.push(new Tuning('Guitar Open Cm Tuning', [63, 60, 55, 48, 43, 36], false)); + Tuning._sixStrings.push(new Tuning('Guitar Open C6 Tuning', [64, 57, 55, 48, 43, 36], false)); + Tuning._sixStrings.push( + new Tuning('Guitar Open Cmaj7 Tuning', [64, 59, 55, 52, 43, 36], false) + ); + Tuning._sixStrings.push(new Tuning('Guitar Open D Tuning', [62, 57, 54, 50, 45, 38], false)); + Tuning._sixStrings.push(new Tuning('Guitar Open Dm Tuning', [62, 57, 53, 50, 45, 38], false)); + Tuning._sixStrings.push(new Tuning('Guitar Open D5 Tuning', [62, 57, 50, 50, 45, 38], false)); + Tuning._sixStrings.push(new Tuning('Guitar Open D6 Tuning', [62, 59, 54, 50, 45, 38], false)); + Tuning._sixStrings.push( + new Tuning('Guitar Open Dsus4 Tuning', [62, 57, 55, 50, 45, 38], false) + ); + Tuning._sixStrings.push(new Tuning('Guitar Open E Tuning', [64, 59, 56, 52, 47, 40], false)); + Tuning._sixStrings.push(new Tuning('Guitar Open Em Tuning', [64, 59, 55, 52, 47, 40], false)); + Tuning._sixStrings.push( + new Tuning('Guitar Open Esus11 Tuning', [64, 59, 55, 52, 45, 40], false) + ); + Tuning._sixStrings.push(new Tuning('Guitar Open F Tuning', [65, 60, 53, 48, 45, 41], false)); + Tuning._sixStrings.push(new Tuning('Guitar Open G Tuning', [62, 59, 55, 50, 43, 38], false)); + Tuning._sixStrings.push(new Tuning('Guitar Open Gm Tuning', [62, 58, 55, 50, 43, 38], false)); + Tuning._sixStrings.push(new Tuning('Guitar Open G6 Tuning', [64, 59, 55, 50, 43, 38], false)); + Tuning._sixStrings.push( + new Tuning('Guitar Open Gsus4 Tuning', [62, 60, 55, 50, 43, 38], false) + ); + Tuning._sixStrings.push(new Tuning('Guitar Open A Tuning', [64, 61, 57, 52, 45, 40], false)); + Tuning._sixStrings.push(new Tuning('Guitar Open Am Tuning', [64, 60, 57, 52, 45, 40], false)); + Tuning._sixStrings.push(new Tuning('Guitar Nashville Tuning', [64, 59, 67, 62, 57, 52], false)); + Tuning._sixStrings.push(new Tuning('Bass 6 Strings Tuning', [48, 43, 38, 33, 28, 23], false)); + Tuning._sixStrings.push(new Tuning('Lute or Vihuela Tuning', [64, 59, 54, 50, 45, 40], false)); + + Tuning._defaultTunings.set(5, new Tuning('Bass 5 Strings Tuning', [43, 38, 33, 28, 23], true)); + Tuning._fiveStrings.push(Tuning._defaultTunings.get(5)!); + Tuning._fiveStrings.push(new Tuning('Banjo Dropped C Tuning', [62, 59, 55, 48, 67], false)); + Tuning._fiveStrings.push(new Tuning('Banjo Open D Tuning', [62, 57, 54, 50, 69], false)); + Tuning._fiveStrings.push(new Tuning('Banjo Open G Tuning', [62, 59, 55, 50, 67], false)); + Tuning._fiveStrings.push(new Tuning('Banjo G Minor Tuning', [62, 58, 55, 50, 67], false)); + Tuning._fiveStrings.push(new Tuning('Banjo G Modal Tuning', [62, 57, 55, 50, 67], false)); + + Tuning._defaultTunings.set(4, new Tuning('Bass Standard Tuning', [43, 38, 33, 28], true)); + Tuning._fourStrings.push(Tuning._defaultTunings.get(4)!); + Tuning._fourStrings.push(new Tuning('Bass Tune down ½ step', [42, 37, 32, 27], false)); + Tuning._fourStrings.push(new Tuning('Bass Tune down 1 step', [41, 36, 31, 26], false)); + Tuning._fourStrings.push(new Tuning('Bass Tune down 2 step', [39, 34, 29, 24], false)); + Tuning._fourStrings.push(new Tuning('Bass Dropped D Tuning', [43, 38, 33, 26], false)); + Tuning._fourStrings.push(new Tuning('Ukulele C Tuning', [45, 40, 36, 43], false)); + Tuning._fourStrings.push(new Tuning('Ukulele G Tuning', [52, 47, 43, 38], false)); + Tuning._fourStrings.push(new Tuning('Mandolin Standard Tuning', [64, 57, 50, 43], false)); + Tuning._fourStrings.push(new Tuning('Mandolin or Violin Tuning', [76, 69, 62, 55], false)); + Tuning._fourStrings.push(new Tuning('Viola Tuning', [69, 62, 55, 48], false)); + Tuning._fourStrings.push(new Tuning('Cello Tuning', [57, 50, 43, 36], false)); + } + + /** + * Tries to find a known tuning by a given list of tuning values. + * @param strings The values defining the tuning. + * @returns The known tuning. + */ + public static findTuning(strings: number[]): Tuning | null { + let tunings: Tuning[] = Tuning.getPresetsFor(strings.length); + for (let t: number = 0, tc: number = tunings.length; t < tc; t++) { + let tuning: Tuning = tunings[t]; + let equals: boolean = true; + for (let i: number = 0, j: number = strings.length; i < j; i++) { + if (strings[i] !== tuning.tunings[i]) { + equals = false; + break; + } + } + if (equals) { + return tuning; + } + } + return null; + } + + /** + * Gets or sets whether this is the standard tuning for this number of strings. + */ + public isStandard: boolean; + + /** + * Gets or sets the name of the tuning. + */ + public name: string; + + /** + * Gets or sets the values for each string of the instrument. + */ + public tunings: number[]; + + /** + * Initializes a new instance of the {@link Tuning} class. + * @param name The name. + * @param tuning The tuning. + * @param isStandard if set to`true`[is standard]. + */ + public constructor(name: string, tuning: number[], isStandard: boolean) { + this.isStandard = isStandard; + this.name = name; + this.tunings = tuning; + } +} + +Tuning.initialize(); diff --git a/src/model/TupletGroup.ts b/src/model/TupletGroup.ts new file mode 100644 index 000000000..40904ef7d --- /dev/null +++ b/src/model/TupletGroup.ts @@ -0,0 +1,104 @@ +import { Beat } from '@src/model/Beat'; +import { GraceType } from '@src/model/GraceType'; +import { Voice } from '@src/model/Voice'; + +/** + * Represents a list of beats that are grouped within the same tuplet. + */ +export class TupletGroup { + private static readonly HalfTicks: number = 1920; + private static readonly QuarterTicks: number = 960; + private static readonly EighthTicks: number = 480; + private static readonly SixteenthTicks: number = 240; + private static readonly ThirtySecondTicks: number = 120; + private static readonly SixtyFourthTicks: number = 60; + private static readonly OneHundredTwentyEighthTicks: number = 30; + private static readonly TwoHundredFiftySixthTicks: number = 15; + + private static AllTicks: number[] = [ + TupletGroup.HalfTicks, + TupletGroup.QuarterTicks, + TupletGroup.EighthTicks, + TupletGroup.SixteenthTicks, + TupletGroup.ThirtySecondTicks, + TupletGroup.SixtyFourthTicks, + TupletGroup.OneHundredTwentyEighthTicks, + TupletGroup.TwoHundredFiftySixthTicks + ]; + + private _isEqualLengthTuplet: boolean = true; + + public totalDuration: number = 0; + + /** + * Gets or sets the list of beats contained in this group. + */ + public beats: Beat[] = []; + + /** + * Gets or sets the voice this group belongs to. + */ + public voice: Voice; + + /** + * Gets a value indicating whether the tuplet group is fully filled. + */ + public isFull: boolean = false; + + /** + * Initializes a new instance of the {@link TupletGroup} class. + * @param voice The voice this group belongs to. + */ + public constructor(voice: Voice) { + this.voice = voice; + } + + public check(beat: Beat): boolean { + if (this.beats.length === 0) { + // accept first beat + this.beats.push(beat); + this.totalDuration += beat.playbackDuration; + return true; + } + if (beat.graceType !== GraceType.None) { + // grace notes do not break tuplet group, but also do not contribute to them. + return true; + } + if ( + beat.voice !== this.voice || + this.isFull || + beat.tupletNumerator !== this.beats[0].tupletNumerator || + beat.tupletDenominator !== this.beats[0].tupletDenominator + ) { + // only same tuplets are potentially accepted + return false; + } + // TBH: I do not really know how the 100% tuplet grouping of Guitar Pro might work + // it sometimes has really strange rules where notes filling 3 quarters, are considered a full 3:2 tuplet + // in alphaTab we have now 2 rules where we consider a tuplet full: + // 1. if all beats have the same length, the tuplet must contain N notes of an N:M tuplet + // 2. if we have mixed beats, we check if the current set of beats, matches a N:M tuplet + // by checking all potential note durations. + // this logic is very likely not 100% correct but for most cases the tuplets + // appeared correct. + if (beat.playbackDuration !== this.beats[0].playbackDuration) { + this._isEqualLengthTuplet = false; + } + this.beats.push(beat); + this.totalDuration += beat.playbackDuration; + if (this._isEqualLengthTuplet) { + if (this.beats.length === this.beats[0].tupletNumerator) { + this.isFull = true; + } + } else { + let factor: number = (this.beats[0].tupletNumerator / this.beats[0].tupletDenominator) | 0; + for (let potentialMatch of TupletGroup.AllTicks) { + if (this.totalDuration === potentialMatch * factor) { + this.isFull = true; + break; + } + } + } + return true; + } +} diff --git a/src/model/VibratoType.ts b/src/model/VibratoType.ts new file mode 100644 index 000000000..e40d882a4 --- /dev/null +++ b/src/model/VibratoType.ts @@ -0,0 +1,17 @@ +/** + * This public enum lists all vibrato types that can be performed. + */ +export enum VibratoType { + /** + * No vibrato. + */ + None, + /** + * A slight vibrato. + */ + Slight, + /** + * A wide vibrato. + */ + Wide +} diff --git a/src/model/Voice.ts b/src/model/Voice.ts new file mode 100644 index 000000000..b29438c1b --- /dev/null +++ b/src/model/Voice.ts @@ -0,0 +1,181 @@ +import { MidiUtils } from '@src/midi/MidiUtils'; +import { Bar } from '@src/model/Bar'; +import { Beat } from '@src/model/Beat'; +import { Duration } from '@src/model/Duration'; +import { GraceType } from '@src/model/GraceType'; +import { Settings } from '@src/Settings'; + +/** + * A voice represents a group of beats + * that can be played during a bar. + */ +export class Voice { + private _beatLookup!: Map; + + /** + * Gets or sets the zero-based index of this voice within the bar. + */ + public index: number = 0; + + /** + * Gets or sets the reference to the bar this voice belongs to. + */ + public bar!: Bar; + + /** + * Gets or sets the list of beats contained in this voice. + */ + public beats: Beat[] = []; + + /** + * Gets or sets a value indicating whether this voice is empty. + */ + public isEmpty: boolean = true; + + public static copyTo(src: Voice, dst: Voice): void { + dst.index = src.index; + dst.isEmpty = src.isEmpty; + } + + public insertBeat(after: Beat, newBeat: Beat): void { + newBeat.nextBeat = after.nextBeat; + if (newBeat.nextBeat) { + newBeat.nextBeat.previousBeat = newBeat; + } + newBeat.previousBeat = after; + newBeat.voice = this; + after.nextBeat = newBeat; + this.beats.splice(after.index + 1, 0, newBeat); + } + + public addBeat(beat: Beat): void { + beat.voice = this; + beat.index = this.beats.length; + this.beats.push(beat); + if (!beat.isEmpty) { + this.isEmpty = false; + } + } + + private chain(beat: Beat): void { + if (!this.bar) { + return; + } + if (beat.index < this.beats.length - 1) { + beat.nextBeat = this.beats[beat.index + 1]; + beat.nextBeat.previousBeat = beat; + } else if (beat.isLastOfVoice && beat.voice.bar.nextBar) { + let nextVoice: Voice = this.bar.nextBar!.voices[this.index]; + if (nextVoice.beats.length > 0) { + beat.nextBeat = nextVoice.beats[0]; + beat.nextBeat.previousBeat = beat; + } else { + beat.nextBeat!.previousBeat = beat; + } + } + + beat.chain(); + } + + public addGraceBeat(beat: Beat): void { + if (this.beats.length === 0) { + this.addBeat(beat); + return; + } + // remove last beat + let lastBeat: Beat = this.beats[this.beats.length - 1]; + this.beats.splice(this.beats.length - 1, 1); + // insert grace beat + this.addBeat(beat); + // reinsert last beat + this.addBeat(lastBeat); + this.isEmpty = false; + } + + public getBeatAtDisplayStart(displayStart: number): Beat | null { + if (this._beatLookup.has(displayStart)) { + return this._beatLookup.get(displayStart)!; + } + return null; + } + + public finish(settings: Settings): void { + this._beatLookup = new Map(); + for (let index: number = 0; index < this.beats.length; index++) { + let beat: Beat = this.beats[index]; + beat.index = index; + this.chain(beat); + } + let currentDisplayTick: number = 0; + let currentPlaybackTick: number = 0; + for (let i: number = 0; i < this.beats.length; i++) { + let beat: Beat = this.beats[i]; + beat.index = i; + beat.finish(settings); + if (beat.graceType === GraceType.None || beat.graceType === GraceType.BendGrace) { + beat.displayStart = currentDisplayTick; + beat.playbackStart = currentPlaybackTick; + currentDisplayTick += beat.displayDuration; + currentPlaybackTick += beat.playbackDuration; + } else { + if (!beat.previousBeat || beat.previousBeat.graceType === GraceType.None) { + // find note which is not a grace note + let nonGrace: Beat | null = beat; + let numberOfGraceBeats: number = 0; + while (nonGrace && nonGrace.graceType !== GraceType.None) { + nonGrace = nonGrace.nextBeat; + numberOfGraceBeats++; + } + let graceDuration: Duration = Duration.Eighth; + let stolenDuration: number = 0; + if (numberOfGraceBeats === 1) { + graceDuration = Duration.Eighth; + } else if (numberOfGraceBeats === 2) { + graceDuration = Duration.Sixteenth; + } else { + graceDuration = Duration.ThirtySecond; + } + if (nonGrace) { + nonGrace.updateDurations(); + } + // grace beats have 1/4 size of the non grace beat preceeding them + let perGraceDisplayDuration: number = !beat.previousBeat + ? MidiUtils.toTicks(Duration.ThirtySecond) + : (((beat.previousBeat.displayDuration / 4) | 0) / numberOfGraceBeats) | 0; + // move all grace beats + let graceBeat: Beat | null = this.beats[i]; + for (let j: number = 0; j < numberOfGraceBeats && graceBeat; j++) { + graceBeat.duration = graceDuration; + graceBeat.updateDurations(); + graceBeat.displayStart = + currentDisplayTick - (numberOfGraceBeats - j + 1) * perGraceDisplayDuration; + graceBeat.displayDuration = perGraceDisplayDuration; + stolenDuration += graceBeat.playbackDuration; + graceBeat = graceBeat.nextBeat; + } + // steal needed duration from beat duration + if (beat.graceType === GraceType.BeforeBeat) { + if (beat.previousBeat) { + beat.previousBeat.playbackDuration -= stolenDuration; + } + currentPlaybackTick -= stolenDuration; + } else if (nonGrace && beat.graceType === GraceType.OnBeat) { + nonGrace.playbackDuration -= stolenDuration; + } + } + beat.playbackStart = currentPlaybackTick; + currentPlaybackTick = beat.playbackStart + beat.playbackDuration; + } + beat.finishTuplet(); + this._beatLookup.set(beat.displayStart, beat); + } + } + + public calculateDuration(): number { + if (this.isEmpty || this.beats.length === 0) { + return 0; + } + let lastBeat: Beat = this.beats[this.beats.length - 1]; + return lastBeat.playbackStart + lastBeat.playbackDuration; + } +} diff --git a/src/model/WhammyType.ts b/src/model/WhammyType.ts new file mode 100644 index 000000000..0ae38af9e --- /dev/null +++ b/src/model/WhammyType.ts @@ -0,0 +1,35 @@ +/** + * Lists all types of whammy bars + */ +export enum WhammyType { + /** + * No whammy at all + */ + None, + /** + * Individual points define the whammy in a flexible manner. + * This system was mainly used in Guitar Pro 3-5 + */ + Custom, + /** + * Simple dive to a lower or higher note. + */ + Dive, + /** + * A dive to a lower or higher note and releasing it back to normal. + */ + Dip, + /** + * Continue to hold the whammy at the position from a previous whammy. + */ + Hold, + /** + * Dive to a lower or higher note before playing it. + */ + Predive, + /** + * Dive to a lower or higher note before playing it, then change to another + * note. + */ + PrediveDive +} diff --git a/src/platform/Cursors.ts b/src/platform/Cursors.ts new file mode 100644 index 000000000..fa2bd4e5c --- /dev/null +++ b/src/platform/Cursors.ts @@ -0,0 +1,45 @@ +import { IContainer } from '@src/platform/IContainer'; + +/** + * This wrapper holds all cursor related elements. + */ +export class Cursors { + /** + * Gets the element that spans across the whole music sheet and holds the other cursor elements. + */ + public readonly cursorWrapper: IContainer; + + /** + * Gets the element that is positioned above the bar that is currently played. + */ + public readonly barCursor: IContainer; + + /** + * Gets the element that is positioned above the beat that is currently played. + */ + public readonly beatCursor: IContainer; + + /** + * Gets the element that spans across the whole music sheet and will hold any selection related elements. + */ + public readonly selectionWrapper: IContainer; + + /** + * Initializes a new instance of the {@link Cursors} class. + * @param cursorWrapper + * @param barCursor + * @param beatCursor + * @param selectionWrapper + */ + public constructor( + cursorWrapper: IContainer, + barCursor: IContainer, + beatCursor: IContainer, + selectionWrapper: IContainer + ) { + this.cursorWrapper = cursorWrapper; + this.barCursor = barCursor; + this.beatCursor = beatCursor; + this.selectionWrapper = selectionWrapper; + } +} diff --git a/src/platform/ICanvas.ts b/src/platform/ICanvas.ts new file mode 100644 index 000000000..595ba46ca --- /dev/null +++ b/src/platform/ICanvas.ts @@ -0,0 +1,104 @@ +import { Color } from '@src/model/Color'; +import { Font } from '@src/model/Font'; +import { MusicFontSymbol } from '@src/rendering/glyphs/MusicFontSymbol'; +import { Settings } from '@src/Settings'; + +/** + * This public enum lists all different text alignments + */ +export enum TextAlign { + /** + * Text is left aligned. + */ + Left, + /** + * Text is centered. + */ Center, + /** + * Text is right aligned. + */ Right +} + +/** + * This public enum lists all base line modes + */ +export enum TextBaseline { + /** + * Text is aligned on top. + */ + Top, + /** + * Text is aligned middle + */ Middle, + /** + * Text is aligend on the bottom. + */ Bottom +} + +/** + * This is the base public interface for canvas implementations on different plattforms. + */ +export interface ICanvas { + settings: Settings; + + color: Color; + + lineWidth: number; + + fillRect(x: number, y: number, w: number, h: number): void; + + strokeRect(x: number, y: number, w: number, h: number): void; + + fillCircle(x: number, y: number, radius: number): void; + strokeCircle(x: number, y: number, radius: number): void; + + font: Font; + + textAlign: TextAlign; + + textBaseline: TextBaseline; + + beginGroup(identifier: string): void; + + endGroup(): void; + + fillText(text: string, x: number, y: number): void; + + measureText(text: string): number; + + fillMusicFontSymbol(x: number, y: number, scale: number, symbol: MusicFontSymbol, centerAtPosition?: boolean): void; + + fillMusicFontSymbols( + x: number, + y: number, + scale: number, + symbols: MusicFontSymbol[], + centerAtPosition?: boolean + ): void; + + beginRender(width: number, height: number): void; + + endRender(): unknown; + + onRenderFinished(): unknown; + + beginRotate(centerX: number, centerY: number, angle: number): void; + + endRotate(): void; + + beginPath(): void; + + closePath(): void; + + fill(): void; + + stroke(): void; + + moveTo(x: number, y: number): void; + + lineTo(x: number, y: number): void; + + bezierCurveTo(cp1X: number, cp1Y: number, cp2X: number, cp2Y: number, x: number, y: number): void; + + quadraticCurveTo(cpx: number, cpy: number, x: number, y: number): void; +} diff --git a/src/platform/IContainer.ts b/src/platform/IContainer.ts new file mode 100644 index 000000000..53969089f --- /dev/null +++ b/src/platform/IContainer.ts @@ -0,0 +1,90 @@ +import { IEventEmitter, IEventEmitterOfT } from '@src/EventEmitter'; +import { IMouseEventArgs } from '@src/platform/IMouseEventArgs'; + +/** + * This interface represents a container control in the UI layer. + */ +export interface IContainer { + /** + * Gets or sets the Y-position of the control, relative to its parent. + */ + top: number; + + /** + * Gets or sets the X-position of the control, relative to its parent. + */ + left: number; + + /** + * Gets or sets the width of the control. + */ + width: number; + + /** + * Gets or sets the height of the control. + */ + height: number; + + /** + * Gets a value indicating whether the control is visible. + */ + readonly isVisible: boolean; + + /** + * Gets or sets the horizontal scroll offset of this control if it is scrollable. + */ + scrollLeft: number; + + /** + * Gets or sets the vertical scroll offset of this control if it is scrollable. + */ + scrollTop: number; + + /** + * Adds the given child control to this container. + * @param child The child control to add. + */ + appendChild(child: IContainer): void; + + /** + * Stops the animations of this control immediately. + */ + stopAnimation(): void; + + /** + * Tells the control to move to the given X-position in the given time. + * @param duration The milliseconds that should be needed to reach the new X-position + * @param x The new X-position + */ + transitionToX(duration: number, x: number): void; + + /** + * Clears the container and removes all child items. + */ + clear(): void; + + /** + * This event occurs when a scroll on the control happened. + */ + scroll: IEventEmitter; + + /** + * This event occurs when the control was resized. + */ + resize: IEventEmitter; + + /** + * This event occurs when a mouse/finger press happened on the control. + */ + mouseDown: IEventEmitterOfT; + + /** + * This event occurs when a mouse/finger moves on top of the control. + */ + mouseMove: IEventEmitterOfT + + /** + * This event occurs when a mouse/finger is released from the control. + */ + mouseUp: IEventEmitterOfT +} diff --git a/src/platform/IMouseEventArgs.ts b/src/platform/IMouseEventArgs.ts new file mode 100644 index 000000000..964f41588 --- /dev/null +++ b/src/platform/IMouseEventArgs.ts @@ -0,0 +1,30 @@ +import { IContainer } from '@src/platform/IContainer'; + +/** + * This interface represents the information about a mouse event that occured on the UI. + */ +export interface IMouseEventArgs { + /** + * Gets a value indicating whether the left mouse button was pressed. + */ + readonly isLeftMouseButton: boolean; + + /** + * Gets the X-position of the cursor at the time of the event relative to the given UI container. + * @param relativeTo The UI element to which the relative position should be calculated. + * @returns The relative X-position of the cursor to the given UI container at the time the event occured. + */ + getX(relativeTo: IContainer): number; + + /** + * Gets the Y-position of the cursor at the time of the event relative to the given UI container. + * @param relativeTo The UI element to which the relative position should be calculated. + * @returns The relative Y-position of the cursor to the given UI container at the time the event occured. + */ + getY(relativeTo: IContainer): number; + + /** + * If called, the original mouse action is prevented and the event is flagged as handled. + */ + preventDefault(): void; +} diff --git a/src/platform/IUiFacade.ts b/src/platform/IUiFacade.ts new file mode 100644 index 000000000..5916be022 --- /dev/null +++ b/src/platform/IUiFacade.ts @@ -0,0 +1,177 @@ +import { AlphaTabApiBase } from '@src/AlphaTabApiBase'; +import { IAlphaSynth } from '@src/synth/IAlphaSynth'; +import { IEventEmitter } from '@src/EventEmitter'; +import { Score } from '@src/model/Score'; +import { IContainer } from '@src/platform/IContainer'; +import { IMouseEventArgs } from '@src/platform/IMouseEventArgs'; +import { Cursors } from '@src/platform/Cursors'; +import { IScoreRenderer } from '@src/rendering/IScoreRenderer'; +import { RenderFinishedEventArgs } from '@src/rendering/RenderFinishedEventArgs'; +import { Bounds } from '@src/rendering/utils/Bounds'; + +/** + * This interface represents the UI abstraction between alphaTab and the corresponding UI framework being used. + * @param The type of that holds the settings passed from the UI layer. + */ +export interface IUiFacade { + /** + * Gets the root UI element that holds the whole alphaTab control. + */ + readonly rootContainer: IContainer; + + /** + * Gets a value indicating whether the UI framework supports worker based rendering. + */ + readonly areWorkersSupported: boolean; + + /** + * Gets or sets whether the UI is ready to render the music notation. On some platforms where pre-loading of assets is done asynchronously, + * rendering might need to be deferred. + */ + readonly canRender: boolean; + + /** + * Gets the resize throttling in milliseconds. Then the music sheet is resized, the re-rendering is deferred until this timeout is reached. + */ + readonly resizeThrottle: number; + + /** + * Initializes the UI using the given alphaTab API and settings object. + * @param api The alphaTab API wrapper responsible for UI interaction. + * @param settings The settings object holding the settings from the UI layer. + */ + initialize(api: AlphaTabApiBase, settings: TSettings): void; + + /** + * Tells the UI layer to destroy the alphaTab controls and restore the initial state. + */ + destroy(): void; + + /** + * Creates the canvas element that wraps all individually rendered partials. + * @returns The canvas element that wraps all individually rendered partials. + */ + createCanvasElement(): IContainer; + + /** + * Tells the UI layer to trigger an event with the given name and details. + * @param container The element on which the event should be triggered. + * @param eventName The event that should be triggered. + * @param details The object holding the details about the event. + * @param originalEvent The original event related to this custom event. + */ + triggerEvent(container: IContainer, eventName: string, details: unknown, originalEvent?: IMouseEventArgs): void; + + /** + * Tells the UI layer to do the initial rendering. + */ + initialRender(): void; + + /** + * Tells the UI layer to append the given render results to the UI. + * @param renderResults The rendered partial that should be added to the UI. + */ + beginAppendRenderResults(renderResults: RenderFinishedEventArgs | null): void; + + /** + * Tells the UI layer to create the worker renderer. This method is the UI layer supports worker rendering and worker rendering is not disabled via setting. + * @returns + */ + createWorkerRenderer(): IScoreRenderer; + + /** + * Tells the UI layer to create a player worker. + * @returns + */ + createWorkerPlayer(): IAlphaSynth | null; + + /** + * Creates the cursor objects that are used to highlight the currently played beats and bars. + * @returns + */ + createCursors(): Cursors | null; + + /** + * Destroys the cursor objects that are used to highlight the currently played beats and bars. + */ + destroyCursors(): void; + + /** + * Tells the UI layer to invoke the given action. + * @param action + */ + beginInvoke(action: () => void): void; + + /** + * Tells the UI layer to remove all highlights from highlighted music notation elements. + */ + removeHighlights(): void; + + /** + * Tells the UI layer to highlight the music notation elements with the given ID. + * @param groupId The group id that identifies the elements to be highlighted. + */ + highlightElements(groupId: string): void; + + /** + * Creates a new UI element that is used to display the selection rectangle. + * @returns + */ + createSelectionElement(): IContainer | null; + + /** + * Gets the UI element that is used for scrolling during playback. + * @returns + */ + getScrollContainer(): IContainer; + + /** + * Calculates the relative offset of a container to the scroll element. + * @param scrollElement The parent scroll element to which the relative position is computed. + * @param container The container element for which the relative position is calculated. + * @returns + */ + getOffset(scrollElement: IContainer | null, container: IContainer): Bounds; + + /** + * Initiates a vertical scroll on the given element. + * @param scrollElement The element on which the scrolling should happen. + * @param offset The absolute scroll offset to which scrolling should happen. + * @param speed How fast the scrolling from the current offset to the given one should happen in milliseconds. + */ + scrollToY(scrollElement: IContainer, offset: number, speed: number): void; + + /** + * Initiates a horizontal scroll on the given element. + * @param scrollElement The element on which the scrolling should happen. + * @param offset The absolute scroll offset to which scrolling should happen. + * @param speed How fast the scrolling from the current offset to the given one should happen in milliseconds. + */ + scrollToX(scrollElement: IContainer, offset: number, speed: number): void; + + /** + * Attempts a load of the score represented by the given data object. + * @param data The data object to decode + * @param success The action to call if the score was loaded + * @param error The action to call if any error during loading ocurred. + * @returns true if the data object is supported and a load was initiated, otherwise false + */ + load(data: unknown, success: (score: Score) => void, error: (error: Error) => void): boolean; + + /** + * Attempts a load of the score represented by the given data object. + * @param data The data object to decode + * @returns true if the data object is supported and a load was initiated, otherwise false + */ + loadSoundFont(data: unknown): boolean; + + /** + * This events is fired when the {@link canRender} property changes. + */ + readonly canRenderChanged: IEventEmitter; + + /** + * This event is fired when {@link rootContainer} became visible when it was invisible at the time rendering was initiated. + */ + readonly rootContainerBecameVisible: IEventEmitter; +} diff --git a/src/platform/javascript/AlphaSynthWebAudioOutput.ts b/src/platform/javascript/AlphaSynthWebAudioOutput.ts new file mode 100644 index 000000000..db5dfb8a6 --- /dev/null +++ b/src/platform/javascript/AlphaSynthWebAudioOutput.ts @@ -0,0 +1,175 @@ +import { CircularSampleBuffer } from '@src/synth/ds/CircularSampleBuffer'; +import { ISynthOutput } from '@src/synth/ISynthOutput'; +import { EventEmitter, IEventEmitterOfT, IEventEmitter, EventEmitterOfT } from '@src/EventEmitter'; +import { Environment } from '@src/Environment'; +import { AlphaTabError, AlphaTabErrorType } from '@src/AlphaTabError'; + +declare var webkitAudioContext: any; + +/** + * This class implements a HTML5 Web Audio API based audio output device + * for alphaSynth. It can be controlled via a JS API. + * @target web + */ +export class AlphaSynthWebAudioOutput implements ISynthOutput { + private static readonly BufferSize: number = 4096; + private static readonly BufferCount: number = 10; + private static readonly PreferredSampleRate: number = 44100; + + private _context: AudioContext | null = null; + private _buffer: AudioBuffer | null = null; + private _source: AudioBufferSourceNode | null = null; + private _audioNode: ScriptProcessorNode | null = null; + private _circularBuffer!: CircularSampleBuffer; + private _finished: boolean = false; + + public get sampleRate(): number { + return this._context ? this._context.sampleRate : AlphaSynthWebAudioOutput.PreferredSampleRate; + } + + public open(): void { + this._finished = false; + this.patchIosSampleRate(); + this._circularBuffer = new CircularSampleBuffer( + AlphaSynthWebAudioOutput.BufferSize * AlphaSynthWebAudioOutput.BufferCount + ); + this._context = this.createAudioContext(); + // possible fix for Web Audio in iOS 9 (issue #4) + let ctx: any = this._context; + if (ctx.state === 'suspended') { + let resume = () => { + ctx.resume(); + window.setTimeout(() => { + if (ctx.state === 'running') { + document.body.removeEventListener('touchend', resume, false); + document.body.removeEventListener('click', resume, false); + } + }, 0); + }; + document.body.addEventListener('touchend', resume, false); + document.body.addEventListener('click', resume, false); + } + (this.ready as EventEmitter).trigger(); + } + + public activate(): void { + if (!this._context) { + this._context = this.createAudioContext(); + } + + // tslint:disable-next-line: no-floating-promises + this._context.resume(); + } + + private patchIosSampleRate(): void { + let ua: string = navigator.userAgent; + if (ua.indexOf('iPhone') !== -1 || ua.indexOf('iPad') !== 0) { + let context: AudioContext = this.createAudioContext(); + let buffer: AudioBuffer = context.createBuffer(1, 1, AlphaSynthWebAudioOutput.PreferredSampleRate); + let dummy: AudioBufferSourceNode = context.createBufferSource(); + dummy.buffer = buffer; + dummy.connect(context.destination); + dummy.start(0); + dummy.disconnect(0); + // tslint:disable-next-line: no-floating-promises + context.close(); + } + } + + private createAudioContext(): AudioContext { + if('AudioContext' in Environment.globalThis) { + return new AudioContext(); + } else if('webkitAudioContext' in Environment.globalThis) { + return new webkitAudioContext(); + } + throw new AlphaTabError(AlphaTabErrorType.General, "AudioContext not found"); + } + + public play(): void { + let ctx = this._context; + if (!ctx) { + return; + } + if (ctx.state === 'suspended' || (ctx.state as string) === 'interrupted') { + // tslint:disable-next-line: no-floating-promises + ctx.resume(); + } + // create an empty buffer source (silence) + this._buffer = ctx.createBuffer(2, 4096, ctx.sampleRate); + // create a script processor node which will replace the silence with the generated audio + this._audioNode = ctx.createScriptProcessor(4096, 0, 2); + this._audioNode.onaudioprocess = this.generateSound.bind(this); + this._circularBuffer.clear(); + this.requestBuffers(); + this._finished = false; + this._source = ctx.createBufferSource(); + this._source.buffer = this._buffer; + this._source.loop = true; + this._source.connect(this._audioNode, 0, 0); + this._source.start(0); + this._audioNode.connect(ctx.destination, 0, 0); + } + + public pause(): void { + if (this._source) { + this._source.stop(0); + this._source.disconnect(0); + } + this._source = null; + if (this._audioNode) { + this._audioNode.disconnect(0); + } + this._audioNode = null; + } + + public sequencerFinished(): void { + this._finished = true; + } + + public addSamples(f: Float32Array): void { + this._circularBuffer.write(f, 0, f.length); + } + + public resetSamples(): void { + this._circularBuffer.clear(); + } + + private requestBuffers(): void { + // if we fall under the half of buffers + // we request one half + let count: number = ((10 / 2) | 0) * 4096; + if (this._circularBuffer.count < count && this.sampleRequest) { + for (let i: number = 0; i < ((10 / 2) | 0); i++) { + (this.sampleRequest as EventEmitter).trigger(); + } + } + } + + private generateSound(e: AudioProcessingEvent): void { + let left: Float32Array = e.outputBuffer.getChannelData(0); + let right: Float32Array = e.outputBuffer.getChannelData(1); + let samples: number = left.length + right.length; + if (this._circularBuffer.count < samples) { + if (this._finished) { + (this.finished as EventEmitter).trigger(); + } + } else { + let buffer: Float32Array = new Float32Array(samples); + this._circularBuffer.read(buffer, 0, buffer.length); + let s: number = 0; + for (let i: number = 0; i < left.length; i++) { + left[i] = buffer[s++]; + right[i] = buffer[s++]; + } + (this.samplesPlayed as EventEmitterOfT).trigger(left.length); + } + if (!this._finished) { + this.requestBuffers(); + } + } + + readonly ready: IEventEmitter = new EventEmitter(); + readonly samplesPlayed: IEventEmitterOfT = new EventEmitterOfT(); + readonly sampleRequest: IEventEmitter = new EventEmitter(); + readonly finished: IEventEmitter = new EventEmitter(); +} diff --git a/src/platform/javascript/AlphaSynthWebWorker.ts b/src/platform/javascript/AlphaSynthWebWorker.ts new file mode 100644 index 000000000..55fcbd19b --- /dev/null +++ b/src/platform/javascript/AlphaSynthWebWorker.ts @@ -0,0 +1,214 @@ +import { AlphaSynth } from '@src/synth/AlphaSynth'; +import { PlayerStateChangedEventArgs } from '@src/synth/PlayerStateChangedEventArgs'; +import { PositionChangedEventArgs } from '@src/synth/PositionChangedEventArgs'; +import { JsonConverter } from '@src/model/JsonConverter'; +import { AlphaSynthWorkerSynthOutput } from '@src/platform/javascript/AlphaSynthWorkerSynthOutput'; +import { IWorkerScope } from '@src/platform/javascript/IWorkerScope'; +import { Logger } from '@src/Logger'; +import { Environment } from '@src/Environment'; + +/** + * This class implements a HTML5 WebWorker based version of alphaSynth + * which can be controlled via WebWorker messages. + * @target web + */ +export class AlphaSynthWebWorker { + public static readonly CmdPrefix: string = 'alphaSynth.'; + public static readonly CmdInitialize: string = AlphaSynthWebWorker.CmdPrefix + 'initialize'; + public static readonly CmdSetLogLevel: string = AlphaSynthWebWorker.CmdPrefix + 'setLogLevel'; + public static readonly CmdSetMasterVolume: string = AlphaSynthWebWorker.CmdPrefix + 'setMasterVolume'; + public static readonly CmdSetMetronomeVolume: string = AlphaSynthWebWorker.CmdPrefix + 'setMetronomeVolume'; + public static readonly CmdSetPlaybackSpeed: string = AlphaSynthWebWorker.CmdPrefix + 'setPlaybackSpeed'; + public static readonly CmdSetTickPosition: string = AlphaSynthWebWorker.CmdPrefix + 'setTickPosition'; + public static readonly CmdSetTimePosition: string = AlphaSynthWebWorker.CmdPrefix + 'setTimePosition'; + public static readonly CmdSetPlaybackRange: string = AlphaSynthWebWorker.CmdPrefix + 'setPlaybackRange'; + public static readonly CmdSetIsLooping: string = AlphaSynthWebWorker.CmdPrefix + 'setIsLooping'; + public static readonly CmdPlay: string = AlphaSynthWebWorker.CmdPrefix + 'play'; + public static readonly CmdPause: string = AlphaSynthWebWorker.CmdPrefix + 'pause'; + public static readonly CmdPlayPause: string = AlphaSynthWebWorker.CmdPrefix + 'playPause'; + public static readonly CmdStop: string = AlphaSynthWebWorker.CmdPrefix + 'stop'; + public static readonly CmdLoadSoundFontBytes: string = AlphaSynthWebWorker.CmdPrefix + 'loadSoundFontBytes'; + public static readonly CmdLoadMidi: string = AlphaSynthWebWorker.CmdPrefix + 'loadMidi'; + public static readonly CmdSetChannelMute: string = AlphaSynthWebWorker.CmdPrefix + 'setChannelMute'; + public static readonly CmdSetChannelSolo: string = AlphaSynthWebWorker.CmdPrefix + 'setChannelSolo'; + public static readonly CmdSetChannelVolume: string = AlphaSynthWebWorker.CmdPrefix + 'setChannelVolume'; + public static readonly CmdResetChannelStates: string = AlphaSynthWebWorker.CmdPrefix + 'resetChannelStates'; + public static readonly CmdReady: string = AlphaSynthWebWorker.CmdPrefix + 'ready'; + public static readonly CmdReadyForPlayback: string = AlphaSynthWebWorker.CmdPrefix + 'readyForPlayback'; + public static readonly CmdPositionChanged: string = AlphaSynthWebWorker.CmdPrefix + 'positionChanged'; + public static readonly CmdPlayerStateChanged: string = AlphaSynthWebWorker.CmdPrefix + 'playerStateChanged'; + public static readonly CmdFinished: string = AlphaSynthWebWorker.CmdPrefix + 'finished'; + public static readonly CmdSoundFontLoaded: string = AlphaSynthWebWorker.CmdPrefix + 'soundFontLoaded'; + public static readonly CmdSoundFontLoadFailed: string = AlphaSynthWebWorker.CmdPrefix + 'soundFontLoadFailed'; + public static readonly CmdMidiLoaded: string = AlphaSynthWebWorker.CmdPrefix + 'midiLoaded'; + public static readonly CmdMidiLoadFailed: string = AlphaSynthWebWorker.CmdPrefix + 'midiLoadFailed'; + public static readonly CmdLog: string = AlphaSynthWebWorker.CmdPrefix + 'log'; + + private _player: AlphaSynth; + private _main: IWorkerScope; + + public constructor(main: IWorkerScope) { + this._main = main; + this._main.addEventListener('message', this.handleMessage.bind(this)); + + this._player = new AlphaSynth(new AlphaSynthWorkerSynthOutput()); + this._player.positionChanged.on(this.onPositionChanged.bind(this)); + this._player.stateChanged.on(this.onPlayerStateChanged.bind(this)); + this._player.finished.on(this.onFinished.bind(this)); + this._player.soundFontLoaded.on(this.onSoundFontLoaded.bind(this)); + this._player.soundFontLoadFailed.on(this.onSoundFontLoadFailed.bind(this)); + this._player.soundFontLoadFailed.on(this.onSoundFontLoadFailed.bind(this)); + this._player.midiLoaded.on(this.onMidiLoaded.bind(this)); + this._player.midiLoadFailed.on(this.onMidiLoadFailed.bind(this)); + this._player.readyForPlayback.on(this.onReadyForPlayback.bind(this)); + this._main.postMessage({ + cmd: 'alphaSynth.ready' + }); + } + + public static init(): void { + let main: IWorkerScope = Environment.globalThis as IWorkerScope; + main.addEventListener('message', e => { + let data: any = e.data; + let cmd: string = data.cmd; + switch (cmd) { + case 'alphaSynth.initialize': + AlphaSynthWorkerSynthOutput.preferredSampleRate = data.sampleRate; + Logger.logLevel = data.logLevel; + Environment.globalThis.alphaSynthWebWorker = new AlphaSynthWebWorker(main); + break; + } + }); + } + + public handleMessage(e: MessageEvent): void { + let data: any = e.data; + let cmd: string = data.cmd; + switch (cmd) { + case 'alphaSynth.setLogLevel': + Logger.logLevel = data.value; + break; + case 'alphaSynth.setMasterVolume': + this._player.masterVolume = data.value; + break; + case 'alphaSynth.setMetronomeVolume': + this._player.metronomeVolume = data.value; + break; + case 'alphaSynth.setPlaybackSpeed': + this._player.playbackSpeed = data.value; + break; + case 'alphaSynth.setTickPosition': + this._player.tickPosition = data.value; + break; + case 'alphaSynth.setTimePosition': + this._player.timePosition = data.value; + break; + case 'alphaSynth.setPlaybackRange': + this._player.playbackRange = data.value; + break; + case 'alphaSynth.setIsLooping': + this._player.isLooping = data.value; + break; + case 'alphaSynth.play': + this._player.play(); + break; + case 'alphaSynth.pause': + this._player.pause(); + break; + case 'alphaSynth.playPause': + this._player.playPause(); + break; + case 'alphaSynth.stop': + this._player.stop(); + break; + case 'alphaSynth.loadSoundFontBytes': + this._player.loadSoundFont(data.data); + break; + case 'alphaSynth.loadMidi': + this._player.loadMidiFile(JsonConverter.jsObjectToMidiFile(data.midi)); + break; + case 'alphaSynth.setChannelMute': + this._player.setChannelMute(data.channel, data.mute); + break; + case 'alphaSynth.setChannelSolo': + this._player.setChannelSolo(data.channel, data.solo); + break; + case 'alphaSynth.setChannelVolume': + this._player.setChannelVolume(data.channel, data.volume); + break; + case 'alphaSynth.resetChannelStates': + this._player.resetChannelStates(); + break; + } + } + + public onPositionChanged(e: PositionChangedEventArgs): void { + this._main.postMessage({ + cmd: 'alphaSynth.positionChanged', + currentTime: e.currentTime, + endTime: e.endTime, + currentTick: e.currentTick, + endTick: e.endTick + }); + } + + public onPlayerStateChanged(e: PlayerStateChangedEventArgs): void { + this._main.postMessage({ + cmd: 'alphaSynth.playerStateChanged', + state: e.state, + stopped: e.stopped + }); + } + + public onFinished(): void { + this._main.postMessage({ + cmd: 'alphaSynth.finished' + }); + } + + public onSoundFontLoaded(): void { + this._main.postMessage({ + cmd: 'alphaSynth.soundFontLoaded' + }); + } + + public onSoundFontLoadFailed(e: any): void { + this._main.postMessage({ + cmd: 'alphaSynth.soundFontLoadFailed', + error: this.serializeException(e) + }); + } + + private serializeException(e: any): unknown { + let error: any = JSON.parse(JSON.stringify(e)); + if (e.message) { + error.message = e.message; + } + if (e.stack) { + error.stack = e.stack; + } + if (e.constructor && e.constructor.name) { + error.type = e.constructor.name; + } + return error; + } + + public onMidiLoaded(): void { + this._main.postMessage({ + cmd: 'alphaSynth.midiLoaded' + }); + } + + public onMidiLoadFailed(e: any): void { + this._main.postMessage({ + cmd: 'alphaSynth.midiLoaded', + error: this.serializeException(e) + }); + } + + public onReadyForPlayback(): void { + this._main.postMessage({ + cmd: 'alphaSynth.readyForPlayback' + }); + } +} diff --git a/src/platform/javascript/AlphaSynthWebWorkerApi.ts b/src/platform/javascript/AlphaSynthWebWorkerApi.ts new file mode 100644 index 000000000..ee1b20fa5 --- /dev/null +++ b/src/platform/javascript/AlphaSynthWebWorkerApi.ts @@ -0,0 +1,420 @@ +import { MidiFile } from '@src/midi/MidiFile'; +import { IAlphaSynth } from '@src/synth/IAlphaSynth'; +import { ISynthOutput } from '@src/synth/ISynthOutput'; +import { PlaybackRange } from '@src/synth/PlaybackRange'; +import { PlayerState } from '@src/synth/PlayerState'; +import { PlayerStateChangedEventArgs } from '@src/synth/PlayerStateChangedEventArgs'; +import { PositionChangedEventArgs } from '@src/synth/PositionChangedEventArgs'; +import { SynthHelper } from '@src/synth/SynthHelper'; +import { EventEmitter, IEventEmitter, IEventEmitterOfT, EventEmitterOfT } from '@src/EventEmitter'; +import { FileLoadError } from '@src/FileLoadError'; +import { JsonConverter } from '@src/model/JsonConverter'; +import { ProgressEventArgs } from '@src/ProgressEventArgs'; +import { Logger } from '@src/Logger'; +import { LogLevel } from '@src/LogLevel'; +import { SynthConstants } from '@src/synth/SynthConstants'; + +/** + * a WebWorker based alphaSynth which uses the given player as output. + * @target web + */ +export class AlphaSynthWebWorkerApi implements IAlphaSynth { + private _synth!: Worker; + private _output: ISynthOutput; + private _workerIsReadyForPlayback: boolean = false; + private _workerIsReady: boolean = false; + private _outputIsReady: boolean = false; + private _state: PlayerState = PlayerState.Paused; + private _masterVolume: number = 0; + private _metronomeVolume: number = 0; + private _playbackSpeed: number = 0; + private _tickPosition: number = 0; + private _timePosition: number = 0; + private _isLooping: boolean = false; + private _playbackRange: PlaybackRange | null = null; + + public get isReady(): boolean { + return this._workerIsReady && this._outputIsReady; + } + + public get isReadyForPlayback(): boolean { + return this._workerIsReadyForPlayback; + } + + public get state(): PlayerState { + return this._state; + } + + public get logLevel(): LogLevel { + return Logger.logLevel; + } + + public set logLevel(value: LogLevel) { + Logger.logLevel = value; + this._synth.postMessage({ + cmd: 'alphaSynth.setLogLevel', + value: value + }); + } + + public get masterVolume(): number { + return this._masterVolume; + } + + public set masterVolume(value: number) { + value = SynthHelper.clamp(value, SynthConstants.MinVolume, SynthConstants.MaxVolume); + this._masterVolume = value; + this._synth.postMessage({ + cmd: 'alphaSynth.setMasterVolume', + value: value + }); + } + + public get metronomeVolume(): number { + return this._metronomeVolume; + } + + public set metronomeVolume(value: number) { + value = SynthHelper.clamp(value, SynthConstants.MinVolume, SynthConstants.MaxVolume); + this._metronomeVolume = value; + this._synth.postMessage({ + cmd: 'alphaSynth.setMetronomeVolume', + value: value + }); + } + + public get playbackSpeed(): number { + return this._playbackSpeed; + } + + public set playbackSpeed(value: number) { + value = SynthHelper.clamp(value, SynthConstants.MinPlaybackSpeed, SynthConstants.MaxPlaybackSpeed); + this._playbackSpeed = value; + this._synth.postMessage({ + cmd: 'alphaSynth.setPlaybackSpeed', + value: value + }); + } + + public get tickPosition(): number { + return this._tickPosition; + } + + public set tickPosition(value: number) { + if (value < 0) { + value = 0; + } + this._tickPosition = value; + this._synth.postMessage({ + cmd: 'alphaSynth.setTickPosition', + value: value + }); + } + + public get timePosition(): number { + return this._timePosition; + } + + public set timePosition(value: number) { + if (value < 0) { + value = 0; + } + this._timePosition = value; + this._synth.postMessage({ + cmd: 'alphaSynth.setTimePosition', + value: value + }); + } + + public get isLooping(): boolean { + return this._isLooping; + } + + public set isLooping(value: boolean) { + this._isLooping = value; + this._synth.postMessage({ + cmd: 'alphaSynth.setIsLooping', + value: value + }); + } + + public get playbackRange(): PlaybackRange | null { + return this._playbackRange; + } + + public set playbackRange(value: PlaybackRange | null) { + if (value) { + if (value.startTick < 0) { + value.startTick = 0; + } + if (value.endTick < 0) { + value.endTick = 0; + } + } + this._playbackRange = value; + this._synth.postMessage({ + cmd: 'alphaSynth.setPlaybackRange', + value: value + }); + } + + public constructor(player: ISynthOutput, alphaSynthScriptFile: string, logLevel: LogLevel) { + this._workerIsReadyForPlayback = false; + this._workerIsReady = false; + this._outputIsReady = false; + this._state = PlayerState.Paused; + this._masterVolume = 0.0; + this._metronomeVolume = 0.0; + this._playbackSpeed = 0.0; + this._tickPosition = 0; + this._timePosition = 0.0; + this._isLooping = false; + this._playbackRange = null; + this._output = player; + this._output.ready.on(this.onOutputReady.bind(this)); + this._output.samplesPlayed.on(this.onOutputSamplesPlayed.bind(this)); + this._output.sampleRequest.on(this.onOutputSampleRequest.bind(this)); + this._output.finished.on(this.onOutputFinished.bind(this)); + this._output.open(); + try { + let script: string = "importScripts('" + alphaSynthScriptFile + "')"; + let blob: Blob = new Blob([script]); + this._synth = new Worker(URL.createObjectURL(blob)); + } catch (e) { + // fallback to direct worker + try { + this._synth = new Worker(alphaSynthScriptFile); + } catch (e2) { + Logger.error('AlphaSynth', 'Failed to create WebWorker: ' + e2); + return; + } + } + this._synth.addEventListener('message', this.handleWorkerMessage.bind(this), false); + this._synth.postMessage({ + cmd: 'alphaSynth.initialize', + sampleRate: this._output.sampleRate, + logLevel: logLevel + }); + this.masterVolume = 1; + this.playbackSpeed = 1; + this.metronomeVolume = 0; + } + + public destroy(): void { + this._synth.terminate(); + } + + // + // API communicating with the web worker + public play(): boolean { + if (this.state === PlayerState.Playing || !this.isReadyForPlayback) { + return false; + } + this._output.activate(); + this._synth.postMessage({ + cmd: 'alphaSynth.play' + }); + return true; + } + + public pause(): void { + this._synth.postMessage({ + cmd: 'alphaSynth.pause' + }); + } + + public playPause(): void { + this._output.activate(); + this._synth.postMessage({ + cmd: 'alphaSynth.playPause' + }); + } + + public stop(): void { + this._synth.postMessage({ + cmd: 'alphaSynth.stop' + }); + } + + public loadSoundFont(data: Uint8Array): void { + this._synth.postMessage({ + cmd: 'alphaSynth.loadSoundFontBytes', + data: data + }); + } + + public loadSoundFontFromUrl(url: string, progress: (e: ProgressEventArgs) => void): void { + Logger.debug('AlphaSynth', `Start loading Soundfont from url ${url}`); + let request: XMLHttpRequest = new XMLHttpRequest(); + request.open('GET', url, true, null, null); + request.responseType = 'arraybuffer'; + request.onload = _ => { + let buffer: Uint8Array = new Uint8Array(request.response); + this._synth.postMessage({ + cmd: 'alphaSynth.loadSoundFontBytes', + data: buffer + }); + }; + request.onerror = e => { + Logger.error('AlphaSynth', 'Loading failed: ' + (e as any).message); + (this.soundFontLoadFailed as EventEmitterOfT).trigger( + new FileLoadError((e as any).message, request) + ); + }; + request.onprogress = e => { + Logger.debug('AlphaSynth', `Soundfont downloading: ${e.loaded}/${e.total} bytes`); + progress(new ProgressEventArgs(e.loaded, e.total)); + }; + request.send(); + } + + public loadMidiFile(midi: MidiFile): void { + this._synth.postMessage({ + cmd: 'alphaSynth.loadMidi', + midi: JsonConverter.midiFileToJsObject(midi) + }); + } + + public setChannelMute(channel: number, mute: boolean): void { + this._synth.postMessage({ + cmd: 'alphaSynth.setChannelMute', + channel: channel, + mute: mute + }); + } + + public resetChannelStates(): void { + this._synth.postMessage({ + cmd: 'alphaSynth.resetChannelStates' + }); + } + + public setChannelSolo(channel: number, solo: boolean): void { + this._synth.postMessage({ + cmd: 'alphaSynth.setChannelSolo', + channel: channel, + solo: solo + }); + } + + public setChannelVolume(channel: number, volume: number): void { + volume = SynthHelper.clamp(volume, SynthConstants.MinVolume, SynthConstants.MaxVolume); + this._synth.postMessage({ + cmd: 'alphaSynth.setChannelVolume', + channel: channel, + volume: volume + }); + } + + public handleWorkerMessage(e: MessageEvent): void { + let data: any = e.data; + let cmd: string = data.cmd; + switch (cmd) { + case 'alphaSynth.ready': + this._workerIsReady = true; + this.checkReady(); + break; + case 'alphaSynth.readyForPlayback': + this._workerIsReadyForPlayback = true; + this.checkReadyForPlayback(); + break; + case 'alphaSynth.positionChanged': + this._timePosition = data.currentTime; + this._tickPosition = data.currentTick; + (this.positionChanged as EventEmitterOfT).trigger( + new PositionChangedEventArgs(data.currentTime, data.endTime, data.currentTick, data.endTick) + ); + break; + case 'alphaSynth.playerStateChanged': + this._state = data.state; + (this.stateChanged as EventEmitterOfT).trigger( + new PlayerStateChangedEventArgs(data.state, data.stopped) + ); + break; + case 'alphaSynth.finished': + (this.finished as EventEmitter).trigger(); + break; + case 'alphaSynth.soundFontLoaded': + (this.soundFontLoaded as EventEmitter).trigger(); + break; + case 'alphaSynth.soundFontLoadFailed': + (this.soundFontLoadFailed as EventEmitterOfT).trigger(data.error); + break; + case 'alphaSynth.midiLoaded': + this.checkReadyForPlayback(); + (this.midiLoaded as EventEmitter).trigger(); + break; + case 'alphaSynth.midiLoadFailed': + this.checkReadyForPlayback(); + (this.midiLoadFailed as EventEmitterOfT).trigger(data.error); + break; + case 'alphaSynth.output.sequencerFinished': + this._output.sequencerFinished(); + break; + case 'alphaSynth.output.addSamples': + this._output.addSamples(data.samples); + break; + case 'alphaSynth.output.play': + this._output.play(); + break; + case 'alphaSynth.output.pause': + this._output.pause(); + break; + case 'alphaSynth.output.resetSamples': + this._output.resetSamples(); + break; + } + } + + private checkReady(): void { + if (this.isReady) { + (this.ready as EventEmitter).trigger(); + } + } + + private checkReadyForPlayback(): void { + if (this.isReadyForPlayback) { + (this.readyForPlayback as EventEmitter).trigger(); + } + } + + readonly ready: IEventEmitter = new EventEmitter(); + readonly readyForPlayback: IEventEmitter = new EventEmitter(); + readonly finished: IEventEmitter = new EventEmitter(); + readonly soundFontLoaded: IEventEmitter = new EventEmitter(); + readonly soundFontLoadFailed: IEventEmitterOfT = new EventEmitterOfT(); + readonly midiLoaded: IEventEmitter = new EventEmitter(); + readonly midiLoadFailed: IEventEmitterOfT = new EventEmitterOfT(); + readonly stateChanged: IEventEmitterOfT = new EventEmitterOfT< + PlayerStateChangedEventArgs + >(); + readonly positionChanged: IEventEmitterOfT = new EventEmitterOfT< + PositionChangedEventArgs + >(); + + // + // output communication ( output -> worker ) + public onOutputSampleRequest(): void { + this._synth.postMessage({ + cmd: 'alphaSynth.output.sampleRequest' + }); + } + + public onOutputFinished(): void { + this._synth.postMessage({ + cmd: 'alphaSynth.output.finished' + }); + } + + public onOutputSamplesPlayed(samples: number): void { + this._synth.postMessage({ + cmd: 'alphaSynth.output.samplesPlayed', + samples: samples + }); + } + + private onOutputReady(): void { + this._outputIsReady = true; + this.checkReady(); + } +} diff --git a/src/platform/javascript/AlphaSynthWorkerSynthOutput.ts b/src/platform/javascript/AlphaSynthWorkerSynthOutput.ts new file mode 100644 index 000000000..de3a45334 --- /dev/null +++ b/src/platform/javascript/AlphaSynthWorkerSynthOutput.ts @@ -0,0 +1,96 @@ +import { ISynthOutput } from '@src/synth/ISynthOutput'; +import { EventEmitter, IEventEmitter, IEventEmitterOfT, EventEmitterOfT } from '@src/EventEmitter'; +import { IWorkerScope } from '@src/platform/javascript/IWorkerScope'; +import { Logger } from '@src/Logger'; +import { Environment } from '@src/Environment'; + +/** + * @target web + */ +export class AlphaSynthWorkerSynthOutput implements ISynthOutput { + public static readonly CmdOutputPrefix: string = 'alphaSynth.output.'; + public static readonly CmdOutputSequencerFinished: string = + AlphaSynthWorkerSynthOutput.CmdOutputPrefix + 'sequencerFinished'; + public static readonly CmdOutputAddSamples: string = AlphaSynthWorkerSynthOutput.CmdOutputPrefix + 'addSamples'; + public static readonly CmdOutputPlay: string = AlphaSynthWorkerSynthOutput.CmdOutputPrefix + 'play'; + public static readonly CmdOutputPause: string = AlphaSynthWorkerSynthOutput.CmdOutputPrefix + 'pause'; + public static readonly CmdOutputResetSamples: string = AlphaSynthWorkerSynthOutput.CmdOutputPrefix + 'resetSamples'; + public static readonly CmdOutputSampleRequest: string = + AlphaSynthWorkerSynthOutput.CmdOutputPrefix + 'sampleRequest'; + public static readonly CmdOutputFinished: string = AlphaSynthWorkerSynthOutput.CmdOutputPrefix + 'finished'; + public static readonly CmdOutputSamplesPlayed: string = + AlphaSynthWorkerSynthOutput.CmdOutputPrefix + 'samplesPlayed'; + + // this value is initialized by the alphaSynth WebWorker wrapper + // that also includes the alphaSynth library into the worker. + public static preferredSampleRate: number = 0; + + private _worker!: IWorkerScope; + + public get sampleRate(): number { + return AlphaSynthWorkerSynthOutput.preferredSampleRate; + } + + public open(): void { + Logger.debug('AlphaSynth', 'Initializing webworker worker'); + this._worker = Environment.globalThis as IWorkerScope; + this._worker.addEventListener('message', this.handleMessage.bind(this)); + (this.ready as EventEmitter).trigger(); + } + + private handleMessage(e: MessageEvent): void { + let data: any = e.data; + let cmd: any = data.cmd; + switch (cmd) { + case AlphaSynthWorkerSynthOutput.CmdOutputSampleRequest: + (this.sampleRequest as EventEmitter).trigger(); + break; + case AlphaSynthWorkerSynthOutput.CmdOutputFinished: + (this.finished as EventEmitter).trigger(); + break; + case AlphaSynthWorkerSynthOutput.CmdOutputSamplesPlayed: + (this.samplesPlayed as EventEmitterOfT).trigger(data.samples); + break; + } + } + + public readonly ready: IEventEmitter = new EventEmitter(); + public readonly samplesPlayed: IEventEmitterOfT = new EventEmitterOfT(); + public readonly sampleRequest: IEventEmitter = new EventEmitter(); + public readonly finished: IEventEmitter = new EventEmitter(); + + public sequencerFinished(): void { + this._worker.postMessage({ + cmd: 'alphaSynth.output.sequencerFinished' + }); + } + + public addSamples(samples: Float32Array): void { + this._worker.postMessage({ + cmd: 'alphaSynth.output.addSamples', + samples: samples + }); + } + + public play(): void { + this._worker.postMessage({ + cmd: 'alphaSynth.output.play' + }); + } + + public pause(): void { + this._worker.postMessage({ + cmd: 'alphaSynth.output.pause' + }); + } + + public resetSamples(): void { + this._worker.postMessage({ + cmd: 'alphaSynth.output.resetSamples' + }); + } + + public activate(): void { + // nothing to do + } +} diff --git a/src/platform/javascript/AlphaTabApi.ts b/src/platform/javascript/AlphaTabApi.ts new file mode 100644 index 000000000..0ab44fe3f --- /dev/null +++ b/src/platform/javascript/AlphaTabApi.ts @@ -0,0 +1,153 @@ +import { AlphaTabApiBase } from '@src/AlphaTabApiBase'; +import { AlphaSynthMidiFileHandler } from '@src/midi/AlphaSynthMidiFileHandler'; +import { MidiFileGenerator } from '@src/midi/MidiFileGenerator'; +import { MidiFile } from '@src/midi/MidiFile'; +import { LayoutMode } from '@src/DisplaySettings'; +import { IEventEmitterOfT, EventEmitterOfT } from '@src/EventEmitter'; +import { Track } from '@src/model/Track'; +import { AlphaSynthWebWorkerApi } from '@src/platform/javascript/AlphaSynthWebWorkerApi'; +import { BrowserUiFacade } from '@src/platform/javascript/BrowserUiFacade'; +import { ProgressEventArgs } from '@src/ProgressEventArgs'; +import { Settings } from '@src/Settings'; + +/** + * @target web + */ +export class AlphaTabApi extends AlphaTabApiBase { + public constructor(element: HTMLElement, options: unknown) { + super(new BrowserUiFacade(element), options); + } + + public tex(tex: string, tracks?: number[]): void { + let browser: BrowserUiFacade = this.uiFacade as BrowserUiFacade; + super.tex(tex, browser.parseTracks(tracks)); + } + + public print(width: string): void { + // prepare a popup window for printing (a4 width, window height, centered) + let preview: Window = window.open('', '', 'width=0,height=0')!; + let a4: HTMLElement = preview.document.createElement('div'); + if (width) { + a4.style.width = width; + } else { + if (this.settings.display.layoutMode === LayoutMode.Horizontal) { + a4.style.width = '297mm'; + } else { + a4.style.width = '210mm'; + } + } + preview.document.write(''); + preview.document.body.appendChild(a4); + let dualScreenLeft: number = + typeof (window as any)['screenLeft'] !== 'undefined' + ? (window as any)['screenLeft'] + : (window as any)['left']; + let dualScreenTop: number = + typeof (window as any)['screenTop'] !== 'undefined' ? (window as any)['screenTop'] : (window as any)['top']; + let screenWidth: number = + "innerWidth" in window + ? window.innerWidth + : "clientWidth" in document.documentElement + ? document.documentElement.clientWidth + : window.screen.width; + let screenHeight: number = + "innerHeight" in window + ? window.innerHeight + : "clientHeight" in document.documentElement + ? document.documentElement.clientHeight + : window.screen.height; + let w: number = a4.offsetWidth + 50; + let h: number = window.innerHeight; + let left: number = ((screenWidth / 2) | 0) - ((w / 2) | 0) + dualScreenLeft; + let top: number = ((screenHeight / 2) | 0) - ((h / 2) | 0) + dualScreenTop; + preview.resizeTo(w, h); + preview.moveTo(left, top); + preview.focus(); + // render alphaTab + let settings: Settings = new Settings(); + settings.core.scriptFile = this.settings.core.scriptFile; + settings.core.fontDirectory = this.settings.core.fontDirectory; + settings.core.enableLazyLoading = false; + settings.core.useWorkers = false; + settings.display.scale = 0.8; + settings.display.stretchForce = 0.8; + let alphaTab: AlphaTabApi = new AlphaTabApi(a4, settings); + alphaTab.renderer.postRenderFinished.on(() => { + alphaTab.canvasElement.height = -1; + preview.print(); + }); + alphaTab.renderTracks(this.tracks); + } + + public downloadMidi(): void { + if(!this.score) { + return; + } + + let midiFile: MidiFile = new MidiFile(); + let handler: AlphaSynthMidiFileHandler = new AlphaSynthMidiFileHandler(midiFile); + let generator: MidiFileGenerator = new MidiFileGenerator(this.score, this.settings, handler); + generator.generate(); + let binary: Uint8Array = midiFile.toBinary(); + let fileName: string = !this.score.title ? 'File.mid' : `${this.score.title}.mid`; + let dlLink: HTMLAnchorElement = document.createElement('a'); + dlLink.download = fileName; + let blob: Blob = new Blob([binary], { + type: 'audio/midi' + }); + let url: string = URL.createObjectURL(blob); + dlLink.href = url; + dlLink.style.display = 'none'; + document.body.appendChild(dlLink); + dlLink.click(); + document.body.removeChild(dlLink); + } + + public changeTrackMute(tracks: Track[], mute: boolean): void { + let trackList: Track[] = this.trackIndexesToTracks((this.uiFacade as BrowserUiFacade).parseTracks(tracks)); + super.changeTrackMute(trackList, mute); + } + + public changeTrackSolo(tracks: Track[], solo: boolean): void { + let trackList: Track[] = this.trackIndexesToTracks((this.uiFacade as BrowserUiFacade).parseTracks(tracks)); + super.changeTrackSolo(trackList, solo); + } + + public changeTrackVolume(tracks: Track[], volume: number): void { + let trackList: Track[] = this.trackIndexesToTracks((this.uiFacade as BrowserUiFacade).parseTracks(tracks)); + super.changeTrackVolume(trackList, volume); + } + + private trackIndexesToTracks(trackIndexes: number[]): Track[] { + if (!this.score) { + return []; + } + let tracks: Track[] = []; + if (trackIndexes.length === 1 && trackIndexes[0] === -1) { + for (let track of this.score.tracks) { + tracks.push(track); + } + } else { + for (let index of trackIndexes) { + if (index >= 0 && index < this.score.tracks.length) { + tracks.push(this.score.tracks[index]); + } + } + } + return tracks; + } + + public soundFontLoad: IEventEmitterOfT = new EventEmitterOfT(); + public loadSoundFontFromUrl(url: string): void { + if (!this.player) { + return; + } + (this.player as AlphaSynthWebWorkerApi).loadSoundFontFromUrl( + url, + e => { + (this.soundFontLoad as EventEmitterOfT).trigger(e); + this.uiFacade.triggerEvent(this.container, 'soundFontLoad', e); + } + ); + } +} diff --git a/src/platform/javascript/AlphaTabWebWorker.ts b/src/platform/javascript/AlphaTabWebWorker.ts new file mode 100644 index 000000000..610693321 --- /dev/null +++ b/src/platform/javascript/AlphaTabWebWorker.ts @@ -0,0 +1,111 @@ +import { JsonConverter } from '@src/model/JsonConverter'; +import { Score } from '@src/model/Score'; +import { IWorkerScope } from '@src/platform/javascript/IWorkerScope'; +import { FontSizes } from '@src/platform/svg/FontSizes'; +import { ScoreRenderer } from '@src/rendering/ScoreRenderer'; +import { Settings } from '@src/Settings'; +import { Logger } from '@src/Logger'; +import { Environment } from '@src/Environment'; + +/** + * @target web + */ +export class AlphaTabWebWorker { + private _renderer!: ScoreRenderer; + private _main: IWorkerScope; + + public constructor(main: IWorkerScope) { + this._main = main; + this._main.addEventListener('message', this.handleMessage.bind(this), false); + } + + public static init(): void { + Environment.globalThis.alphaTabWebWorker = new AlphaTabWebWorker(Environment.globalThis as IWorkerScope); + } + + private handleMessage(e: MessageEvent): void { + let data: any = e.data; + let cmd: any = data ? data.cmd : ''; + switch (cmd) { + case 'alphaTab.initialize': + let settings: Settings = new Settings(); + settings.fillFromJson(data.settings); + Logger.logLevel = settings.core.logLevel; + this._renderer = new ScoreRenderer(settings); + this._renderer.partialRenderFinished.on(result => { + this._main.postMessage({ + cmd: 'alphaTab.partialRenderFinished', + result: result + }); + }); + this._renderer.renderFinished.on(result => { + this._main.postMessage({ + cmd: 'alphaTab.renderFinished', + result: result + }); + }); + this._renderer.postRenderFinished.on(() => { + this._main.postMessage({ + cmd: 'alphaTab.postRenderFinished', + boundsLookup: this._renderer.boundsLookup?.toJson() ?? null + }); + }); + this._renderer.preRender.on(resize => { + this._main.postMessage({ + cmd: 'alphaTab.preRender', + resize: resize + }); + }); + this._renderer.error.on(this.error.bind(this)); + break; + case 'alphaTab.invalidate': + this._renderer.render(); + break; + case 'alphaTab.resizeRender': + this._renderer.resizeRender(); + break; + case 'alphaTab.setWidth': + this._renderer.width = data.width; + break; + case 'alphaTab.renderScore': + this.updateFontSizes(data.fontSizes); + let score: any = JsonConverter.jsObjectToScore(data.score, this._renderer.settings); + this.renderMultiple(score, data.trackIndexes); + break; + case 'alphaTab.updateSettings': + this.updateSettings(data.settings); + break; + } + } + + private updateFontSizes(fontSizes: any): void { + if (fontSizes) { + if (!FontSizes.FontSizeLookupTables) { + FontSizes.FontSizeLookupTables = new Map(); + } + for (let font in fontSizes) { + FontSizes.FontSizeLookupTables.set(font, fontSizes[font]); + } + } + } + + private updateSettings(json: unknown): void { + this._renderer.settings.fillFromJson(json); + } + + private renderMultiple(score: Score, trackIndexes: number[]): void { + try { + this._renderer.renderScore(score, trackIndexes); + } catch (e) { + this.error(e); + } + } + + private error(error: Error): void { + Logger.error('Worker', 'An unexpected error occurred in worker', error); + this._main.postMessage({ + cmd: 'alphaTab.error', + error: error + }); + } +} diff --git a/src/platform/javascript/AlphaTabWorkerScoreRenderer.ts b/src/platform/javascript/AlphaTabWorkerScoreRenderer.ts new file mode 100644 index 000000000..77cd6f784 --- /dev/null +++ b/src/platform/javascript/AlphaTabWorkerScoreRenderer.ts @@ -0,0 +1,130 @@ +import { AlphaTabApiBase } from '@src/AlphaTabApiBase'; +import { EventEmitter, IEventEmitterOfT, IEventEmitter, EventEmitterOfT } from '@src/EventEmitter'; +import { JsonConverter } from '@src/model/JsonConverter'; +import { Score } from '@src/model/Score'; +import { FontSizes } from '@src/platform/svg/FontSizes'; +import { IScoreRenderer } from '@src/rendering/IScoreRenderer'; +import { RenderFinishedEventArgs } from '@src/rendering/RenderFinishedEventArgs'; +import { BoundsLookup } from '@src/rendering/utils/BoundsLookup'; +import { Settings } from '@src/Settings'; +import { Logger } from '@src/Logger'; + +/** + * @target web + */ +export class AlphaTabWorkerScoreRenderer implements IScoreRenderer { + private _api: AlphaTabApiBase; + private _worker!: Worker; + private _width: number = 0; + + public boundsLookup: BoundsLookup | null = null; + + public constructor(api: AlphaTabApiBase, settings: Settings) { + this._api = api; + + if(!settings.core.scriptFile) { + Logger.error('Rendering', `Could not detect alphaTab script file, cannot initialize renderer`); + return; + } + + // first try blob worker + try { + let script: string = `importScripts('${settings.core.scriptFile}')`; + let blob: Blob = new Blob([script]); + this._worker = new Worker(URL.createObjectURL(blob)); + } catch (e) { + try { + this._worker = new Worker(settings.core.scriptFile); + } catch (e2) { + Logger.error('Rendering', `Failed to create WebWorker: ${e}`); + return; + } + } + this._worker.postMessage({ + cmd: 'alphaTab.initialize', + settings: this.serializeSettingsForWorker(settings) + }); + this._worker.addEventListener('message', this.handleWorkerMessage.bind(this)); + } + + public destroy(): void { + this._worker.terminate(); + } + + public updateSettings(settings: Settings): void { + this._worker.postMessage({ + cmd: 'alphaTab.updateSettings', + settings: this.serializeSettingsForWorker(settings) + }); + } + + private serializeSettingsForWorker(settings: Settings): unknown { + let json: any = Settings.toJson(settings); + // cut out player settings, they are only needed on UI thread side + json.player = null; + return json; + } + + public render(): void { + this._worker.postMessage({ + cmd: 'alphaTab.render' + }); + } + + public resizeRender(): void { + this._worker.postMessage({ + cmd: 'alphaTab.resizeRender' + }); + } + + public get width(): number { + return this._width; + } + + public set width(value: number) { + this._width = value; + this._worker.postMessage({ + cmd: 'alphaTab.setWidth', + width: value + }); + } + + private handleWorkerMessage(e: MessageEvent): void { + let data: any = e.data; + let cmd: string = data.cmd; + switch (cmd) { + case 'alphaTab.preRender': + (this.preRender as EventEmitterOfT).trigger(data.resize); + break; + case 'alphaTab.partialRenderFinished': + (this.partialRenderFinished as EventEmitterOfT).trigger(data.result); + break; + case 'alphaTab.renderFinished': + (this.renderFinished as EventEmitterOfT).trigger(data.result); + break; + case 'alphaTab.postRenderFinished': + this.boundsLookup = BoundsLookup.fromJson(data.boundsLookup, this._api.score!); + (this.postRenderFinished as EventEmitter).trigger(); + break; + case 'alphaTab.error': + (this.error as EventEmitterOfT).trigger(data.error); + break; + } + } + + public renderScore(score: Score, trackIndexes: number[]): void { + let jsObject: unknown = JsonConverter.scoreToJsObject(score); + this._worker.postMessage({ + cmd: 'alphaTab.renderScore', + score: jsObject, + trackIndexes: trackIndexes, + fontSizes: FontSizes.FontSizeLookupTables + }); + } + + public preRender: IEventEmitterOfT = new EventEmitterOfT(); + public partialRenderFinished: IEventEmitterOfT = new EventEmitterOfT(); + public renderFinished: IEventEmitterOfT = new EventEmitterOfT(); + public postRenderFinished: IEventEmitter = new EventEmitter(); + public error: IEventEmitterOfT = new EventEmitterOfT(); +} diff --git a/src/platform/javascript/BrowserMouseEventArgs.ts b/src/platform/javascript/BrowserMouseEventArgs.ts new file mode 100644 index 000000000..6b9a669f7 --- /dev/null +++ b/src/platform/javascript/BrowserMouseEventArgs.ts @@ -0,0 +1,36 @@ +import { IContainer } from '@src/platform/IContainer'; +import { IMouseEventArgs } from '@src/platform/IMouseEventArgs'; +import { HtmlElementContainer } from '@src/platform/javascript/HtmlElementContainer'; + +/** + * @target web + */ +export class BrowserMouseEventArgs implements IMouseEventArgs { + public readonly mouseEvent: MouseEvent; + + public get isLeftMouseButton(): boolean { + return this.mouseEvent.button === 0; + } + + public getX(relativeTo: IContainer): number { + let relativeToElement: HTMLElement = (relativeTo as HtmlElementContainer).element; + let bounds: DOMRect = relativeToElement.getBoundingClientRect(); + let left: number = bounds.left + relativeToElement.ownerDocument!.defaultView!.pageXOffset; + return this.mouseEvent.pageX - left; + } + + public getY(relativeTo: IContainer): number { + let relativeToElement: HTMLElement = (relativeTo as HtmlElementContainer).element; + let bounds: DOMRect = relativeToElement.getBoundingClientRect(); + let top: number = bounds.top + relativeToElement.ownerDocument!.defaultView!.pageYOffset; + return this.mouseEvent.pageY - top; + } + + public preventDefault(): void { + this.mouseEvent.preventDefault(); + } + + public constructor(e: MouseEvent) { + this.mouseEvent = e; + } +} diff --git a/src/platform/javascript/BrowserUiFacade.ts b/src/platform/javascript/BrowserUiFacade.ts new file mode 100644 index 000000000..6e99eef45 --- /dev/null +++ b/src/platform/javascript/BrowserUiFacade.ts @@ -0,0 +1,644 @@ +import { AlphaTabApiBase } from '@src/AlphaTabApiBase'; +import { IAlphaSynth } from '@src/synth/IAlphaSynth'; +import { Environment } from '@src/Environment'; +import { EventEmitter, IEventEmitter } from '@src/EventEmitter'; +import { ScoreLoader } from '@src/importer/ScoreLoader'; +import { Font } from '@src/model/Font'; +import { Score } from '@src/model/Score'; +import { NotationMode } from '@src/NotationSettings'; +import { IContainer } from '@src/platform/IContainer'; +import { HtmlElementContainer } from '@src/platform/javascript/HtmlElementContainer'; +import { FontSizes } from '@src/platform/svg/FontSizes'; +import { IScoreRenderer } from '@src/rendering/IScoreRenderer'; +import { RenderFinishedEventArgs } from '@src/rendering/RenderFinishedEventArgs'; +import { Bounds } from '@src/rendering/utils/Bounds'; +import { Settings } from '@src/Settings'; +import { FontLoadingChecker } from '@src/util/FontLoadingChecker'; +import { Logger } from '@src/Logger'; +import { IMouseEventArgs } from '@src/platform/IMouseEventArgs'; +import { IUiFacade } from '@src/platform/IUiFacade'; +import { AlphaSynthWebAudioOutput } from '@src/platform/javascript/AlphaSynthWebAudioOutput'; +import { AlphaSynthWebWorkerApi } from '@src/platform/javascript/AlphaSynthWebWorkerApi'; +import { AlphaTabApi } from '@src/platform/javascript/AlphaTabApi'; +import { AlphaTabWorkerScoreRenderer } from '@src/platform/javascript/AlphaTabWorkerScoreRenderer'; +import { BrowserMouseEventArgs } from '@src/platform/javascript/BrowserMouseEventArgs'; +import { Cursors } from '@src/platform/Cursors'; + +/** + * @target web + */ +export class BrowserUiFacade implements IUiFacade { + private _fontCheckers: Map = new Map(); + private _api!: AlphaTabApiBase; + private _contents: string | null = null; + private _file: string | null = null; + private _visibilityCheckIntervalId: number = 0; + private _visibilityCheckInterval: number = 0; + private _totalResultCount: number = 0; + private _initialTrackIndexes: number[] | null = null; + + private _rootContainerBecameVisible: IEventEmitter = new EventEmitter(); + public rootContainerBecameVisible: IEventEmitter; + public canRenderChanged: IEventEmitter = new EventEmitter(); + + public get resizeThrottle(): number { + return 10; + } + + public rootContainer: IContainer; + public areWorkersSupported: boolean; + + public get canRender(): boolean { + return this.areAllFontsLoaded(); + } + + private areAllFontsLoaded(): boolean { + Environment.bravuraFontChecker.checkForFontAvailability(); + if (!Environment.bravuraFontChecker.isFontLoaded) { + return false; + } + + let isAnyNotLoaded = false; + this._fontCheckers.forEach(checker => { + if (!checker.isFontLoaded) { + isAnyNotLoaded = true; + } + }); + + if (isAnyNotLoaded) { + return false; + } + + Logger.debug('Font', 'All fonts loaded: ' + this._fontCheckers.size); + return true; + } + + private onFontLoaded(family: string): void { + FontSizes.generateFontLookup(family); + if (this.areAllFontsLoaded()) { + (this.canRenderChanged as EventEmitter).trigger(); + } + } + + public constructor(rootElement: HTMLElement) { + rootElement.classList.add('alphaTab'); + this.rootContainer = new HtmlElementContainer(rootElement); + this.areWorkersSupported = 'Worker' in window; + Environment.bravuraFontChecker.fontLoaded.on(this.onFontLoaded.bind(this)); + + this.rootContainerBecameVisible = { + on: (value: any) => { + if (this.rootContainer.isVisible) { + value(); + } else { + this._rootContainerBecameVisible.on(value); + + if (this._visibilityCheckIntervalId === 0) { + this._visibilityCheckIntervalId = window.setInterval(() => { + if (this._api.container.isVisible) { + window.clearInterval(this._visibilityCheckIntervalId); + this._visibilityCheckIntervalId = 0; + (this._rootContainerBecameVisible as EventEmitter).trigger(); + } + }, this._visibilityCheckInterval); + } + } + }, + + off: (value: any) => { + this._rootContainerBecameVisible.off(value); + } + }; + } + + public createWorkerRenderer(): IScoreRenderer { + return new AlphaTabWorkerScoreRenderer(this._api, this._api.settings); + } + + public initialize(api: AlphaTabApiBase, raw: unknown): void { + this._api = api; + let settings: Settings; + if (raw instanceof Settings) { + settings = raw; + } else { + settings = new Settings(); + settings.fillFromJson(raw); + } + + let dataAttributes: Map = this.getDataAttributes(); + settings.fillFromDataAttributes(dataAttributes); + if (settings.notation.notationMode === NotationMode.SongBook) { + settings.setSongBookModeSettings(); + } + api.settings = settings; + if (settings.core.engine === 'default' || settings.core.engine === 'svg') { + api.container.scroll.on(this.showSvgsInViewPort.bind(this)); + api.container.resize.on(this.showSvgsInViewPort.bind(this)); + } + this.setupFontCheckers(settings); + + this._initialTrackIndexes = this.parseTracks(settings.core.tracks); + this._contents = ''; + let element: HtmlElementContainer = api.container as HtmlElementContainer; + if (settings.core.tex) { + this._contents = element.element.innerHTML; + element.element.innerHTML = ''; + } + this.createStyleElement(settings); + this._file = settings.core.file; + + this._visibilityCheckInterval = settings.core.visibilityCheckInterval; + } + + private setupFontCheckers(settings: Settings): void { + this.registerFontChecker(settings.display.resources.copyrightFont); + this.registerFontChecker(settings.display.resources.effectFont); + this.registerFontChecker(settings.display.resources.fingeringFont); + this.registerFontChecker(settings.display.resources.graceFont); + this.registerFontChecker(settings.display.resources.markerFont); + this.registerFontChecker(settings.display.resources.tablatureFont); + this.registerFontChecker(settings.display.resources.titleFont); + this.registerFontChecker(settings.display.resources.wordsFont); + this.registerFontChecker(settings.display.resources.barNumberFont); + this.registerFontChecker(settings.display.resources.fretboardNumberFont); + this.registerFontChecker(settings.display.resources.subTitleFont); + } + + private registerFontChecker(font: Font): void { + if (!this._fontCheckers.has(font.family)) { + let checker: FontLoadingChecker = new FontLoadingChecker(font.family); + this._fontCheckers.set(font.family, checker); + checker.fontLoaded.on(this.onFontLoaded.bind(this)); + checker.checkForFontAvailability(); + } + } + + public destroy(): void { + (this.rootContainer as HtmlElementContainer).element.innerHTML = ''; + } + + public createCanvasElement(): IContainer { + let canvasElement: HTMLElement = document.createElement('div'); + canvasElement.className = 'at-surface'; + canvasElement.style.fontSize = '0'; + canvasElement.style.overflow = 'hidden'; + canvasElement.style.lineHeight = '0'; + return new HtmlElementContainer(canvasElement); + } + + public triggerEvent( + container: IContainer, + name: string, + details: unknown = null, + originalEvent?: IMouseEventArgs + ): void { + let element: HTMLElement = (container as HtmlElementContainer).element; + name = 'alphaTab.' + name; + let e: any = document.createEvent('CustomEvent'); + let originalMouseEvent: MouseEvent | null = originalEvent + ? (originalEvent as BrowserMouseEventArgs).mouseEvent + : null; + e.initCustomEvent(name, false, false, details); + if (originalMouseEvent) { + e.originalEvent = originalMouseEvent; + } + element.dispatchEvent(e); + if (window && 'jQuery' in window) { + let jquery: any = (window as any)['jQuery']; + let args: unknown[] = []; + args.push(details); + if (originalMouseEvent) { + args.push(originalMouseEvent); + } + jquery(element).trigger(name, args); + } + } + + public load(data: unknown, success: (score: Score) => void, error: (error: Error) => void): boolean { + if (data instanceof Score) { + success(data); + return true; + } + if (data instanceof ArrayBuffer) { + let byteArray: Uint8Array = new Uint8Array(data); + success(ScoreLoader.loadScoreFromBytes(byteArray, this._api.settings)); + return true; + } + if (data instanceof Uint8Array) { + success(ScoreLoader.loadScoreFromBytes(data, this._api.settings)); + return true; + } + if (typeof data === 'string') { + ScoreLoader.loadScoreAsync(data, success, error, this._api.settings); + return true; + } + return false; + } + + public loadSoundFont(data: unknown): boolean { + if (!this._api.player) { + return false; + } + + if (data instanceof ArrayBuffer) { + this._api.player.loadSoundFont(new Uint8Array(data)); + return true; + } + if (data instanceof Uint8Array) { + this._api.player.loadSoundFont(data); + return true; + } + if (typeof data === 'string') { + (this._api as AlphaTabApi).loadSoundFontFromUrl(data); + return true; + } + return false; + } + + public initialRender(): void { + this._api.renderer.preRender.on((_: boolean) => { + this._totalResultCount = 0; + }); + this.rootContainerBecameVisible.on(() => { + // rendering was possibly delayed due to invisible element + // in this case we need the correct width for autosize + this._api.renderer.width = this.rootContainer.width | 0; + this._api.renderer.updateSettings(this._api.settings); + if (this._contents) { + this._api.tex(this._contents, this._initialTrackIndexes ?? undefined); + this._initialTrackIndexes = null; + } else if (this._file) { + ScoreLoader.loadScoreAsync( + this._file, + s => { + this._api.renderScore(s, this._initialTrackIndexes ?? undefined); + this._initialTrackIndexes = null; + }, + e => { + this._api.onError(e as Error); + }, + this._api.settings + ); + } + }); + } + + private showSvgsInViewPort(): void { + let placeholders: NodeList = (this._api.canvasElement as HtmlElementContainer).element.querySelectorAll( + '[data-lazy=true]' + ); + for (let i: number = 0; i < placeholders.length; i++) { + let placeholder: HTMLElement = placeholders.item(i) as HTMLElement; + if (this.isElementInViewPort(placeholder)) { + this.replacePlaceholder(placeholder, (placeholder as any)['svg']); + } + } + } + + public isElementInViewPort(element: HTMLElement): boolean { + let rect: DOMRect = element.getBoundingClientRect(); + return ( + rect.top + rect.height >= 0 && + rect.top <= window.innerHeight && + rect.left + rect.width >= 0 && + rect.left <= window.innerWidth + ); + } + + private createStyleElement(settings: Settings): void { + let elementDocument: HTMLDocument = (this._api.container as HtmlElementContainer).element.ownerDocument!; + Environment.createStyleElement(elementDocument, settings.core.fontDirectory); + } + + public parseTracks(tracksData: unknown): number[] { + if (!tracksData) { + return []; + } + let tracks: number[] = []; + // decode string + if (typeof tracksData === 'string') { + try { + if (tracksData === 'all') { + return [-1]; + } + tracksData = JSON.parse(tracksData); + } catch (e) { + tracksData = [0]; + } + } + // decode array + if (typeof tracksData === 'number') { + tracks.push(tracksData); + } else if ('length' in (tracksData as any)) { + let length: number = (tracksData as any).length; + let array: unknown[] = tracksData as unknown[]; + for (let i: number = 0; i < length; i++) { + let item: unknown = array[i]; + let value: number = 0; + if (typeof item === 'number') { + value = item; + } else if ('index' in (item as any)) { + value = (item as any).index; + } else { + value = parseInt((item as any).toString()); + } + if (value >= 0 || value === -1) { + tracks.push(value); + } + } + } else if ('index' in (tracksData as any)) { + tracks.push((tracksData as any).index); + } + return tracks; + } + + private getDataAttributes(): Map { + let dataAttributes: Map = new Map(); + let element: HTMLElement = (this._api.container as HtmlElementContainer).element; + if (element.dataset) { + for (let key of Object.keys(element.dataset)) { + let value: unknown = (element.dataset as any)[key]; + try { + let stringValue: string = value as string; + value = JSON.parse(stringValue); + } catch (e) { + if (value === '') { + value = null; + } + } + dataAttributes.set(key, value); + } + } else { + for (let i: number = 0; i < element.attributes.length; i++) { + let attr: Attr = element.attributes.item(i)!; + let nodeName: string = attr.nodeName; + if (nodeName.startsWith('data-')) { + let keyParts: string[] = nodeName.substr(5).split('-'); + let key: string = keyParts[0]; + for (let j: number = 1; j < keyParts.length; j++) { + key += keyParts[j].substr(0, 1).toUpperCase() + keyParts[j].substr(1); + } + let value: unknown = attr.nodeValue; + try { + value = JSON.parse(value as string); + } catch (e) { + if (value === '') { + value = null; + } + } + dataAttributes.set(key, value); + } + } + } + return dataAttributes; + } + + public beginAppendRenderResults(renderResult: RenderFinishedEventArgs): void { + let canvasElement: HTMLElement = (this._api.canvasElement as HtmlElementContainer).element; + // null result indicates that the rendering finished + if (!renderResult) { + // so we remove elements that might be from a previous render session + while (canvasElement.childElementCount > this._totalResultCount) { + canvasElement.removeChild(canvasElement.lastChild!); + } + // directly show the elements in the viewport once we're done. + if (this._api.settings.core.enableLazyLoading) { + this.showSvgsInViewPort(); + } + } else { + let body: unknown = renderResult.renderResult; + if (typeof body === 'string') { + let placeholder: HTMLElement; + if (this._totalResultCount < canvasElement.childElementCount) { + placeholder = canvasElement.childNodes.item(this._totalResultCount) as HTMLElement; + } else { + placeholder = document.createElement('div'); + canvasElement.appendChild(placeholder); + } + placeholder.style.width = renderResult.width + 'px'; + placeholder.style.height = renderResult.height + 'px'; + placeholder.style.display = 'inline-block'; + if (this.isElementInViewPort(placeholder) || !this._api.settings.core.enableLazyLoading) { + this.replacePlaceholder(placeholder, body); + } else { + (placeholder as any)['svg'] = body; + placeholder.setAttribute('data-lazy', 'true'); + } + } else { + if (this._totalResultCount < canvasElement.childElementCount) { + canvasElement.replaceChild( + renderResult.renderResult as Node, + canvasElement.childNodes.item(this._totalResultCount) + ); + } else { + canvasElement.appendChild(renderResult.renderResult as Node); + } + } + this._totalResultCount++; + } + } + + private replacePlaceholder(placeholder: HTMLElement, body: any) { + if (typeof placeholder.outerHTML === 'string') { + placeholder.outerHTML = body; + } else { + const display = document.createElement('div'); + display.innerHTML = body; + placeholder.parentNode?.replaceChild(display.firstChild!, placeholder); + } + } + + /** + * This method creates the player. It detects browser compatibility and + * initializes a alphaSynth version for the client. + */ + public createWorkerPlayer(): IAlphaSynth | null { + let supportsWebAudio: boolean = 'ScriptProcessorNode' in window; + let alphaSynthScriptFile: string | null = Environment.scriptFile; + if (!alphaSynthScriptFile) { + Logger.error('Player', 'alphaTab script file could not be detected, player cannot initialize'); + return null; + } + + let player: AlphaSynthWebWorkerApi | null = null; + if (supportsWebAudio) { + Logger.debug('Player', 'Will use webworkers for synthesizing and web audio api for playback'); + player = new AlphaSynthWebWorkerApi( + new AlphaSynthWebAudioOutput(), + alphaSynthScriptFile, + this._api.settings.core.logLevel + ); + } + + if (!player) { + Logger.error('Player', 'Player requires webworkers and web audio api, browser unsupported', null); + } else { + player.ready.on(() => { + if (this._api.settings.player.soundFont) { + (this._api as AlphaTabApi).loadSoundFontFromUrl(this._api.settings.player.soundFont); + } + }); + } + return player; + } + + public beginInvoke(action: () => void): void { + window.requestAnimationFrame(() => { + action(); + }); + } + + public highlightElements(groupId: string): void { + let element: HTMLElement = (this._api.container as HtmlElementContainer).element; + let elementsToHighlight: HTMLCollection = element.getElementsByClassName(groupId); + for (let i: number = 0; i < elementsToHighlight.length; i++) { + elementsToHighlight.item(i)!.classList.add('at-highlight'); + } + } + + public removeHighlights(): void { + let element: HTMLElement = (this._api.container as HtmlElementContainer).element; + let elements: HTMLCollection = element.getElementsByClassName('at-highlight'); + while (elements.length > 0) { + elements.item(0)!.classList.remove('at-highlight'); + } + } + + public destroyCursors(): void { + let element: HTMLElement = (this._api.container as HtmlElementContainer).element; + let cursorWrapper: HTMLElement = element.querySelector('.at-cursors') as HTMLElement; + element.removeChild(cursorWrapper); + } + + public createCursors(): Cursors | null { + let element: HTMLElement = (this._api.container as HtmlElementContainer).element; + let cursorWrapper: HTMLElement = document.createElement('div'); + cursorWrapper.classList.add('at-cursors'); + let selectionWrapper: HTMLElement = document.createElement('div'); + selectionWrapper.classList.add('at-selection'); + let barCursor: HTMLElement = document.createElement('div'); + barCursor.classList.add('at-cursor-bar'); + let beatCursor: HTMLElement = document.createElement('div'); + beatCursor.classList.add('at-cursor-beat'); + // required css styles + element.style.position = 'relative'; + element.style.textAlign = 'left'; + cursorWrapper.style.position = 'absolute'; + cursorWrapper.style.zIndex = '1000'; + cursorWrapper.style.display = 'inline'; + cursorWrapper.style.pointerEvents = 'none'; + selectionWrapper.style.position = 'absolute'; + barCursor.style.position = 'absolute'; + beatCursor.style.position = 'absolute'; + beatCursor.style.transition = 'all 0s linear'; + // add cursors to UI + element.insertBefore(cursorWrapper, element.firstChild); + cursorWrapper.appendChild(selectionWrapper); + cursorWrapper.appendChild(barCursor); + cursorWrapper.appendChild(beatCursor); + return new Cursors( + new HtmlElementContainer(cursorWrapper), + new HtmlElementContainer(barCursor), + new HtmlElementContainer(beatCursor), + new HtmlElementContainer(selectionWrapper) + ); + } + + public getOffset(scrollContainer: IContainer | null, container: IContainer): Bounds { + let element: HTMLElement = (container as HtmlElementContainer).element; + let bounds: DOMRect = element.getBoundingClientRect(); + let top: number = bounds.top + element.ownerDocument!.defaultView!.pageYOffset; + let left: number = bounds.left + element.ownerDocument!.defaultView!.pageXOffset; + if (scrollContainer) { + let scrollElement: HTMLElement = (scrollContainer as HtmlElementContainer).element; + let nodeName: string = scrollElement.nodeName.toLowerCase(); + if (nodeName !== 'html' && nodeName !== 'body') { + let scrollElementOffset: Bounds = this.getOffset(null, scrollContainer); + top = top + scrollElement.scrollTop - scrollElementOffset.y; + left = left + scrollElement.scrollLeft - scrollElementOffset.x; + } + } + + let b = new Bounds(); + b.x = left; + b.y = top; + b.w = bounds.width; + b.h = bounds.height; + return b; + } + + public getScrollContainer(): IContainer { + let scrollElement: HTMLElement = + // tslint:disable-next-line: strict-type-predicates + typeof this._api.settings.player.scrollElement === 'string' + ? (document.querySelector(this._api.settings.player.scrollElement) as HTMLElement) + : (this._api.settings.player.scrollElement as HTMLElement); + let nodeName: string = scrollElement.nodeName.toLowerCase(); + if (nodeName === 'html' || nodeName === 'body') { + // https://github.com/CoderLine/alphaTab/issues/205 + // https://github.com/CoderLine/alphaTab/issues/354 + // https://dev.opera.com/articles/fixing-the-scrolltop-bug/ + if ('scrollingElement' in document) { + scrollElement = document.scrollingElement as HTMLElement; + } else { + const userAgent = navigator.userAgent; + if (userAgent.indexOf('WebKit') !== -1) { + scrollElement = (document as HTMLDocument).body; + } else { + scrollElement = (document as HTMLDocument).documentElement; + } + } + } + return new HtmlElementContainer(scrollElement); + } + + public createSelectionElement(): IContainer | null { + let element: HTMLElement = document.createElement('div'); + element.style.position = 'absolute'; + return new HtmlElementContainer(element); + } + + public scrollToY(element: IContainer, scrollTargetY: number, speed: number): void { + this.internalScrollToY((element as HtmlElementContainer).element, scrollTargetY, speed); + } + + public scrollToX(element: IContainer, scrollTargetY: number, speed: number): void { + this.internalScrollToX((element as HtmlElementContainer).element, scrollTargetY, speed); + } + + private internalScrollToY(element: HTMLElement, scrollTargetY: number, speed: number): void { + let startY: number = element.scrollTop; + let diff: number = scrollTargetY - startY; + let start: number = 0; + let step = (x: number) => { + if (start === 0) { + start = x; + } + let time: number = x - start; + let percent: number = Math.min(time / speed, 1); + element.scrollTop = (startY + diff * percent) | 0; + if (time < speed) { + window.requestAnimationFrame(step); + } + }; + window.requestAnimationFrame(step); + } + + private internalScrollToX(element: HTMLElement, scrollTargetX: number, speed: number): void { + let startX: number = element.scrollLeft; + let diff: number = scrollTargetX - startX; + let start: number = 0; + let step = (t: number) => { + if (start === 0) { + start = t; + } + let time: number = t - start; + let percent: number = Math.min(time / speed, 1); + element.scrollLeft = (startX + diff * percent) | 0; + if (time < speed) { + window.requestAnimationFrame(step); + } + }; + window.requestAnimationFrame(step); + } +} diff --git a/src/platform/javascript/Html5Canvas.ts b/src/platform/javascript/Html5Canvas.ts new file mode 100644 index 000000000..c77fe41ae --- /dev/null +++ b/src/platform/javascript/Html5Canvas.ts @@ -0,0 +1,296 @@ +import { Color } from '@src/model/Color'; +import { Font, FontStyle } from '@src/model/Font'; +import { ICanvas, TextAlign, TextBaseline } from '@src/platform/ICanvas'; +import { MusicFontSymbol } from '@src/rendering/glyphs/MusicFontSymbol'; +import { Settings } from '@src/Settings'; + +/** + * A canvas implementation for HTML5 canvas + * @target web + */ +export class Html5Canvas implements ICanvas { + private _measureCanvas: HTMLCanvasElement; + private _measureContext: CanvasRenderingContext2D; + private _canvas: HTMLCanvasElement | null = null; + private _context!: CanvasRenderingContext2D; + private _color: Color = new Color(0, 0, 0, 0xff); + private _font: Font = new Font('Arial', 10, FontStyle.Plain); + private _musicFont: Font; + private _lineWidth: number = 0; + + public settings!: Settings; + + public constructor() { + let fontElement: HTMLElement = document.createElement('span'); + fontElement.classList.add('at'); + document.body.appendChild(fontElement); + let style: CSSStyleDeclaration = window.getComputedStyle(fontElement); + let family: string = style.fontFamily; + if (family.startsWith('"') || family.startsWith("'")) { + family = family.substr(1, family.length - 2); + } + this._musicFont = new Font(family, parseFloat(style.fontSize), FontStyle.Plain); + this._measureCanvas = document.createElement('canvas'); + this._measureCanvas.width = 10; + this._measureCanvas.height = 10; + this._measureCanvas.style.width = '10px'; + this._measureCanvas.style.height = '10px'; + this._measureContext = this._measureCanvas.getContext('2d')!; + this._measureContext.textBaseline = 'hanging'; + } + + public onRenderFinished(): unknown { + return null; + } + + public beginRender(width: number, height: number): void { + this._canvas = document.createElement('canvas'); + this._canvas.width = width | 0; + this._canvas.height = height | 0; + this._canvas.style.width = width + 'px'; + this._canvas.style.height = height + 'px'; + this._context = this._canvas.getContext('2d')!; + this._context.textBaseline = 'hanging'; + this._context.lineWidth = this._lineWidth; + } + + public endRender(): unknown { + let result: HTMLCanvasElement = this._canvas!; + this._canvas = null; + return result; + } + + public get color(): Color { + return this._color; + } + + public set color(value: Color) { + if (this._color.rgba === value.rgba) { + return; + } + this._color = value; + this._context.strokeStyle = value.rgba; + this._context.fillStyle = value.rgba; + } + + public get lineWidth(): number { + return this._lineWidth; + } + + public set lineWidth(value: number) { + this._lineWidth = value; + if (this._context) { + this._context.lineWidth = value; + } + } + + public fillRect(x: number, y: number, w: number, h: number): void { + if (w > 0) { + this._context.fillRect((x | 0), (y | 0), w, h); + } + } + + public strokeRect(x: number, y: number, w: number, h: number): void { + this._context.strokeRect((x | 0), (y | 0), w, h); + } + + public beginPath(): void { + this._context.beginPath(); + } + + public closePath(): void { + this._context.closePath(); + } + + public moveTo(x: number, y: number): void { + this._context.moveTo((x | 0), (y | 0)); + } + + public lineTo(x: number, y: number): void { + this._context.lineTo((x | 0), (y | 0)); + } + + public quadraticCurveTo(cpx: number, cpy: number, x: number, y: number): void { + this._context.quadraticCurveTo(cpx, cpy, x, y); + } + + public bezierCurveTo(cp1x: number, cp1y: number, cp2x: number, cp2y: number, x: number, y: number): void { + this._context.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y); + } + + public fillCircle(x: number, y: number, radius: number): void { + this._context.beginPath(); + this._context.arc( + (x | 0), + (y | 0), + radius, + 0, + Math.PI * 2, + true + ); + this.fill(); + } + + public strokeCircle(x: number, y: number, radius: number): void { + this._context.beginPath(); + this._context.arc( + (x | 0), + (y | 0), + radius, + 0, + Math.PI * 2, + true + ); + this.stroke(); + } + + public fill(): void { + this._context.fill(); + } + + public stroke(): void { + this._context.stroke(); + } + + public get font(): Font { + return this._font; + } + + public set font(value: Font) { + this._font = value; + if (this._context) { + this._context.font = value.toCssString(this.settings.display.scale); + } + this._measureContext.font = value.toCssString(this.settings.display.scale); + } + + public get textAlign(): TextAlign { + switch (this._context.textAlign) { + case 'left': + return TextAlign.Left; + case 'center': + return TextAlign.Center; + case 'right': + return TextAlign.Right; + default: + return TextAlign.Left; + } + } + + public set textAlign(value: TextAlign) { + switch (value) { + case TextAlign.Left: + this._context.textAlign = 'left'; + break; + case TextAlign.Center: + this._context.textAlign = 'center'; + break; + case TextAlign.Right: + this._context.textAlign = 'right'; + break; + } + } + + public get textBaseline(): TextBaseline { + switch (this._context.textBaseline) { + case 'hanging': + return TextBaseline.Top; + case 'middle': + return TextBaseline.Middle; + case 'bottom': + return TextBaseline.Bottom; + default: + return TextBaseline.Top; + } + } + + public set textBaseline(value: TextBaseline) { + switch (value) { + case TextBaseline.Top: + this._context.textBaseline = 'hanging'; + break; + case TextBaseline.Middle: + this._context.textBaseline = 'middle'; + break; + case TextBaseline.Bottom: + this._context.textBaseline = 'bottom'; + break; + } + } + + public beginGroup(_: string): void { + // not supported + } + + public endGroup(): void { + // not supported + } + + public fillText(text: string, x: number, y: number): void { + this._context.fillText(text, (x | 0), (y | 0)); + } + + public measureText(text: string): number { + return this._measureContext.measureText(text).width; + } + + public fillMusicFontSymbol( + x: number, + y: number, + scale: number, + symbol: MusicFontSymbol, + centerAtPosition: boolean = false + ): void { + if (symbol === MusicFontSymbol.None) { + return; + } + this.fillMusicFontSymbolText(x, y, scale, String.fromCharCode(symbol), centerAtPosition); + } + + public fillMusicFontSymbols( + x: number, + y: number, + scale: number, + symbols: MusicFontSymbol[], + centerAtPosition: boolean = false + ): void { + let s: string = ''; + for (let symbol of symbols) { + if (symbol !== MusicFontSymbol.None) { + s += String.fromCharCode(symbol); + } + } + this.fillMusicFontSymbolText(x, y, scale, s, centerAtPosition); + } + + private fillMusicFontSymbolText( + x: number, + y: number, + scale: number, + symbols: string, + centerAtPosition: boolean = false + ): void { + let textAlign = this._context.textAlign; + let baseLine = this._context.textBaseline; + let font: string = this._context.font; + this._context.font = this._musicFont.toCssString(scale); + this._context.textBaseline = 'middle'; + if (centerAtPosition) { + this._context.textAlign = 'center'; + } + this._context.fillText(symbols, (x | 0), (y | 0)); + this._context.textBaseline = baseLine; + this._context.font = font; + this._context.textAlign = textAlign; + } + + public beginRotate(centerX: number, centerY: number, angle: number): void { + this._context.save(); + this._context.translate(centerX, centerY); + this._context.rotate((angle * Math.PI) / 180.0); + } + + public endRotate(): void { + this._context.restore(); + } +} diff --git a/src/platform/javascript/HtmlElementContainer.ts b/src/platform/javascript/HtmlElementContainer.ts new file mode 100644 index 000000000..5641e0b23 --- /dev/null +++ b/src/platform/javascript/HtmlElementContainer.ts @@ -0,0 +1,177 @@ +import { IEventEmitter, IEventEmitterOfT } from '@src/EventEmitter'; +import { IContainer } from '@src/platform/IContainer'; +import { IMouseEventArgs } from '@src/platform/IMouseEventArgs'; +import { BrowserMouseEventArgs } from '@src/platform/javascript/BrowserMouseEventArgs'; + +/** + * @target web + */ +export class HtmlElementContainer implements IContainer { + public get top(): number { + return parseFloat(this.element.style.top); + } + + public set top(value: number) { + this.element.style.top = value + 'px'; + } + + public get left(): number { + return parseFloat(this.element.style.top); + } + + public set left(value: number) { + this.element.style.left = value + 'px'; + } + + public get width(): number { + return this.element.offsetWidth; + } + + public set width(value: number) { + this.element.style.width = value + 'px'; + } + + public get scrollLeft(): number { + return this.element.scrollLeft; + } + + public set scrollLeft(value: number) { + this.element.scrollTop = value; + } + + public get scrollTop(): number { + return this.element.scrollLeft; + } + + public set scrollTop(value: number) { + this.element.scrollTop = value; + } + + public get height(): number { + return this.element.offsetHeight; + } + + public set height(value: number) { + if (value >= 0) { + this.element.style.height = value + 'px'; + } else { + this.element.style.height = '100%'; + } + } + + public get isVisible(): boolean { + return !!this.element.offsetWidth || !!this.element.offsetHeight || !!this.element.getClientRects().length; + } + + public readonly element: HTMLElement; + + public constructor(element: HTMLElement) { + this.element = element; + + this.mouseDown = { + on: (value: any) => { + this.element.addEventListener( + 'mousedown', + e => { + value(new BrowserMouseEventArgs(e)); + }, + true + ); + }, + off: (value: any) => { + // not supported due to wrapping + } + }; + + this.mouseUp = { + on: (value: any) => { + this.element.addEventListener( + 'mouseup', + e => { + value(new BrowserMouseEventArgs(e)); + }, + true + ); + }, + off: (value: any) => { + // not supported due to wrapping + } + }; + + this.mouseMove = { + on: (value: any) => { + this.element.addEventListener( + 'mousemove', + e => { + value(new BrowserMouseEventArgs(e)); + }, + true + ); + }, + off: (_: any) => { + // not supported due to wrapping + } + }; + + this.scroll = { + on: (value: any) => { + window.addEventListener('scroll', value, true); + }, + off: (value: any) => { + window.removeEventListener('scroll', value, true); + } + }; + + this.resize = { + on: (value: any) => { + window.addEventListener('resize', value, true); + }, + off: (value: any) => { + window.removeEventListener('resize', value, true); + } + }; + } + + public stopAnimation(): void { + this.element.style.transition = 'none'; + } + + public transitionToX(duration: number, x: number): void { + this.element.style.transition = 'all 0s linear'; + this.element.style.transitionDuration = duration + 'ms'; + this.element.style.left = x + 'px'; + } + + /** + * This event occurs when a scroll on the control happened. + */ + public scroll: IEventEmitter; + + /** + * This event occurs when the control was resized. + */ + public resize: IEventEmitter; + + /** + * This event occurs when a mouse/finger press happened on the control. + */ + public mouseDown: IEventEmitterOfT; + + /** + * This event occurs when a mouse/finger moves on top of the control. + */ + public mouseMove: IEventEmitterOfT; + + /** + * This event occurs when a mouse/finger is released from the control. + */ + public mouseUp: IEventEmitterOfT; + + public appendChild(child: IContainer): void { + this.element.appendChild((child as HtmlElementContainer).element); + } + + public clear(): void { + this.element.innerHTML = ''; + } +} diff --git a/src/platform/javascript/IWorkerScope.ts b/src/platform/javascript/IWorkerScope.ts new file mode 100644 index 000000000..18d1ad7a6 --- /dev/null +++ b/src/platform/javascript/IWorkerScope.ts @@ -0,0 +1,7 @@ +/** + * @target web + */ +export interface IWorkerScope { + addEventListener(eventType: string, listener: (e: MessageEvent) => void, capture?: boolean): void; + postMessage(message: unknown): void; +} diff --git a/src/platform/javascript/JQueryAlphaTab.ts b/src/platform/javascript/JQueryAlphaTab.ts new file mode 100644 index 000000000..cf56fb732 --- /dev/null +++ b/src/platform/javascript/JQueryAlphaTab.ts @@ -0,0 +1,218 @@ +import { IAlphaSynth } from '@src/synth/IAlphaSynth'; +import { PlayerState } from '@src/synth/PlayerState'; +import { Score } from '@src/model/Score'; +import { Track } from '@src/model/Track'; +import { AlphaTabApi } from '@src/platform/javascript/AlphaTabApi'; +import { IScoreRenderer } from '@src/rendering/IScoreRenderer'; +import { Settings } from '@src/Settings'; +import { Logger } from '@src/Logger'; + +/** + * @target web + */ +export declare class jQuery extends Array { + public constructor(v?: any); + + public readonly context: HTMLElement; + + public readonly length: number; + + public data(key: string): unknown; + public data(key: string, value: any): void; + + public removeData(key: string): void; + + public each(action: (i: number, x: HTMLElement) => void): void; + + public empty(): jQuery; +} + +/** + * @target web + */ +export class JQueryAlphaTab { + public exec(element: HTMLElement, method: string, args: any[]): unknown { + if (typeof method !== 'string') { + args = [method]; + method = 'init'; + } + if (method.charCodeAt(0) === 95 || method === 'exec') { + return null; + } + let jElement: jQuery = new jQuery(element); + let context: AlphaTabApi = jElement.data('alphaTab') as AlphaTabApi; + if (method === 'destroy' && !context) { + return null; + } + if (method !== 'init' && !context) { + throw new Error('alphaTab not initialized'); + } + let apiMethod: Function = (this as any)[method]; + if (apiMethod) { + let realArgs: string[] = ([jElement, context] as any[]).concat(args); + return apiMethod.apply(this, realArgs); + } else { + Logger.error('Api', "Method '" + method + "' does not exist on jQuery.alphaTab"); + return null; + } + } + + public init(element: jQuery, context: AlphaTabApi, options: any): void { + if (!context) { + context = new AlphaTabApi(element[0], options); + element.data('alphaTab', context); + for (let listener of this._initListeners) { + listener(element, context, options); + } + } + } + + public destroy(element: jQuery, context: AlphaTabApi): void { + element.removeData('alphaTab'); + context.destroy(); + } + + public print(element: jQuery, context: AlphaTabApi, width: string): void { + context.print(width); + } + + public load(element: jQuery, context: AlphaTabApi, data: unknown, tracks?: number[]): boolean { + return context.load(data, tracks); + } + + public render(element: jQuery, context: AlphaTabApi): void { + context.render(); + } + + public renderScore(element: jQuery, context: AlphaTabApi, score: Score, tracks?: number[]): void { + context.renderScore(score, tracks); + } + + public renderTracks(element: jQuery, context: AlphaTabApi, tracks: Track[]): void { + context.renderTracks(tracks); + } + + public invalidate(element: jQuery, context: AlphaTabApi): void { + context.render(); + } + + public tex(element: jQuery, context: AlphaTabApi, tex: string, tracks: number[]): void { + context.tex(tex, tracks); + } + + public muteTrack(element: jQuery, context: AlphaTabApi, tracks: Track[], mute: boolean): void { + context.changeTrackMute(tracks, mute); + } + + public soloTrack(element: jQuery, context: AlphaTabApi, tracks: Track[], solo: boolean): void { + context.changeTrackSolo(tracks, solo); + } + + public trackVolume(element: jQuery, context: AlphaTabApi, tracks: Track[], volume: number): void { + context.changeTrackVolume(tracks, volume); + } + + public loadSoundFont(element: jQuery, context: AlphaTabApi, value: unknown): void { + context.loadSoundFont(value); + } + + public pause(element: jQuery, context: AlphaTabApi): void { + context.pause(); + } + + public play(element: jQuery, context: AlphaTabApi): boolean { + return context.play(); + } + + public playPause(element: jQuery, context: AlphaTabApi): void { + context.playPause(); + } + + public stop(element: jQuery, context: AlphaTabApi): void { + context.stop(); + } + + public api(element: jQuery, context: AlphaTabApi): AlphaTabApi { + return context; + } + + public player(element: jQuery, context: AlphaTabApi): IAlphaSynth | null { + return context.player; + } + + public isReadyForPlayback(element: jQuery, context: AlphaTabApi): boolean { + return context.isReadyForPlayback; + } + + public playerState(element: jQuery, context: AlphaTabApi): PlayerState { + return context.playerState; + } + + public masterVolume(element: jQuery, context: AlphaTabApi, masterVolume?: number): number { + if (typeof masterVolume === 'number') { + context.masterVolume = masterVolume; + } + return context.masterVolume; + } + + public metronomeVolume(element: jQuery, context: AlphaTabApi, metronomeVolume?: number): number { + if (typeof metronomeVolume === 'number') { + context.metronomeVolume = metronomeVolume; + } + return context.metronomeVolume; + } + + public playbackSpeed(element: jQuery, context: AlphaTabApi, playbackSpeed?: number): number { + if (typeof playbackSpeed === 'number') { + context.playbackSpeed = playbackSpeed; + } + return context.playbackSpeed; + } + + public tickPosition(element: jQuery, context: AlphaTabApi, tickPosition?: number): number { + if (typeof tickPosition === 'number') { + context.tickPosition = tickPosition; + } + return context.tickPosition; + } + + public timePosition(element: jQuery, context: AlphaTabApi, timePosition?: number): number { + if (typeof timePosition === 'number') { + context.timePosition = timePosition; + } + return context.timePosition; + } + + public loop(element: jQuery, context: AlphaTabApi, loop?: boolean): boolean { + if (typeof loop === 'boolean') { + context.isLooping = loop; + } + return context.isLooping; + } + + public renderer(element: jQuery, context: AlphaTabApi): IScoreRenderer { + return context.renderer; + } + + public score(element: jQuery, context: AlphaTabApi): Score | null { + return context.score; + } + + public settings(element: jQuery, context: AlphaTabApi): Settings { + return context.settings; + } + + public tracks(element: jQuery, context: AlphaTabApi): Track[] { + return context.tracks; + } + + private _initListeners: ((jq: jQuery, api: AlphaTabApi, params: any[]) => void)[] = []; + + public _oninit(listener: (jq: jQuery, api: AlphaTabApi, params: any[]) => void): void { + this._initListeners.push(listener); + } + + public static restore(selector: string) { + new jQuery(selector).empty().removeData('alphaTab'); + } +} diff --git a/src/platform/svg/CssFontSvgCanvas.ts b/src/platform/svg/CssFontSvgCanvas.ts new file mode 100644 index 000000000..5c4463154 --- /dev/null +++ b/src/platform/svg/CssFontSvgCanvas.ts @@ -0,0 +1,63 @@ +import { TextAlign } from '@src/platform/ICanvas'; +import { SvgCanvas } from '@src/platform/svg/SvgCanvas'; +import { MusicFontSymbol } from '@src/rendering/glyphs/MusicFontSymbol'; + +/** + * This SVG canvas renders the music symbols by adding a CSS class 'at' to all elements. + */ +export class CssFontSvgCanvas extends SvgCanvas { + public constructor() { + super(); + } + + public fillMusicFontSymbol( + x: number, + y: number, + scale: number, + symbol: MusicFontSymbol, + centerAtPosition: boolean = false + ): void { + if (symbol === MusicFontSymbol.None) { + return; + } + this.fillMusicFontSymbolText(x, y, scale, `&#${symbol};`, centerAtPosition); + } + + public fillMusicFontSymbols( + x: number, + y: number, + scale: number, + symbols: MusicFontSymbol[], + centerAtPosition: boolean = false + ): void { + let s: string = ''; + for (let symbol of symbols) { + if (symbol !== MusicFontSymbol.None) { + s += `&#${symbol};`; + } + } + this.fillMusicFontSymbolText(x, y, scale, s, centerAtPosition); + } + + private fillMusicFontSymbolText( + x: number, + y: number, + scale: number, + symbols: string, + centerAtPosition: boolean = false + ): void { + this.buffer += `${symbols}`; + } +} diff --git a/src/platform/svg/FontSizes.ts b/src/platform/svg/FontSizes.ts new file mode 100644 index 000000000..fbe354b39 --- /dev/null +++ b/src/platform/svg/FontSizes.ts @@ -0,0 +1,92 @@ +import { FontStyle } from '@src/model/Font'; +import { Environment } from '@src/Environment'; + +/** + * This public class stores text widths for several fonts and allows width calculation + * @partial + */ +export class FontSizes { + // prettier-ignore + public static Georgia: Uint8Array = new Uint8Array([ + 3, 4, 5, 7, 7, 9, 8, 2, 4, 4, 5, 7, 3, 4, 3, 5, 7, 5, 6, 6, 6, 6, 6, 6, 7, 6, 3, 3, 7, + 7, 7, 5, 10, 7, 7, 7, 8, 7, 7, 8, 9, 4, 6, 8, 7, 10, 8, 8, 7, 8, 8, 6, 7, 8, 7, 11, 8, + 7, 7, 4, 5, 4, 7, 7, 6, 6, 6, 5, 6, 5, 4, 6, 6, 3, 3, 6, 3, 10, 6, 6, 6, 6, 5, 5, 4, 6, + 5, 8, 6, 5, 5, 5, 4, 5, 7, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 3, 4, 6, 7, 6, 7, 4, 6, 6, 10, 6, 6, 7, 0, 10, 7, + 5, 7, 6, 6, 6, 6, 6, 3, 6, 6, 6, 6, 12, 12, 12, 5, 7, 7, 7, 7, 7, 7, 11, 7, 7, 7, 7, 7, + 4, 4, 4, 4, 8, 8, 8, 8, 8, 8, 8, 7, 8, 8, 8, 8, 8, 7, 7, 6, 6, 6, 6, 6, 6, 6, 8, 5, 5, + 5, 5, 5, 3, 3, 3, 3, 6, 6, 6, 6, 6, 6, 6, 7, 6, 6, 6, 6, 6, 5, 6 + ]); + + // prettier-ignore + public static Arial: Uint8Array = new Uint8Array([ + 3, 3, 4, 6, 6, 10, 7, 2, 4, 4, 4, 6, 3, 4, 3, 3, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 3, 3, 6, + 6, 6, 6, 11, 7, 7, 8, 8, 7, 7, 9, 8, 3, 6, 7, 6, 9, 8, 9, 7, 9, 8, 7, 7, 8, 7, 10, 7, 7, + 7, 3, 3, 3, 5, 6, 4, 6, 6, 6, 6, 6, 3, 6, 6, 2, 2, 6, 2, 9, 6, 6, 6, 6, 4, 6, 3, 6, 6, + 8, 6, 6, 6, 4, 3, 4, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 6, 6, 6, 6, 3, 6, 4, 8, 4, 6, 6, 0, 8, 6, 4, + 6, 4, 4, 4, 6, 6, 4, 4, 4, 4, 6, 9, 9, 9, 7, 7, 7, 7, 7, 7, 7, 11, 8, 7, 7, 7, 7, 3, 3, + 3, 3, 8, 8, 9, 9, 9, 9, 9, 6, 9, 8, 8, 8, 8, 7, 7, 7, 6, 6, 6, 6, 6, 6, 10, 6, 6, 6, 6, + 6, 3, 3, 3, 3, 6, 6, 6, 6, 6, 6, 6, 6, 7, 6, 6, 6, 6, 6, 6 + ]); + + public static FontSizeLookupTables: Map = new Map([ + ['Arial', FontSizes.Arial], + ["'Arial'", FontSizes.Arial], + ['"Arial"', FontSizes.Arial], + ['Georgia', FontSizes.Georgia], + ["'Georgia'", FontSizes.Georgia], + ['"Georgia"', FontSizes.Georgia] + ]); + + public static readonly ControlChars: number = 0x20; + + /** + * @target web + */ + public static generateFontLookup(family: string): void { + if (FontSizes.FontSizeLookupTables.has(family)) { + return; + } + + if (!Environment.isRunningInWorker) { + let canvas: HTMLCanvasElement = document.createElement('canvas'); + let measureContext: CanvasRenderingContext2D = canvas.getContext('2d')!; + measureContext.font = `11px ${family}`; + let sizes: number[] = []; + for (let i: number = 0x20; i < 255; i++) { + let s: string = String.fromCharCode(i); + sizes.push(measureContext.measureText(s).width); + } + + let data: Uint8Array = new Uint8Array(sizes); + FontSizes.FontSizeLookupTables.set(family, data); + } else { + FontSizes.FontSizeLookupTables.set(family, new Uint8Array([8])); + } + } + + public static measureString(s: string, family: string, size: number, style: FontStyle): number { + let data: Uint8Array; + let dataSize: number = 11; + if (!FontSizes.FontSizeLookupTables.has(family)) { + FontSizes.generateFontLookup(family); + } + data = FontSizes.FontSizeLookupTables.get(family)!; + let factor: number = 1; + if ((style & FontStyle.Italic) !== 0) { + factor *= 1.2; + } + if ((style & FontStyle.Bold) !== 0) { + factor *= 1.2; + } + let stringSize: number = 0; + for (let i: number = 0; i < s.length; i++) { + let code: number = Math.min(data.length - 1, s.charCodeAt(i) - 32); + if (code >= 0) { + stringSize += (data[code] * size) / dataSize; + } + } + return stringSize * factor; + } +} diff --git a/src/platform/svg/SvgCanvas.ts b/src/platform/svg/SvgCanvas.ts new file mode 100644 index 000000000..04b56a150 --- /dev/null +++ b/src/platform/svg/SvgCanvas.ts @@ -0,0 +1,203 @@ +import { Color } from '@src/model/Color'; +import { Font, FontStyle } from '@src/model/Font'; +import { ICanvas, TextAlign, TextBaseline } from '@src/platform/ICanvas'; +import { FontSizes } from '@src/platform/svg/FontSizes'; +import { MusicFontSymbol } from '@src/rendering/glyphs/MusicFontSymbol'; +import { Settings } from '@src/Settings'; + +/** + * A canvas implementation storing SVG data + */ +export abstract class SvgCanvas implements ICanvas { + protected buffer: string = ''; + + private _currentPath: string = ''; + private _currentPathIsEmpty: boolean = true; + + public color: Color = new Color(255, 255, 255, 0xff); + public lineWidth: number = 1; + public font: Font = new Font('Arial', 10, FontStyle.Plain); + public textAlign: TextAlign = TextAlign.Left; + public textBaseline: TextBaseline = TextBaseline.Top; + public settings!: Settings; + + public beginRender(width: number, height: number): void { + this.buffer = `\n`; + this._currentPath = ''; + this._currentPathIsEmpty = true; + } + + public beginGroup(identifier: string): void { + this.buffer += ``; + } + + public endGroup(): void { + this.buffer += ''; + } + + public endRender(): unknown { + this.buffer += ''; + return this.buffer; + } + + public fillRect(x: number, y: number, w: number, h: number): void { + if (w > 0) { + this.buffer += `\n`; + } + } + + public strokeRect(x: number, y: number, w: number, h: number): void { + this.buffer += `${text}`; + this.buffer += s; + } + + protected getSvgTextAlignment(textAlign: TextAlign): string { + switch (textAlign) { + case TextAlign.Left: + return 'start'; + case TextAlign.Center: + return 'middle'; + case TextAlign.Right: + return 'end'; + } + return ''; + } + + protected getSvgBaseLine(): string { + switch (this.textBaseline) { + case TextBaseline.Top: + return `dy="1.25ex"`; + case TextBaseline.Middle: + return `dy="0.5ex"`; + case TextBaseline.Bottom: + default: + return ''; + } + } + + public measureText(text: string): number { + if (!text) { + return 0; + } + return FontSizes.measureString(text, this.font.family, this.font.size, this.font.style); + } + + public abstract fillMusicFontSymbol( + x: number, + y: number, + scale: number, + symbol: MusicFontSymbol, + centerAtPosition?: boolean + ): void; + + public abstract fillMusicFontSymbols( + x: number, + y: number, + scale: number, + symbols: MusicFontSymbol[], + centerAtPosition?: boolean + ): void; + + public onRenderFinished(): unknown { + // nothing to do + return null; + } + + public beginRotate(centerX: number, centerY: number, angle: number): void { + this.buffer += ''; + } + + public endRotate(): void { + this.buffer += ''; + } +} diff --git a/src/rendering/BarRendererBase.ts b/src/rendering/BarRendererBase.ts new file mode 100644 index 000000000..e7aaae73f --- /dev/null +++ b/src/rendering/BarRendererBase.ts @@ -0,0 +1,449 @@ +import { Bar } from '@src/model/Bar'; +import { Beat } from '@src/model/Beat'; +import { Note } from '@src/model/Note'; +import { SimileMark } from '@src/model/SimileMark'; +import { Voice } from '@src/model/Voice'; +import { ICanvas } from '@src/platform/ICanvas'; +import { BeatXPosition } from '@src/rendering/BeatXPosition'; +import { BeatContainerGlyph } from '@src/rendering/glyphs/BeatContainerGlyph'; +import { BeatGlyphBase } from '@src/rendering/glyphs/BeatGlyphBase'; +import { Glyph } from '@src/rendering/glyphs/Glyph'; +import { LeftToRightLayoutingGlyphGroup } from '@src/rendering/glyphs/LeftToRightLayoutingGlyphGroup'; +import { MusicFontSymbol } from '@src/rendering/glyphs/MusicFontSymbol'; +import { VoiceContainerGlyph } from '@src/rendering/glyphs/VoiceContainerGlyph'; +import { ScoreRenderer } from '@src/rendering/ScoreRenderer'; +import { BarLayoutingInfo } from '@src/rendering/staves/BarLayoutingInfo'; +import { RenderStaff } from '@src/rendering/staves/RenderStaff'; +import { BarBounds } from '@src/rendering/utils/BarBounds'; +import { BarHelpers } from '@src/rendering/utils/BarHelpers'; +import { Bounds } from '@src/rendering/utils/Bounds'; +import { MasterBarBounds } from '@src/rendering/utils/MasterBarBounds'; +import { RenderingResources } from '@src/RenderingResources'; +import { Settings } from '@src/Settings'; +import { BeatOnNoteGlyphBase } from './glyphs/BeatOnNoteGlyphBase'; + +/** + * Lists the different position modes for {@link BarRendererBase.getNoteY} + */ +export enum NoteYPosition { + /** + * Gets the note y-position on top of the note stem or tab number. + */ + TopWithStem, + /** + * Gets the note y-position on top of the note head or tab number. + */ + Top, + /** + * Gets the note y-position on the center of the note head or tab number. + */ + Center, + /** + * Gets the note y-position on the bottom of the note head or tab number. + */ + Bottom, + /** + * Gets the note y-position on the bottom of the note stem or tab number. + */ + BottomWithStem +} + +/** + * Lists the different position modes for {@link BarRendererBase.getNoteX} + */ +export enum NoteXPosition { + /** + * Gets the note x-position on left of the note head or tab number. + */ + Left, + /** + * Gets the note x-position on the center of the note head or tab number. + */ + Center, + /** + * Gets the note x-position on the right of the note head or tab number. + */ + Right +} + +/** + * This is the base public class for creating blocks which can render bars. + */ +export class BarRendererBase { + private _preBeatGlyphs: LeftToRightLayoutingGlyphGroup = new LeftToRightLayoutingGlyphGroup(); + private _voiceContainers: Map = new Map(); + private _postBeatGlyphs: LeftToRightLayoutingGlyphGroup = new LeftToRightLayoutingGlyphGroup(); + + public get nextRenderer(): BarRendererBase | null { + if (!this.bar || !this.bar.nextBar) { + return null; + } + return this.scoreRenderer.layout!.getRendererForBar(this.staff.staveId, this.bar.nextBar); + } + + public get previousRenderer(): BarRendererBase | null { + if (!this.bar || !this.bar.previousBar) { + return null; + } + return this.scoreRenderer.layout!.getRendererForBar(this.staff.staveId, this.bar.previousBar); + } + + public scoreRenderer: ScoreRenderer; + public staff!: RenderStaff; + public layoutingInfo!: BarLayoutingInfo; + public bar!: Bar; + + public x: number = 0; + public y: number = 0; + public width: number = 0; + public height: number = 0; + public index: number = 0; + public topOverflow: number = 0; + public bottomOverflow: number = 0; + public helpers!: BarHelpers; + + /** + * Gets or sets whether this renderer is linked to the next one + * by some glyphs like a vibrato effect + */ + public isLinkedToPrevious: boolean = false; + + /** + * Gets or sets whether this renderer can wrap to the next line + * or it needs to stay connected to the previous one. + * (e.g. when having double bar repeats we must not separate the 2 bars) + */ + public canWrap: boolean = true; + + public constructor(renderer: ScoreRenderer, bar: Bar) { + this.scoreRenderer = renderer; + this.bar = bar; + if (bar) { + this.helpers = new BarHelpers(bar); + } + } + + public registerOverflowTop(topOverflow: number): void { + if (topOverflow > this.topOverflow) { + this.topOverflow = topOverflow; + } + } + + public registerOverflowBottom(bottomOverflow: number): void { + if (bottomOverflow > this.bottomOverflow) { + this.bottomOverflow = bottomOverflow; + } + } + + public scaleToWidth(width: number): void { + // preBeat and postBeat glyphs do not get resized + let containerWidth: number = width - this._preBeatGlyphs.width - this._postBeatGlyphs.width; + this._voiceContainers.forEach(container => { + container.scaleToWidth(containerWidth); + }); + this._postBeatGlyphs.x = this._preBeatGlyphs.x + this._preBeatGlyphs.width + containerWidth; + this.width = width; + } + + public get resources(): RenderingResources { + return this.settings.display.resources; + } + + public get settings(): Settings { + return this.scoreRenderer.settings; + } + + public get scale(): number { + return this.settings.display.scale; + } + + private _wasFirstOfLine: boolean = false; + + public get isFirstOfLine(): boolean { + return this.index === 0; + } + + public get isLast(): boolean { + return !this.bar || this.bar.index === this.scoreRenderer.layout!.lastBarIndex; + } + + public registerLayoutingInfo(): void { + let info: BarLayoutingInfo = this.layoutingInfo; + let preSize: number = this._preBeatGlyphs.width; + if (info.preBeatSize < preSize) { + info.preBeatSize = preSize; + } + this._voiceContainers.forEach(container => { + container.registerLayoutingInfo(info); + }); + let postSize: number = this._postBeatGlyphs.width; + if (info.postBeatSize < postSize) { + info.postBeatSize = postSize; + } + } + + private _appliedLayoutingInfo: number = 0; + + public applyLayoutingInfo(): boolean { + if (this._appliedLayoutingInfo >= this.layoutingInfo.version) { + return false; + } + this._appliedLayoutingInfo = this.layoutingInfo.version; + // if we need additional space in the preBeat group we simply + // add a new spacer + this._preBeatGlyphs.width = this.layoutingInfo.preBeatSize; + // on beat glyphs we apply the glyph spacing + let voiceEnd: number = this._preBeatGlyphs.x + this._preBeatGlyphs.width; + this._voiceContainers.forEach(c => { + c.x = this._preBeatGlyphs.x + this._preBeatGlyphs.width; + c.applyLayoutingInfo(this.layoutingInfo); + let newEnd: number = c.x + c.width; + if (voiceEnd < newEnd) { + voiceEnd = newEnd; + } + }); + // on the post glyphs we add the spacing before all other glyphs + this._postBeatGlyphs.x = Math.floor(voiceEnd); + this._postBeatGlyphs.width = this.layoutingInfo.postBeatSize; + this.width = Math.ceil(this._postBeatGlyphs.x + this._postBeatGlyphs.width); + return true; + } + + public isFinalized: boolean = false; + + public finalizeRenderer(): void { + this.isFinalized = true; + } + + /** + * Gets the top padding for the main content of the renderer. + * Can be used to specify where i.E. the score lines of the notation start. + * @returns + */ + public topPadding: number = 0; + + /** + * Gets the bottom padding for the main content of the renderer. + * Can be used to specify where i.E. the score lines of the notation end. + */ + public bottomPadding: number = 0; + + public doLayout(): void { + if (!this.bar) { + return; + } + this._preBeatGlyphs = new LeftToRightLayoutingGlyphGroup(); + this._preBeatGlyphs.renderer = this; + this._voiceContainers.clear(); + this._postBeatGlyphs = new LeftToRightLayoutingGlyphGroup(); + this._postBeatGlyphs.renderer = this; + for (let i: number = 0; i < this.bar.voices.length; i++) { + let voice: Voice = this.bar.voices[i]; + if (this.hasVoiceContainer(voice)) { + let c: VoiceContainerGlyph = new VoiceContainerGlyph(0, 0, voice); + c.renderer = this; + this._voiceContainers.set(this.bar.voices[i].index, c); + } + } + if (this.bar.simileMark === SimileMark.SecondOfDouble) { + this.canWrap = false; + } + this.createPreBeatGlyphs(); + this.createBeatGlyphs(); + this.createPostBeatGlyphs(); + this.updateSizes(); + } + + protected hasVoiceContainer(voice: Voice): boolean { + return !voice.isEmpty || voice.index === 0; + } + + protected updateSizes(): void { + this.staff.registerStaffTop(this.topPadding); + this.staff.registerStaffBottom(this.height - this.bottomPadding); + let voiceContainers: Map = this._voiceContainers; + let beatGlyphsStart: number = this.beatGlyphsStart; + let postBeatStart: number = beatGlyphsStart; + voiceContainers.forEach(c => { + c.x = beatGlyphsStart; + c.doLayout(); + let x: number = c.x + c.width; + if (postBeatStart < x) { + postBeatStart = x; + } + }); + this._postBeatGlyphs.x = Math.floor(postBeatStart); + this.width = Math.ceil(this._postBeatGlyphs.x + this._postBeatGlyphs.width); + } + + protected addPreBeatGlyph(g: Glyph): void { + g.renderer = this; + this._preBeatGlyphs.addGlyph(g); + } + + protected addBeatGlyph(g: BeatContainerGlyph): void { + g.renderer = this; + g.preNotes.renderer = this; + g.onNotes.renderer = this; + g.onNotes.beamingHelper = this.helpers!.beamHelperLookup[g.beat.voice.index].get(g.beat.index)!; + this.getOrCreateVoiceContainer(g.beat.voice).addGlyph(g); + } + + protected getOrCreateVoiceContainer(voice: Voice): VoiceContainerGlyph { + return this._voiceContainers.get(voice.index)!; + } + + public getBeatContainer(beat: Beat): BeatContainerGlyph { + return this.getOrCreateVoiceContainer(beat.voice).beatGlyphs[beat.index]; + } + + public getPreNotesGlyphForBeat(beat: Beat): BeatGlyphBase { + return this.getBeatContainer(beat).preNotes; + } + + public getOnNotesGlyphForBeat(beat: Beat): BeatOnNoteGlyphBase { + return this.getBeatContainer(beat).onNotes; + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + this.paintBackground(cx, cy, canvas); + canvas.color = this.resources.mainGlyphColor; + this._preBeatGlyphs.paint(cx + this.x, cy + this.y, canvas); + this._voiceContainers.forEach(c => { + canvas.color = c.voice.index === 0 ? this.resources.mainGlyphColor : this.resources.secondaryGlyphColor; + c.paint(cx + this.x, cy + this.y, canvas); + }); + canvas.color = this.resources.mainGlyphColor; + this._postBeatGlyphs.paint(cx + this.x, cy + this.y, canvas); + } + + protected paintBackground(cx: number, cy: number, canvas: ICanvas): void { + // no default brackgroundpainting + } + + public buildBoundingsLookup(masterBarBounds: MasterBarBounds, cx: number, cy: number): void { + let barBounds: BarBounds = new BarBounds(); + barBounds.bar = this.bar; + barBounds.visualBounds = new Bounds(); + barBounds.visualBounds.x = cx + this.x; + barBounds.visualBounds.y = cy + this.y + this.topPadding; + barBounds.visualBounds.w = this.width; + barBounds.visualBounds.h = this.height - this.topPadding - this.bottomPadding; + + barBounds.realBounds = new Bounds(); + barBounds.realBounds.x = cx + this.x; + barBounds.realBounds.y = cy + this.y; + barBounds.realBounds.w = this.width; + barBounds.realBounds.h = this.height; + + masterBarBounds.addBar(barBounds); + this._voiceContainers.forEach((c, index) => { + let isEmptyBar: boolean = this.bar.isEmpty && index === 0; + if (!c.voice.isEmpty || isEmptyBar) { + for (let i: number = 0, j: number = c.beatGlyphs.length; i < j; i++) { + let bc: BeatContainerGlyph = c.beatGlyphs[i]; + bc.buildBoundingsLookup(barBounds, cx + this.x + c.x, cy + this.y + c.y, isEmptyBar); + } + } + }); + } + + protected addPostBeatGlyph(g: Glyph): void { + this._postBeatGlyphs.addGlyph(g); + } + + protected createPreBeatGlyphs(): void { + this._wasFirstOfLine = this.isFirstOfLine; + } + + protected createBeatGlyphs(): void { + // filled in subclasses + } + + protected createPostBeatGlyphs(): void { + // filled in subclasses + } + + public get beatGlyphsStart(): number { + return this._preBeatGlyphs.x + this._preBeatGlyphs.width; + } + + public get postBeatGlyphsStart(): number { + return this._postBeatGlyphs.x; + } + + public getBeatX(beat: Beat, requestedPosition: BeatXPosition = BeatXPosition.PreNotes): number { + let container: BeatContainerGlyph = this.getBeatContainer(beat); + if (container) { + switch (requestedPosition) { + case BeatXPosition.PreNotes: + return container.voiceContainer.x + container.x; + case BeatXPosition.OnNotes: + return container.voiceContainer.x + container.x + container.onNotes.x; + case BeatXPosition.MiddleNotes: + return container.voiceContainer.x + container.x + container.onTimeX; + case BeatXPosition.Stem: + return ( + container.voiceContainer.x + + container.onNotes.beamingHelper.getBeatLineX(beat) + ); + case BeatXPosition.PostNotes: + return container.voiceContainer.x + container.x + container.onNotes.x + container.onNotes.width; + case BeatXPosition.EndBeat: + return container.voiceContainer.x + container.x + container.width; + } + } + return 0; + } + + public getNoteX(note: Note, requestedPosition: NoteXPosition): number { + let container: BeatContainerGlyph = this.getBeatContainer(note.beat); + if (container) { + return container.voiceContainer.x + container.x + container.onNotes.x + container.onNotes.getNoteX(note, requestedPosition); + } + return 0; + } + + public getNoteY(note: Note, requestedPosition: NoteYPosition): number { + let beat = this.getOnNotesGlyphForBeat(note.beat); + if (beat) { + return beat.getNoteY(note, requestedPosition); + } + return 0; + } + + public reLayout(): void { + // there are some glyphs which are shown only for renderers at the line start, so we simply recreate them + // but we only need to recreate them for the renderers that were the first of the line or are now the first of the line + if ((this._wasFirstOfLine && !this.isFirstOfLine) || (!this._wasFirstOfLine && this.isFirstOfLine)) { + this._preBeatGlyphs = new LeftToRightLayoutingGlyphGroup(); + this._preBeatGlyphs.renderer = this; + this.createPreBeatGlyphs(); + } + this.updateSizes(); + this.registerLayoutingInfo(); + } + + protected paintSimileMark(cx: number, cy: number, canvas: ICanvas): void { + switch (this.bar!.simileMark) { + case SimileMark.Simple: + canvas.fillMusicFontSymbol( + cx + this.x + (this.width - 20 * this.scale) / 2, + cy + this.y + this.height / 2, + 1, + MusicFontSymbol.SimileMarkSimple, + false + ); + break; + case SimileMark.SecondOfDouble: + canvas.fillMusicFontSymbol( + cx + this.x - (28 * this.scale) / 2, + cy + this.y + this.height / 2, + 1, + MusicFontSymbol.SimileMarkDouble, + false + ); + break; + } + } +} diff --git a/src/rendering/BarRendererFactory.ts b/src/rendering/BarRendererFactory.ts new file mode 100644 index 000000000..76c9cadc4 --- /dev/null +++ b/src/rendering/BarRendererFactory.ts @@ -0,0 +1,23 @@ +import { Bar } from '@src/model/Bar'; +import { Staff } from '@src/model/Staff'; +import { Track } from '@src/model/Track'; +import { BarRendererBase } from '@src/rendering/BarRendererBase'; +import { ScoreRenderer } from '@src/rendering/ScoreRenderer'; + +/** + * This is the base public class for creating factories providing BarRenderers + */ +export abstract class BarRendererFactory { + public isInAccolade: boolean = true; + public isRelevantForBoundsLookup: boolean = true; + public hideOnMultiTrack: boolean = false; + public hideOnPercussionTrack: boolean = false; + + public abstract get staffId(): string; + + public canCreate(track: Track, staff: Staff): boolean { + return !this.hideOnPercussionTrack || !staff.isPercussion; + } + + public abstract create(renderer: ScoreRenderer, bar: Bar): BarRendererBase; +} diff --git a/src/rendering/BeatXPosition.ts b/src/rendering/BeatXPosition.ts new file mode 100644 index 000000000..e5b997f80 --- /dev/null +++ b/src/rendering/BeatXPosition.ts @@ -0,0 +1,30 @@ +/** + * Lists the different position modes for {@link BarRendererBase.getBeatX} + */ +export enum BeatXPosition { + /** + * Gets the pre-notes position which is located before the accidentals + */ + PreNotes, + /** + * Gets the on-notes position which is located after the accidentals but before the note heads. + */ + OnNotes, + /** + * Gets the middle-notes position which is located after in the middle the note heads. + */ + MiddleNotes, + /** + * Gets position of the stem for this beat + */ + Stem, + /** + * Get the post-notes position which is located at after the note heads. + */ + PostNotes, + /** + * Get the end-beat position which is located at the end of the beat. This position is almost + * equal to the pre-notes position of the next beat. + */ + EndBeat +} diff --git a/src/rendering/EffectBand.ts b/src/rendering/EffectBand.ts new file mode 100644 index 000000000..128fe7edd --- /dev/null +++ b/src/rendering/EffectBand.ts @@ -0,0 +1,195 @@ +import { Beat } from '@src/model/Beat'; +import { Voice } from '@src/model/Voice'; +import { ICanvas } from '@src/platform/ICanvas'; +import { EffectBandSlot } from '@src/rendering/EffectBandSlot'; +import { EffectBarGlyphSizing } from '@src/rendering/EffectBarGlyphSizing'; +import { EffectBarRenderer } from '@src/rendering/EffectBarRenderer'; +import { BeatContainerGlyph } from '@src/rendering/glyphs/BeatContainerGlyph'; +import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph'; +import { Glyph } from '@src/rendering/glyphs/Glyph'; +import { EffectBarRendererInfo } from '@src/rendering/EffectBarRendererInfo'; + +export class EffectBand extends Glyph { + private _uniqueEffectGlyphs: EffectGlyph[][] = []; + private _effectGlyphs: Map[] = []; + public isEmpty: boolean = true; + + public previousBand: EffectBand | null = null; + public isLinkedToPrevious: boolean = false; + public firstBeat: Beat | null = null; + public lastBeat: Beat | null = null; + public height: number = 0; + public voice: Voice; + public info: EffectBarRendererInfo; + public slot: EffectBandSlot | null = null; + + public constructor(voice: Voice, info: EffectBarRendererInfo) { + super(0, 0); + this.voice = voice; + this.info = info; + } + + public doLayout(): void { + super.doLayout(); + for (let i: number = 0; i < this.renderer.bar.voices.length; i++) { + this._effectGlyphs.push(new Map()); + this._uniqueEffectGlyphs.push([]); + } + } + + public createGlyph(beat: Beat): void { + if (beat.voice !== this.voice) { + return; + } + // NOTE: the track order will never change. even if the staff behind the renderer changes, the trackIndex will not. + // so it's okay to access the staff here while creating the glyphs. + if ( + this.info.shouldCreateGlyph(this.renderer.settings, beat) && + (!this.info.hideOnMultiTrack || this.renderer.staff.trackIndex === 0) + ) { + this.isEmpty = false; + if (!this.firstBeat || beat.isBefore(this.firstBeat)) { + this.firstBeat = beat; + } + if (!this.lastBeat || beat.isAfter(this.lastBeat)) { + this.lastBeat = beat; + // for "toEnd" sizing occupy until next follow-up-beat + switch (this.info.sizingMode) { + case EffectBarGlyphSizing.SingleOnBeatToEnd: + case EffectBarGlyphSizing.GroupedOnBeatToEnd: + if (this.lastBeat.nextBeat) { + this.lastBeat = this.lastBeat.nextBeat; + } + break; + } + } + let glyph: EffectGlyph = this.createOrResizeGlyph(this.info.sizingMode, beat); + if (glyph.height > this.height) { + this.height = glyph.height; + } + } + } + + private createOrResizeGlyph(sizing: EffectBarGlyphSizing, b: Beat): EffectGlyph { + let g: EffectGlyph; + switch (sizing) { + case EffectBarGlyphSizing.FullBar: + g = this.info.createNewGlyph(this.renderer, b); + g.renderer = this.renderer; + g.beat = b; + g.doLayout(); + this._effectGlyphs[b.voice.index].set(b.index, g); + this._uniqueEffectGlyphs[b.voice.index].push(g); + return g; + case EffectBarGlyphSizing.SinglePreBeat: + case EffectBarGlyphSizing.SingleOnBeat: + case EffectBarGlyphSizing.SingleOnBeatToEnd: + g = this.info.createNewGlyph(this.renderer, b); + g.renderer = this.renderer; + g.beat = b; + g.doLayout(); + this._effectGlyphs[b.voice.index].set(b.index, g); + this._uniqueEffectGlyphs[b.voice.index].push(g); + return g; + case EffectBarGlyphSizing.GroupedOnBeat: + case EffectBarGlyphSizing.GroupedOnBeatToEnd: + let singleSizing: EffectBarGlyphSizing = + sizing === EffectBarGlyphSizing.GroupedOnBeat + ? EffectBarGlyphSizing.SingleOnBeat + : EffectBarGlyphSizing.SingleOnBeatToEnd; + if (b.index > 0 || this.renderer.index > 0) { + // check if the previous beat also had this effect + let prevBeat = b.previousBeat!; + if (this.info.shouldCreateGlyph(this.renderer.settings, prevBeat)) { + // first load the effect bar renderer and glyph + let prevEffect: EffectGlyph | null = null; + if (b.index > 0 && this._effectGlyphs[b.voice.index].has(prevBeat.index)) { + // load effect from previous beat in the same renderer + prevEffect = this._effectGlyphs[b.voice.index].get(prevBeat.index)!; + } else if (this.renderer.index > 0) { + // load the effect from the previous renderer if possible. + let previousRenderer: EffectBarRenderer = this.renderer + .previousRenderer as EffectBarRenderer; + let previousBand: EffectBand = previousRenderer.getBand(this.voice, this.info.effectId)!; + let voiceGlyphs: Map = previousBand._effectGlyphs[b.voice.index]; + if (voiceGlyphs.has(prevBeat.index)) { + prevEffect = voiceGlyphs.get(prevBeat.index)!; + } + } + // if the effect cannot be expanded, create a new glyph + // in case of expansion also create a new glyph, but also link the glyphs together + // so for rendering it might be expanded. + let newGlyph: EffectGlyph = this.createOrResizeGlyph(singleSizing, b); + if (prevEffect && this.info.canExpand(prevBeat, b)) { + // link glyphs + prevEffect.nextGlyph = newGlyph; + newGlyph.previousGlyph = prevEffect; + // mark renderers as linked for consideration when layouting the renderers (line breaking, partial breaking) + this.isLinkedToPrevious = true; + } + return newGlyph; + } + // in case the previous beat did not have the same effect, we simply create a new glyph + return this.createOrResizeGlyph(singleSizing, b); + } + // in case of the very first beat, we simply create the glyph. + return this.createOrResizeGlyph(singleSizing, b); + default: + return this.createOrResizeGlyph(EffectBarGlyphSizing.SingleOnBeat, b); + } + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + super.paint(cx, cy, canvas); + // canvas.LineWidth = 1; + // canvas.StrokeRect(cx + X, cy + Y, Renderer.Width, Slot.Shared.Height); + // canvas.LineWidth = 1.5f; + for (let i: number = 0, j: number = this._uniqueEffectGlyphs.length; i < j; i++) { + let v: EffectGlyph[] = this._uniqueEffectGlyphs[i]; + for (let k: number = 0, l: number = v.length; k < l; k++) { + let g: EffectGlyph = v[k]; + g.paint(cx + this.x, cy + this.y, canvas); + } + } + } + + public alignGlyphs(): void { + for (let v: number = 0; v < this._effectGlyphs.length; v++) { + this._effectGlyphs[v].forEach((g, beatIndex)=> { + this.alignGlyph(this.info.sizingMode, this.renderer.bar.voices[v].beats[beatIndex]); + }); + } + } + + private alignGlyph(sizing: EffectBarGlyphSizing, beat: Beat): void { + let g: EffectGlyph = this._effectGlyphs[beat.voice.index].get(beat.index)!; + let pos: Glyph; + let container: BeatContainerGlyph = this.renderer.getBeatContainer(beat); + switch (sizing) { + case EffectBarGlyphSizing.SinglePreBeat: + pos = container.preNotes; + g.x = this.renderer.beatGlyphsStart + pos.x + container.x; + g.width = pos.width; + break; + case EffectBarGlyphSizing.SingleOnBeat: + case EffectBarGlyphSizing.GroupedOnBeat: + pos = container.onNotes; + g.x = this.renderer.beatGlyphsStart + pos.x + container.x; + g.width = pos.width; + break; + case EffectBarGlyphSizing.SingleOnBeatToEnd: + case EffectBarGlyphSizing.GroupedOnBeatToEnd: + pos = container.onNotes; + g.x = this.renderer.beatGlyphsStart + pos.x + container.x; + if (container.beat.isLastOfVoice) { + g.width = this.renderer.width - g.x; + } else { + g.width = container.width - container.preNotes.width - container.preNotes.x; + } + break; + case EffectBarGlyphSizing.FullBar: + g.width = this.renderer.width; + break; + } + } +} diff --git a/src/rendering/EffectBandSizingInfo.ts b/src/rendering/EffectBandSizingInfo.ts new file mode 100644 index 000000000..1f0a2fa0c --- /dev/null +++ b/src/rendering/EffectBandSizingInfo.ts @@ -0,0 +1,38 @@ +import { EffectBand } from '@src/rendering/EffectBand'; +import { EffectBandSlot } from '@src/rendering/EffectBandSlot'; + +export class EffectBandSizingInfo { + private _effectSlot: Map; + public slots: EffectBandSlot[]; + + public constructor() { + this.slots = []; + this._effectSlot = new Map(); + } + + public getOrCreateSlot(band: EffectBand): EffectBandSlot { + // first check preferrable slot depending on type + if (this._effectSlot.has(band.info.effectId)) { + let slot: EffectBandSlot = this._effectSlot.get(band.info.effectId)!; + if (slot.canBeUsed(band)) { + return slot; + } + } + // find any slot that can be used + for (let slot of this.slots) { + if (slot.canBeUsed(band)) { + return slot; + } + } + // create a new slot if required + let newSlot: EffectBandSlot = new EffectBandSlot(); + this.slots.push(newSlot); + return newSlot; + } + + public register(effectBand: EffectBand): void { + let freeSlot: EffectBandSlot = this.getOrCreateSlot(effectBand); + freeSlot.update(effectBand); + this._effectSlot.set(effectBand.info.effectId, freeSlot); + } +} diff --git a/src/rendering/EffectBandSlot.ts b/src/rendering/EffectBandSlot.ts new file mode 100644 index 000000000..95dbb49e1 --- /dev/null +++ b/src/rendering/EffectBandSlot.ts @@ -0,0 +1,49 @@ +import { Beat } from '@src/model/Beat'; +import { EffectBand } from '@src/rendering/EffectBand'; + +export class EffectBandSlotShared { + public uniqueEffectId: string | null = null; + public y: number = 0; + public height: number = 0; + public firstBeat: Beat | null = null; + public lastBeat: Beat | null = null; +} + +export class EffectBandSlot { + public bands: EffectBand[]; + + public shared: EffectBandSlotShared; + + public constructor() { + this.bands = []; + this.shared = new EffectBandSlotShared(); + } + + public update(effectBand: EffectBand): void { + // lock band to particular effect if needed + if (!effectBand.info.canShareBand) { + this.shared.uniqueEffectId = effectBand.info.effectId; + } + effectBand.slot = this; + this.bands.push(effectBand); + if (effectBand.height > this.shared.height) { + this.shared.height = effectBand.height; + } + if (!this.shared.firstBeat || effectBand.firstBeat!.isBefore(this.shared.firstBeat)) { + this.shared.firstBeat = effectBand.firstBeat; + } + if (!this.shared.lastBeat || effectBand.lastBeat!.isAfter(this.shared.lastBeat)) { + this.shared.lastBeat = effectBand.lastBeat; + } + } + + public canBeUsed(band: EffectBand): boolean { + return ( + ((!this.shared.uniqueEffectId && band.info.canShareBand) || + band.info.effectId === this.shared.uniqueEffectId) && + (!this.shared.firstBeat || + this.shared.lastBeat!.isBefore(band.firstBeat!) || + this.shared.lastBeat!.isBefore(this.shared.firstBeat)) + ); + } +} diff --git a/src/rendering/EffectBarGlyphSizing.ts b/src/rendering/EffectBarGlyphSizing.ts new file mode 100644 index 000000000..d6657109a --- /dev/null +++ b/src/rendering/EffectBarGlyphSizing.ts @@ -0,0 +1,36 @@ +/** + * Lists all sizing types of the effect bar glyphs + */ +export enum EffectBarGlyphSizing { + /** + * The effect glyph is placed above the pre-beat glyph which is before + * the actual note in the area where also accidentals are renderered. + */ + SinglePreBeat, + /** + * The effect glyph is placed above the on-beat glyph which is where + * the actual note head glyphs are placed. + */ + SingleOnBeat, + /** + * The effect glyph is placed above the on-beat glyph which is where + * the actual note head glyphs are placed. The glyph will size to the end of + * the applied beat. + */ + SingleOnBeatToEnd, + /** + * The effect glyph is placed above the on-beat glyph and expaded to the + * on-beat position of the next beat. + */ + GroupedOnBeat, + /** + * The effect glyph is placed above the on-beat glyph and expaded to the + * on-beat position of the next beat. The glyph will size to the end of + * the applied beat. + */ + GroupedOnBeatToEnd, + /** + * The effect glyph is placed on the whole bar covering the whole width + */ + FullBar +} diff --git a/src/rendering/EffectBarRenderer.ts b/src/rendering/EffectBarRenderer.ts new file mode 100644 index 000000000..d1d3c72f7 --- /dev/null +++ b/src/rendering/EffectBarRenderer.ts @@ -0,0 +1,145 @@ +import { Bar } from '@src/model/Bar'; +import { Voice } from '@src/model/Voice'; +import { ICanvas } from '@src/platform/ICanvas'; +import { BarRendererBase } from '@src/rendering/BarRendererBase'; +import { EffectBand } from '@src/rendering/EffectBand'; +import { EffectBandSizingInfo } from '@src/rendering/EffectBandSizingInfo'; +import { BeatContainerGlyph } from '@src/rendering/glyphs/BeatContainerGlyph'; +import { BeatGlyphBase } from '@src/rendering/glyphs/BeatGlyphBase'; +import { BeatOnNoteGlyphBase } from '@src/rendering/glyphs/BeatOnNoteGlyphBase'; +import { EffectBarRendererInfo } from '@src/rendering/EffectBarRendererInfo'; +import { ScoreRenderer } from '@src/rendering/ScoreRenderer'; + +/** + * This renderer is responsible for displaying effects above or below the other staves + * like the vibrato. + */ +export class EffectBarRenderer extends BarRendererBase { + private _infos: EffectBarRendererInfo[]; + private _bands: EffectBand[] = []; + private _bandLookup: Map = new Map(); + public sizingInfo: EffectBandSizingInfo | null = null; + + public constructor(renderer: ScoreRenderer, bar: Bar, infos: EffectBarRendererInfo[]) { + super(renderer, bar); + this._infos = infos; + } + + protected updateSizes(): void { + this.topOverflow = 0; + this.bottomOverflow = 0; + this.topPadding = 0; + this.bottomPadding = 0; + this.updateHeight(); + super.updateSizes(); + } + + public finalizeRenderer(): void { + super.finalizeRenderer(); + this.updateHeight(); + } + + private updateHeight(): void { + if (!this.sizingInfo) { + return; + } + let y: number = 0; + for (let slot of this.sizingInfo.slots) { + slot.shared.y = y; + for (let band of slot.bands) { + band.y = y; + band.height = slot.shared.height; + } + y += slot.shared.height; + } + this.height = y; + } + + public applyLayoutingInfo(): boolean { + if (!super.applyLayoutingInfo()) { + return false; + } + // we create empty slots for the same group + if (this.index > 0) { + let previousRenderer: EffectBarRenderer = this.previousRenderer as EffectBarRenderer; + this.sizingInfo = previousRenderer.sizingInfo; + } else { + this.sizingInfo = new EffectBandSizingInfo(); + } + for (let effectBand of this._bands) { + effectBand.alignGlyphs(); + if (!effectBand.isEmpty) { + // find a slot that ended before the start of the band + this.sizingInfo!.register(effectBand); + } + } + this.updateHeight(); + return true; + } + + public scaleToWidth(width: number): void { + super.scaleToWidth(width); + for (let effectBand of this._bands) { + effectBand.alignGlyphs(); + } + } + + protected createBeatGlyphs(): void { + this._bands = []; + this._bandLookup = new Map(); + for (let voice of this.bar.voices) { + if (this.hasVoiceContainer(voice)) { + for (let info of this._infos) { + let band: EffectBand = new EffectBand(voice, info); + band.renderer = this; + band.doLayout(); + this._bands.push(band); + this._bandLookup.set(voice.index + '.' + info.effectId, band); + } + } + } + for (let voice of this.bar.voices) { + if (this.hasVoiceContainer(voice)) { + this.createVoiceGlyphs(voice); + } + } + for (let effectBand of this._bands) { + if (effectBand.isLinkedToPrevious) { + this.isLinkedToPrevious = true; + } + } + } + + private createVoiceGlyphs(v: Voice): void { + for (let b of v.beats) { + // we create empty glyphs as alignment references and to get the + // effect bar sized + let container: BeatContainerGlyph = new BeatContainerGlyph(b, this.getOrCreateVoiceContainer(v)); + container.preNotes = new BeatGlyphBase(); + container.onNotes = new BeatOnNoteGlyphBase(); + this.addBeatGlyph(container); + for (let effectBand of this._bands) { + effectBand.createGlyph(b); + } + } + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + this.paintBackground(cx, cy, canvas); + for (let effectBand of this._bands) { + canvas.color = + effectBand.voice.index === 0 ? this.resources.mainGlyphColor : this.resources.secondaryGlyphColor; + if (!effectBand.isEmpty) { + effectBand.paint(cx + this.x, cy + this.y, canvas); + } + } + } + + public getBand(voice: Voice, effectId: string): EffectBand | null { + let id: string = voice.index + '.' + effectId; + if (this._bandLookup.has(id)) { + return this._bandLookup.get(id)!; + } + return null; + } +} diff --git a/src/rendering/EffectBarRendererFactory.ts b/src/rendering/EffectBarRendererFactory.ts new file mode 100644 index 000000000..6e85f3acf --- /dev/null +++ b/src/rendering/EffectBarRendererFactory.ts @@ -0,0 +1,30 @@ +import { Bar } from '@src/model/Bar'; +import { BarRendererBase } from '@src/rendering/BarRendererBase'; +import { BarRendererFactory } from '@src/rendering/BarRendererFactory'; +import { EffectBarRenderer } from '@src/rendering/EffectBarRenderer'; +import { EffectBarRendererInfo } from '@src/rendering/EffectBarRendererInfo'; +import { ScoreRenderer } from '@src/rendering/ScoreRenderer'; + +export class EffectBarRendererFactory extends BarRendererFactory { + private _infos: EffectBarRendererInfo[]; + private _staffId: string; + public get staffId(): string { + return this._staffId; + } + + public constructor(staffId: string, infos: EffectBarRendererInfo[]) { + super(); + this._infos = infos; + this._staffId = staffId; + this.isInAccolade = false; + this.isRelevantForBoundsLookup = false; + } + + public create(renderer: ScoreRenderer, bar: Bar): BarRendererBase { + return new EffectBarRenderer( + renderer, + bar, + this._infos.filter(i => renderer.settings.notation.isNotationElementVisible(i.notationElement)) + ); + } +} diff --git a/src/rendering/EffectBarRendererInfo.ts b/src/rendering/EffectBarRendererInfo.ts new file mode 100644 index 000000000..872c3998c --- /dev/null +++ b/src/rendering/EffectBarRendererInfo.ts @@ -0,0 +1,71 @@ +import { Beat } from '@src/model/Beat'; +import { BarRendererBase } from '@src/rendering/BarRendererBase'; +import { EffectBarGlyphSizing } from '@src/rendering/EffectBarGlyphSizing'; +import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph'; +import { Settings } from '@src/Settings'; +import { NotationElement } from '@src/NotationSettings'; + +/** + * A classes inheriting from this base can provide the + * data needed by a EffectBarRenderer to create effect glyphs dynamically. + */ +export abstract class EffectBarRendererInfo { + /** + * Gets the unique effect name for this effect. (Used for grouping) + */ + public get effectId(): string { + return this.notationElement.toString(); + } + + /** + * Gets the notation element that this effect represents. (Used for dynamic showing/hiding) + */ + public abstract get notationElement(): NotationElement; + + /** + * Gets a value indicating whether this effect can share the space + * with other effects if required. + * (Example: tempo and dynamics don't share their space with other effects, a let-ring and palm-mute will share the space if possible) + * @returns true if this effect bar should only be created once for the first track, otherwise false. + */ + public abstract get canShareBand(): boolean; + + /** + * Gets a value indicating whether this effect glyphs + * should only be added once on the first track if multiple tracks are rendered. + * (Example: this allows to render the tempo changes only once) + * @returns true if this effect bar should only be created once for the first track, otherwise false. + */ + public abstract get hideOnMultiTrack(): boolean; + + /** + * Checks whether the given beat has the appropriate effect set and + * needs a glyph creation + * @param settings + * @param beat the beat storing the data + * @returns true if the beat has the effect set, otherwise false. + */ + public abstract shouldCreateGlyph(settings: Settings, beat: Beat): boolean; + + /** + * Gets the sizing mode of the glyphs created by this info. + * @returns the sizing mode to apply to the glyphs during layout + */ + public abstract get sizingMode(): EffectBarGlyphSizing; + + /** + * Creates a new effect glyph for the given beat. + * @param renderer the renderer which requests for glyph creation + * @param beat the beat storing the data + * @returns the glyph which needs to be added to the renderer + */ + public abstract createNewGlyph(renderer: BarRendererBase, beat: Beat): EffectGlyph; + + /** + * Checks whether an effect glyph can be expanded to a particular beat. + * @param from the beat which already has the glyph applied + * @param to the beat which the glyph should get expanded to + * @returns true if the glyph can be expanded, false if a new glyph needs to be created. + */ + public abstract canExpand(from: Beat, to: Beat): boolean; +} diff --git a/src/rendering/IScoreRenderer.ts b/src/rendering/IScoreRenderer.ts new file mode 100644 index 000000000..7958e8a83 --- /dev/null +++ b/src/rendering/IScoreRenderer.ts @@ -0,0 +1,73 @@ +import { IEventEmitter, IEventEmitterOfT } from '@src/EventEmitter'; +import { Score } from '@src/model/Score'; +import { RenderFinishedEventArgs } from '@src/rendering/RenderFinishedEventArgs'; +import { BoundsLookup } from '@src/rendering/utils/BoundsLookup'; +import { Settings } from '@src/Settings'; + +/** + * Represents the public interface of the component that can render scores. + */ +export interface IScoreRenderer { + /** + * Gets or sets the lookup which allows fast access to beats at a given position. + */ + readonly boundsLookup: BoundsLookup | null; + + /** + * Gets or sets the width of the score to be rendered. + */ + width: number; + + /** + * Initiates a full re-rendering of the score using the current settings. + */ + render(): void; + + /** + * Initiates a resize-optimized re-rendering of the score using the current settings. + */ + resizeRender(): void; + + /** + * Initiates the rendering of the specified tracks of the given score. + * @param score The score defining the tracks. + * @param trackIndexes The indexes of the tracks to draw. + */ + renderScore(score: Score, trackIndexes: number[]): void; + + /** + * Updates the settings to the given object. + * @param settings + */ + updateSettings(settings: Settings): void; + + /** + * Destroys the renderer. + */ + destroy(): void; + + /** + * Occurs before the rendering of the tracks starts. + */ + readonly preRender: IEventEmitterOfT; + + /** + * Occurs after the rendering of the tracks finished. + */ + readonly renderFinished: IEventEmitterOfT; + + /** + * Occurs whenever a part of the whole music sheet is rendered and can be displayed. + */ + readonly partialRenderFinished: IEventEmitterOfT; + + /** + * Occurs when the whole rendering and layout process finished. + */ + readonly postRenderFinished: IEventEmitter; + + /** + * Occurs whenever an error happens. + */ + readonly error: IEventEmitterOfT; +} diff --git a/src/rendering/RenderFinishedEventArgs.ts b/src/rendering/RenderFinishedEventArgs.ts new file mode 100644 index 000000000..8e0d67a11 --- /dev/null +++ b/src/rendering/RenderFinishedEventArgs.ts @@ -0,0 +1,40 @@ +/** + * This eventargs define the details about the rendering and layouting process and are + * provided whenever a part of of the music sheet is rendered. + */ +export class RenderFinishedEventArgs { + /** + * Gets or sets the width of the current rendering result. + */ + public width: number = 0; + + /** + * Gets or sets the height of the current rendering result. + */ + public height: number = 0; + + /** + * Gets or sets the currently known total width of the final music sheet. + */ + public totalWidth: number = 0; + + /** + * Gets or sets the currently known total height of the final music sheet. + */ + public totalHeight: number = 0; + + /** + * Gets or sets the index of the first masterbar that was rendered in this result. + */ + public firstMasterBarIndex: number = 0; + + /** + * Gets or sets the index of the last masterbar that was rendered in this result. + */ + public lastMasterBarIndex: number = 0; + + /** + * Gets or sets the render engine specific result object which contains the rendered music sheet. + */ + public renderResult: unknown = null; +} diff --git a/src/rendering/ScoreBarRenderer.ts b/src/rendering/ScoreBarRenderer.ts new file mode 100644 index 000000000..405dc1353 --- /dev/null +++ b/src/rendering/ScoreBarRenderer.ts @@ -0,0 +1,795 @@ +import { AccidentalType } from '@src/model/AccidentalType'; +import { Bar } from '@src/model/Bar'; +import { Beat } from '@src/model/Beat'; +import { Clef } from '@src/model/Clef'; +import { Duration } from '@src/model/Duration'; +import { Fingers } from '@src/model/Fingers'; +import { GraceType } from '@src/model/GraceType'; +import { Note } from '@src/model/Note'; +import { TupletGroup } from '@src/model/TupletGroup'; +import { Voice } from '@src/model/Voice'; +import { FingeringMode, NotationMode } from '@src/NotationSettings'; +import { ICanvas, TextAlign } from '@src/platform/ICanvas'; +import { BarRendererBase } from '@src/rendering/BarRendererBase'; +import { AccidentalGlyph } from '@src/rendering/glyphs/AccidentalGlyph'; +import { BarNumberGlyph } from '@src/rendering/glyphs/BarNumberGlyph'; +import { BarSeperatorGlyph } from '@src/rendering/glyphs/BarSeperatorGlyph'; +import { BeamGlyph } from '@src/rendering/glyphs/BeamGlyph'; +import { ClefGlyph } from '@src/rendering/glyphs/ClefGlyph'; +import { Glyph } from '@src/rendering/glyphs/Glyph'; +import { RepeatCloseGlyph } from '@src/rendering/glyphs/RepeatCloseGlyph'; +import { RepeatCountGlyph } from '@src/rendering/glyphs/RepeatCountGlyph'; +import { RepeatOpenGlyph } from '@src/rendering/glyphs/RepeatOpenGlyph'; +import { ScoreBeatGlyph } from '@src/rendering/glyphs/ScoreBeatGlyph'; +import { ScoreBeatPreNotesGlyph } from '@src/rendering/glyphs/ScoreBeatPreNotesGlyph'; +import { ScoreTimeSignatureGlyph } from '@src/rendering/glyphs/ScoreTimeSignatureGlyph'; +import { SpacingGlyph } from '@src/rendering/glyphs/SpacingGlyph'; +import { VoiceContainerGlyph } from '@src/rendering/glyphs/VoiceContainerGlyph'; +import { ScoreBeatContainerGlyph } from '@src/rendering/ScoreBeatContainerGlyph'; +import { ScoreRenderer } from '@src/rendering/ScoreRenderer'; +import { AccidentalHelper } from '@src/rendering/utils/AccidentalHelper'; +import { BeamDirection } from '@src/rendering/utils/BeamDirection'; +import { BeamingHelper } from '@src/rendering/utils/BeamingHelper'; +import { IBeamYCalculator } from '@src/rendering/utils/IBeamYCalculator'; +import { RenderingResources } from '@src/RenderingResources'; +import { Settings } from '@src/Settings'; +import { ModelUtils } from '@src/model/ModelUtils'; +import { NoteHeadGlyph } from '@src/rendering/glyphs/NoteHeadGlyph'; + +/** + * This BarRenderer renders a bar using standard music notation. + */ +export class ScoreBarRenderer extends BarRendererBase implements IBeamYCalculator { + public static readonly StaffId: string = 'score'; + private static SharpKsSteps: number[] = [1, 4, 0, 3, 6, 2, 5]; + private static FlatKsSteps: number[] = [5, 2, 6, 3, 7, 4, 8]; + private static readonly LineSpacing: number = 8; + private static readonly StemWidth: number = 1.3; + + public simpleWhammyOverflow: number = 0; + + public accidentalHelper: AccidentalHelper; + + public constructor(renderer: ScoreRenderer, bar: Bar) { + super(renderer, bar); + this._startSpacing = false; + this.accidentalHelper = new AccidentalHelper(bar); + } + + public getBeatDirection(beat: Beat): BeamDirection { + let g: ScoreBeatGlyph | null = this.getOnNotesGlyphForBeat(beat) as ScoreBeatGlyph; + if (g) { + return g.noteHeads ? g.noteHeads.direction : BeamDirection.Up; + } + return BeamDirection.Up; + } + + public get lineOffset(): number { + return (ScoreBarRenderer.LineSpacing + 1) * this.scale; + } + + protected updateSizes(): void { + let res: RenderingResources = this.resources; + let glyphOverflow: number = res.tablatureFont.size / 2 + res.tablatureFont.size * 0.2; + this.topPadding = glyphOverflow; + this.bottomPadding = glyphOverflow; + this.height = this.lineOffset * 4 + this.topPadding + this.bottomPadding; + super.updateSizes(); + } + + public doLayout(): void { + super.doLayout(); + if (!this.bar.isEmpty && this.accidentalHelper.maxNoteValueBeat) { + let top: number = this.getScoreY(0, 0); + let bottom: number = this.getScoreY(8, 0); + let whammyOffset: number = this.simpleWhammyOverflow; + this.registerOverflowTop(whammyOffset); + let maxNoteY: number = this.getYPositionForNoteValue(this.accidentalHelper.maxNoteValue); + let maxNoteHelper: BeamingHelper = this.helpers.getBeamingHelperForBeat( + this.accidentalHelper.maxNoteValueBeat + ); + if (maxNoteHelper.direction === BeamDirection.Up) { + maxNoteY -= this.getStemSize(maxNoteHelper); + maxNoteY -= maxNoteHelper.fingeringCount * this.resources.graceFont.size; + if (maxNoteHelper.hasTuplet) { + maxNoteY -= this.resources.effectFont.size * 2; + } + } + if (maxNoteHelper.hasTuplet) { + maxNoteY -= this.resources.effectFont.size * 1.5; + } + if (maxNoteY < top) { + this.registerOverflowTop(Math.abs(maxNoteY) + whammyOffset); + } + let minNoteY: number = this.getYPositionForNoteValue(this.accidentalHelper.minNoteValue); + let minNoteHelper: BeamingHelper = this.helpers.getBeamingHelperForBeat( + this.accidentalHelper.minNoteValueBeat! + ); + if (minNoteHelper.direction === BeamDirection.Down) { + minNoteY += this.getStemSize(minNoteHelper); + minNoteY += minNoteHelper.fingeringCount * this.resources.graceFont.size; + } + if (minNoteY > bottom) { + this.registerOverflowBottom(Math.abs(minNoteY) - bottom); + } + } + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + super.paint(cx, cy, canvas); + this.paintBeams(cx, cy, canvas); + this.paintTuplets(cx, cy, canvas); + } + + private paintTuplets(cx: number, cy: number, canvas: ICanvas): void { + for (let voice of this.bar.voices) { + if (this.hasVoiceContainer(voice)) { + let container: VoiceContainerGlyph = this.getOrCreateVoiceContainer(voice); + for (let tupletGroup of container.tupletGroups) { + this.paintTupletHelper(cx + this.beatGlyphsStart, cy, canvas, tupletGroup); + } + } + } + } + + private paintBeams(cx: number, cy: number, canvas: ICanvas): void { + for (let i: number = 0, j: number = this.helpers.beamHelpers.length; i < j; i++) { + let v: BeamingHelper[] = this.helpers.beamHelpers[i]; + for (let k: number = 0, l: number = v.length; k < l; k++) { + let h: BeamingHelper = v[k]; + this.paintBeamHelper(cx + this.beatGlyphsStart, cy, canvas, h); + } + } + } + + private paintBeamHelper(cx: number, cy: number, canvas: ICanvas, h: BeamingHelper): void { + canvas.color = h.voice!.index === 0 ? this.resources.mainGlyphColor : this.resources.secondaryGlyphColor; + // TODO: draw stem at least at the center of the score staff. + // check if we need to paint simple footer + if (h.beats.length === 1) { + this.paintFooter(cx, cy, canvas, h); + } else { + this.paintBar(cx, cy, canvas, h); + } + } + + private paintTupletHelper(cx: number, cy: number, canvas: ICanvas, h: TupletGroup): void { + let res: RenderingResources = this.resources; + let oldAlign: TextAlign = canvas.textAlign; + canvas.color = h.voice.index === 0 ? this.resources.mainGlyphColor : this.resources.secondaryGlyphColor; + canvas.textAlign = TextAlign.Center; + let s: string; + let num: number = h.beats[0].tupletNumerator; + let den: number = h.beats[0].tupletDenominator; + // list as in Guitar Pro 7. for certain tuplets only the numerator is shown + if (num === 2 && den === 3) { + s = '2'; + } else if (num === 3 && den === 2) { + s = '3'; + } else if (num === 4 && den === 6) { + s = '4'; + } else if (num === 5 && den === 4) { + s = '5'; + } else if (num === 6 && den === 4) { + s = '6'; + } else if (num === 7 && den === 4) { + s = '7'; + } else if (num === 9 && den === 8) { + s = '9'; + } else if (num === 10 && den === 8) { + s = '10'; + } else if (num === 11 && den === 8) { + s = '11'; + } else if (num === 12 && den === 8) { + s = '12'; + } else if (num === 13 && den === 8) { + s = '13'; + } else { + s = num + ':' + den; + } + // check if we need to paint simple footer + if (h.beats.length === 1 || !h.isFull) { + for (let i: number = 0, j: number = h.beats.length; i < j; i++) { + let beat: Beat = h.beats[i]; + let beamingHelper: BeamingHelper = this.helpers.beamHelperLookup[h.voice.index].get(beat.index)!; + if (!beamingHelper) { + continue; + } + let direction: BeamDirection = beamingHelper.direction; + let tupletX: number = beamingHelper.getBeatLineX(beat) + this.scale; + let tupletY: number = cy + this.y + this.calculateBeamY(beamingHelper, tupletX); + let offset: number = direction === BeamDirection.Up ? res.effectFont.size * 1.5 : -3 * this.scale; + canvas.font = res.effectFont; + canvas.fillText(s, cx + this.x + tupletX, tupletY - offset); + } + } else { + let firstBeat: Beat = h.beats[0]; + let lastBeat: Beat = h.beats[h.beats.length - 1]; + let firstBeamingHelper: BeamingHelper = this.helpers.beamHelperLookup[h.voice.index].get(firstBeat.index)!; + let lastBeamingHelper: BeamingHelper = this.helpers.beamHelperLookup[h.voice.index].get(lastBeat.index)!; + if (firstBeamingHelper && lastBeamingHelper) { + let direction: BeamDirection = firstBeamingHelper.direction; + // + // Calculate the overall area of the tuplet bracket + let startX: number = firstBeamingHelper.getBeatLineX(firstBeat) + this.scale; + let endX: number = lastBeamingHelper.getBeatLineX(lastBeat) + this.scale; + // + // Calculate how many space the text will need + canvas.font = res.effectFont; + let sw: number = canvas.measureText(s); + let sp: number = 3 * this.scale; + // + // Calculate the offsets where to break the bracket + let middleX: number = (startX + endX) / 2; + let offset1X: number = middleX - sw / 2 - sp; + let offset2X: number = middleX + sw / 2 + sp; + // + // calculate the y positions for our bracket + let startY: number = this.calculateBeamYWithDirection( + firstBeamingHelper, + startX, + firstBeamingHelper.direction + ); + let endY: number = this.calculateBeamYWithDirection( + lastBeamingHelper, + endX, + firstBeamingHelper.direction + ); + let k: number = (endY - startY) / (endX - startX); + let d: number = startY - k * startX; + let offset1Y: number = k * offset1X + d; + let middleY: number = k * middleX + d; + let offset2Y: number = k * offset2X + d; + let offset: number = 10 * this.scale; + let size: number = 5 * this.scale; + if (direction === BeamDirection.Down) { + offset *= -1; + size *= -1; + } + // + // draw the bracket + canvas.beginPath(); + canvas.moveTo(cx + this.x + startX, (cy + this.y + startY - offset) | 0); + canvas.lineTo(cx + this.x + startX, (cy + this.y + startY - offset - size) | 0); + canvas.lineTo(cx + this.x + offset1X, (cy + this.y + offset1Y - offset - size) | 0); + canvas.stroke(); + canvas.beginPath(); + canvas.moveTo(cx + this.x + offset2X, (cy + this.y + offset2Y - offset - size) | 0); + canvas.lineTo(cx + this.x + endX, (cy + this.y + endY - offset - size) | 0); + canvas.lineTo(cx + this.x + endX, (cy + this.y + endY - offset) | 0); + canvas.stroke(); + // + // Draw the string + canvas.fillText( + s, + cx + this.x + middleX, + cy + this.y + middleY - offset - size - res.effectFont.size / 2 + ); + } + } + canvas.textAlign = oldAlign; + } + + public getStemSize(helper: BeamingHelper): number { + let size: number = + helper.beats.length === 1 + ? this.getFooterStemSize(helper.shortestDuration) + : this.getBarStemSize(helper.shortestDuration); + if (helper.isGrace) { + size = size * NoteHeadGlyph.GraceScale; + } + return size; + } + + private getBarStemSize(duration: Duration): number { + let size: number = 0; + switch (duration) { + case Duration.QuadrupleWhole: + size = 6; + break; + case Duration.Half: + size = 6; + break; + case Duration.Quarter: + size = 6; + break; + case Duration.Eighth: + size = 6; + break; + case Duration.Sixteenth: + size = 6; + break; + case Duration.ThirtySecond: + size = 7; + break; + case Duration.SixtyFourth: + size = 7; + break; + case Duration.OneHundredTwentyEighth: + size = 9; + break; + case Duration.TwoHundredFiftySixth: + size = 10; + break; + default: + size = 0; + break; + } + return this.getScoreY(size, 0); + } + + private getFooterStemSize(duration: Duration): number { + let size: number = 0; + switch (duration) { + case Duration.QuadrupleWhole: + size = 6; + break; + case Duration.Half: + size = 6; + break; + case Duration.Quarter: + size = 6; + break; + case Duration.Eighth: + size = 6; + break; + case Duration.Sixteenth: + size = 6; + break; + case Duration.ThirtySecond: + size = 6; + break; + case Duration.SixtyFourth: + size = 6; + break; + case Duration.OneHundredTwentyEighth: + size = 6; + break; + case Duration.TwoHundredFiftySixth: + size = 6; + break; + default: + size = 0; + break; + } + return this.getScoreY(size, 0); + } + + public getYPositionForNoteValue(noteValue: number): number { + return this.getScoreY(this.accidentalHelper.getNoteLineForValue(noteValue, true), 0); + } + + public calculateBeamY(h: BeamingHelper, x: number): number { + let stemSize: number = this.getStemSize(h); + return h.calculateBeamY(stemSize, this.scale, x, this.scale, this); + } + + private calculateBeamYWithDirection(h: BeamingHelper, x: number, direction: BeamDirection): number { + let stemSize: number = this.getStemSize(h); + return h.calculateBeamYWithDirection(stemSize, this.scale, x, this.scale, this, direction); + } + + private paintBar(cx: number, cy: number, canvas: ICanvas, h: BeamingHelper): void { + for (let i: number = 0, j: number = h.beats.length; i < j; i++) { + let beat: Beat = h.beats[i]; + let isGrace: boolean = beat.graceType !== GraceType.None; + let scaleMod: number = isGrace ? NoteHeadGlyph.GraceScale : 1; + // + // draw line + // + let beatLineX: number = h.getBeatLineX(beat) + this.scale; + let direction: BeamDirection = h.direction; + let y1: number = cy + this.y; + y1 += + direction === BeamDirection.Up + ? this.getYPositionForNoteValue(h.getBeatMinValue(beat)) + : this.getYPositionForNoteValue(h.getBeatMaxValue(beat)); + let y2: number = cy + this.y; + y2 += this.calculateBeamY(h, beatLineX); + canvas.lineWidth = ScoreBarRenderer.StemWidth * this.scale; + canvas.beginPath(); + canvas.moveTo(cx + this.x + beatLineX, y1); + canvas.lineTo(cx + this.x + beatLineX, y2); + canvas.stroke(); + canvas.lineWidth = this.scale; + let fingeringY: number = y2; + if (direction === BeamDirection.Down) { + fingeringY += canvas.font.size * 2; + } else if (i !== 0) { + fingeringY -= canvas.font.size * 1.5; + } + this.paintFingering(canvas, beat, cx + this.x + beatLineX, direction, fingeringY); + let brokenBarOffset: number = 6 * this.scale * scaleMod; + let barSpacing: number = 7 * this.scale * scaleMod; + let barSize: number = (ScoreBarRenderer.LineSpacing / 2) * this.scale * scaleMod; + let barCount: number = ModelUtils.getIndex(beat.duration) - 2; + let barStart: number = cy + this.y; + if (direction === BeamDirection.Down) { + barSpacing = -barSpacing; + barSize = -barSize; + } + for (let barIndex: number = 0; barIndex < barCount; barIndex++) { + let barStartX: number = 0; + let barEndX: number = 0; + let barStartY: number = 0; + let barEndY: number = 0; + let barY: number = barStart + barIndex * barSpacing; + // + // Bar to Next? + // + if (i < h.beats.length - 1) { + // full bar? + if (BeamingHelper.isFullBarJoin(beat, h.beats[i + 1], barIndex)) { + barStartX = beatLineX; + barEndX = h.getBeatLineX(h.beats[i + 1]) + this.scale; + } else if (i === 0 || !BeamingHelper.isFullBarJoin(h.beats[i - 1], beat, barIndex)) { + barStartX = beatLineX; + barEndX = barStartX + brokenBarOffset; + } else { + continue; + } + barStartY = barY + this.calculateBeamY(h, barStartX); + barEndY = barY + this.calculateBeamY(h, barEndX); + ScoreBarRenderer.paintSingleBar( + canvas, + cx + this.x + barStartX, + barStartY, + cx + this.x + barEndX, + barEndY, + barSize + ); + } else if (i > 0 && !BeamingHelper.isFullBarJoin(beat, h.beats[i - 1], barIndex)) { + barStartX = beatLineX - brokenBarOffset; + barEndX = beatLineX; + barStartY = barY + this.calculateBeamY(h, barStartX); + barEndY = barY + this.calculateBeamY(h, barEndX); + ScoreBarRenderer.paintSingleBar( + canvas, + cx + this.x + barStartX, + barStartY, + cx + this.x + barEndX, + barEndY, + barSize + ); + } + } + } + } + + private static paintSingleBar(canvas: ICanvas, x1: number, y1: number, x2: number, y2: number, size: number): void { + canvas.beginPath(); + canvas.moveTo(x1, y1); + canvas.lineTo(x2, y2); + canvas.lineTo(x2, y2 + size); + canvas.lineTo(x1, y1 + size); + canvas.closePath(); + canvas.fill(); + } + + private paintFooter(cx: number, cy: number, canvas: ICanvas, h: BeamingHelper): void { + let beat: Beat = h.beats[0]; + if ( + beat.graceType === GraceType.BendGrace || + (beat.graceType !== GraceType.None && this.settings.notation.notationMode === NotationMode.SongBook) + ) { + return; + } + let isGrace: boolean = beat.graceType !== GraceType.None; + let scaleMod: number = isGrace ? NoteHeadGlyph.GraceScale : 1; + // + // draw line + // + let stemSize: number = this.getFooterStemSize(h.shortestDuration); + let beatLineX: number = h.getBeatLineX(beat) + this.scale; + let direction: BeamDirection = h.direction; + let topY: number = this.getYPositionForNoteValue(h.maxNoteValue); + let bottomY: number = this.getYPositionForNoteValue(h.minNoteValue); + let beamY: number = 0; + let fingeringY: number = 0; + if (direction === BeamDirection.Down) { + bottomY += stemSize * scaleMod; + beamY = bottomY; + fingeringY = cy + this.y + bottomY; + } else { + topY -= stemSize * scaleMod; + beamY = topY; + fingeringY = cy + this.y + topY; + } + this.paintFingering(canvas, beat, cx + this.x + beatLineX, direction, fingeringY); + if ( + beat.duration === Duration.Whole || + beat.duration === Duration.DoubleWhole || + beat.duration === Duration.QuadrupleWhole + ) { + return; + } + canvas.lineWidth = ScoreBarRenderer.StemWidth * this.scale; + canvas.beginPath(); + canvas.moveTo(cx + this.x + beatLineX, cy + this.y + topY); + canvas.lineTo(cx + this.x + beatLineX, cy + this.y + bottomY); + canvas.stroke(); + canvas.lineWidth = this.scale; + if (beat.graceType === GraceType.BeforeBeat) { + let graceSizeY: number = 15 * this.scale; + let graceSizeX: number = 12 * this.scale; + canvas.beginPath(); + if (direction === BeamDirection.Down) { + canvas.moveTo(cx + this.x + beatLineX - graceSizeX / 2, cy + this.y + bottomY - graceSizeY); + canvas.lineTo(cx + this.x + beatLineX + graceSizeX / 2, cy + this.y + bottomY); + } else { + canvas.moveTo(cx + this.x + beatLineX - graceSizeX / 2, cy + this.y + topY + graceSizeY); + canvas.lineTo(cx + this.x + beatLineX + graceSizeX / 2, cy + this.y + topY); + } + canvas.stroke(); + } + // + // Draw beam + // + if (beat.duration > Duration.Quarter || isGrace) { + let glyph: BeamGlyph = new BeamGlyph(beatLineX - this.scale / 2, beamY, beat.duration, direction, isGrace); + glyph.renderer = this; + glyph.doLayout(); + glyph.paint(cx + this.x, cy + this.y, canvas); + } + } + + private paintFingering( + canvas: ICanvas, + beat: Beat, + beatLineX: number, + direction: BeamDirection, + topY: number + ): void { + let settings: Settings = this.settings; + if ( + settings.notation.fingeringMode !== FingeringMode.ScoreDefault && + settings.notation.fingeringMode !== FingeringMode.ScoreForcePiano + ) { + return; + } + if (direction === BeamDirection.Up) { + beatLineX -= 10 * this.scale; + } else { + beatLineX += 3 * this.scale; + } + // sort notes ascending in their value to ensure + // we are drawing the numbers according to their order on the stave + let noteList: Note[] = beat.notes.slice(0); + noteList.sort((a, b) => { + return a.realValue - b.realValue; + }); + for (let n: number = 0; n < noteList.length; n++) { + let note: Note = noteList[n]; + let text: string | null = null; + if (note.leftHandFinger !== Fingers.Unknown) { + text = ModelUtils.fingerToString(settings, beat, note.leftHandFinger, true); + } else if (note.rightHandFinger !== Fingers.Unknown) { + text = ModelUtils.fingerToString(settings, beat, note.rightHandFinger, false); + } + if (!text) { + continue; + } + canvas.fillText(text, beatLineX, topY); + topY -= canvas.font.size | 0; + } + } + + protected createPreBeatGlyphs(): void { + super.createPreBeatGlyphs(); + if (this.bar.masterBar.isRepeatStart) { + this.addPreBeatGlyph(new RepeatOpenGlyph(0, 0, 1.5, 3)); + } + // Clef + if ( + this.isFirstOfLine || + this.bar.clef !== this.bar.previousBar!.clef || + this.bar.clefOttava !== this.bar.previousBar!.clefOttava + ) { + let offset: number = 0; + let correction: number = 0; + switch (this.bar.clef) { + case Clef.Neutral: + offset = 6; + break; + case Clef.F4: + offset = 4; + correction = -1; + break; + case Clef.C3: + offset = 6; + break; + case Clef.C4: + offset = 4; + break; + case Clef.G2: + offset = 8; + break; + } + this.createStartSpacing(); + this.addPreBeatGlyph( + new ClefGlyph(0, this.getScoreY(offset, correction), this.bar.clef, this.bar.clefOttava) + ); + } + // Key signature + if ( + (this.index === 0 && this.bar.masterBar.keySignature !== 0) || + (this.bar.previousBar && this.bar.masterBar.keySignature !== this.bar.previousBar.masterBar.keySignature) + ) { + this.createStartSpacing(); + this.createKeySignatureGlyphs(); + } + // Time Signature + if ( + !this.bar.previousBar || + (this.bar.previousBar && + this.bar.masterBar.timeSignatureNumerator !== this.bar.previousBar.masterBar.timeSignatureNumerator) || + (this.bar.previousBar && + this.bar.masterBar.timeSignatureDenominator !== this.bar.previousBar.masterBar.timeSignatureDenominator) + ) { + this.createStartSpacing(); + this.createTimeSignatureGlyphs(); + } + this.addPreBeatGlyph(new BarNumberGlyph(0, this.getScoreY(-0.5, 0), this.bar.index + 1)); + if (this.bar.isEmpty) { + this.addPreBeatGlyph(new SpacingGlyph(0, 0, 30 * this.scale)); + } + } + + protected createBeatGlyphs(): void { + for (let v: number = 0; v < this.bar.voices.length; v++) { + let voice: Voice = this.bar.voices[v]; + if (this.hasVoiceContainer(voice)) { + this.createVoiceGlyphs(voice); + } + } + } + + protected createPostBeatGlyphs(): void { + super.createPostBeatGlyphs(); + if (this.bar.masterBar.isRepeatEnd) { + this.addPostBeatGlyph(new RepeatCloseGlyph(this.x, 0)); + if (this.bar.masterBar.repeatCount > 2) { + this.addPostBeatGlyph(new RepeatCountGlyph(0, this.getScoreY(-1, -3), this.bar.masterBar.repeatCount)); + } + } else { + this.addPostBeatGlyph(new BarSeperatorGlyph(0, 0)); + } + } + + private _startSpacing: boolean; + + private createStartSpacing(): void { + if (this._startSpacing) { + return; + } + this.addPreBeatGlyph(new SpacingGlyph(0, 0, 2 * this.scale)); + this._startSpacing = true; + } + + private createKeySignatureGlyphs(): void { + let offsetClef: number = 0; + let currentKey: number = this.bar.masterBar.keySignature; + let previousKey: number = !this.bar.previousBar ? 0 : this.bar.previousBar.masterBar.keySignature; + switch (this.bar.clef) { + case Clef.Neutral: + offsetClef = 0; + break; + case Clef.G2: + offsetClef = 1; + break; + case Clef.F4: + offsetClef = 2; + break; + case Clef.C3: + offsetClef = -1; + break; + case Clef.C4: + offsetClef = 1; + break; + } + let newLines: Map = new Map(); + let newGlyphs: Glyph[] = []; + // how many symbols do we need to get from a C-keysignature + // to the new one + // var offsetSymbols = (currentKey <= 7) ? currentKey : currentKey - 7; + // a sharp keysignature + if (ModelUtils.keySignatureIsSharp(currentKey)) { + for (let i: number = 0; i < Math.abs(currentKey); i++) { + let step: number = ScoreBarRenderer.SharpKsSteps[i] + offsetClef; + newGlyphs.push(new AccidentalGlyph(0, this.getScoreY(step, 0), AccidentalType.Sharp, false)); + newLines.set(step, true); + } + } else { + for (let i: number = 0; i < Math.abs(currentKey); i++) { + let step: number = ScoreBarRenderer.FlatKsSteps[i] + offsetClef; + newGlyphs.push(new AccidentalGlyph(0, this.getScoreY(step, 0), AccidentalType.Flat, false)); + newLines.set(step, true); + } + } + // naturalize previous key + let naturalizeSymbols: number = Math.abs(previousKey); + let previousKeyPositions = ModelUtils.keySignatureIsSharp(previousKey) + ? ScoreBarRenderer.SharpKsSteps + : ScoreBarRenderer.FlatKsSteps; + for (let i: number = 0; i < naturalizeSymbols; i++) { + let step: number = previousKeyPositions[i] + offsetClef; + if (!newLines.has(step)) { + this.addPreBeatGlyph( + new AccidentalGlyph( + 0, + this.getScoreY(previousKeyPositions[i] + offsetClef, 0), + AccidentalType.Natural, + false + ) + ); + } + } + for (let newGlyph of newGlyphs) { + this.addPreBeatGlyph(newGlyph); + } + } + + private createTimeSignatureGlyphs(): void { + this.addPreBeatGlyph(new SpacingGlyph(0, 0, 5 * this.scale)); + this.addPreBeatGlyph( + new ScoreTimeSignatureGlyph( + 0, + this.getScoreY(2, 0), + this.bar.masterBar.timeSignatureNumerator, + this.bar.masterBar.timeSignatureDenominator, + this.bar.masterBar.timeSignatureCommon + ) + ); + } + + private createVoiceGlyphs(v: Voice): void { + for (let i: number = 0, j: number = v.beats.length; i < j; i++) { + let b: Beat = v.beats[i]; + let container: ScoreBeatContainerGlyph = new ScoreBeatContainerGlyph(b, this.getOrCreateVoiceContainer(v)); + container.preNotes = new ScoreBeatPreNotesGlyph(); + container.onNotes = new ScoreBeatGlyph(); + this.addBeatGlyph(container); + } + } + + // TODO[performance]: Maybe we should cache this (check profiler) + public getNoteLine(n: Note): number { + return this.accidentalHelper.getNoteLine(n); + } + + /** + * Gets the relative y position of the given steps relative to first line. + * @param steps the amount of steps while 2 steps are one line + * @param correction + * @returns + */ + public getScoreY(steps: number, correction: number = 0): number { + return (this.lineOffset / 2) * steps + correction * this.scale; + } + + // private static readonly Random Random = new Random(); + protected paintBackground(cx: number, cy: number, canvas: ICanvas): void { + super.paintBackground(cx, cy, canvas); + let res: RenderingResources = this.resources; + // var c = new Color((byte)Platform.Random(255), + // (byte)Platform.Random(255), + // (byte)Platform.Random(255), + // 100); + // canvas.Color = c; + // canvas.FillRect(cx + X, cy + Y, Width, Height); + // + // draw string lines + // + canvas.color = res.staffLineColor; + let lineY: number = cy + this.y + this.topPadding; + let lineOffset: number = this.lineOffset; + for (let i: number = 0; i < 5; i++) { + if (i > 0) { + lineY += lineOffset; + } + canvas.fillRect(cx + this.x, lineY | 0, this.width, this.scale); + } + canvas.color = res.mainGlyphColor; + this.paintSimileMark(cx, cy, canvas); + } +} diff --git a/src/rendering/ScoreBarRendererFactory.ts b/src/rendering/ScoreBarRendererFactory.ts new file mode 100644 index 000000000..67e156e42 --- /dev/null +++ b/src/rendering/ScoreBarRendererFactory.ts @@ -0,0 +1,22 @@ +import { Bar } from '@src/model/Bar'; +import { BarRendererBase } from '@src/rendering/BarRendererBase'; +import { BarRendererFactory } from '@src/rendering/BarRendererFactory'; +import { ScoreBarRenderer } from '@src/rendering/ScoreBarRenderer'; +import { ScoreRenderer } from '@src/rendering/ScoreRenderer'; + +/** + * This Factory procudes ScoreBarRenderer instances + */ +export class ScoreBarRendererFactory extends BarRendererFactory { + public get staffId(): string { + return ScoreBarRenderer.StaffId; + } + + public create(renderer: ScoreRenderer, bar: Bar): BarRendererBase { + return new ScoreBarRenderer(renderer, bar); + } + + public constructor() { + super(); + } +} diff --git a/src/rendering/ScoreBeatContainerGlyph.ts b/src/rendering/ScoreBeatContainerGlyph.ts new file mode 100644 index 000000000..4ecf1cecc --- /dev/null +++ b/src/rendering/ScoreBeatContainerGlyph.ts @@ -0,0 +1,114 @@ +import { Beat } from '@src/model/Beat'; +import { GraceType } from '@src/model/GraceType'; +import { Note } from '@src/model/Note'; +import { SlideInType } from '@src/model/SlideInType'; +import { SlideOutType } from '@src/model/SlideOutType'; +import { BeatContainerGlyph } from '@src/rendering/glyphs/BeatContainerGlyph'; +import { ScoreBendGlyph } from '@src/rendering/glyphs/ScoreBendGlyph'; +import { ScoreLegatoGlyph } from '@src/rendering/glyphs/ScoreLegatoGlyph'; +import { ScoreSlideLineGlyph } from '@src/rendering/glyphs/ScoreSlideLineGlyph'; +import { ScoreSlurGlyph } from '@src/rendering/glyphs/ScoreSlurGlyph'; +import { ScoreTieGlyph } from '@src/rendering/glyphs/ScoreTieGlyph'; +import { VoiceContainerGlyph } from '@src/rendering/glyphs/VoiceContainerGlyph'; +import { BeamDirection } from '@src/rendering/utils/BeamDirection'; + +export class ScoreBeatContainerGlyph extends BeatContainerGlyph { + private _bend: ScoreBendGlyph | null = null; + private _effectSlur: ScoreSlurGlyph | null = null; + private _effectEndSlur: ScoreSlurGlyph | null = null; + + public constructor(beat: Beat, voiceContainer: VoiceContainerGlyph) { + super(beat, voiceContainer); + } + + public doLayout(): void { + this._effectSlur = null; + this._effectEndSlur = null; + super.doLayout(); + if (this.beat.isLegatoOrigin) { + // only create slur for very first origin of "group" + if (!this.beat.previousBeat || !this.beat.previousBeat.isLegatoOrigin) { + // tie with end beat + let destination: Beat = this.beat.nextBeat!; + while (destination.nextBeat && destination.nextBeat.isLegatoDestination) { + destination = destination.nextBeat; + } + this.ties.push(new ScoreLegatoGlyph(this.beat, destination, false)); + } + } else if (this.beat.isLegatoDestination) { + // only create slur for last destination of "group" + if (!this.beat.isLegatoOrigin) { + let origin: Beat = this.beat.previousBeat!; + while (origin.previousBeat && origin.previousBeat.isLegatoOrigin) { + origin = origin.previousBeat; + } + this.ties.push(new ScoreLegatoGlyph(origin, this.beat, true)); + } + } + if (this._bend) { + this._bend.renderer = this.renderer; + this._bend.doLayout(); + this.updateWidth(); + } + } + + protected createTies(n: Note): void { + // create a tie if any effect requires it + if (!n.isVisible) { + return; + } + // NOTE: we create 2 tie glyphs if we have a line break inbetween + // the two notes + if ( + n.isTieOrigin && + !n.hasBend && + !n.beat.hasWhammyBar && + n.beat.graceType !== GraceType.BendGrace && + n.tieDestination && + n.tieDestination.isVisible + ) { + let tie: ScoreTieGlyph = new ScoreTieGlyph(n, n.tieDestination, false); + this.ties.push(tie); + } + if (n.isTieDestination && !n.tieOrigin!.hasBend && !n.beat.hasWhammyBar) { + let tie: ScoreTieGlyph = new ScoreTieGlyph(n.tieOrigin!, n, true); + this.ties.push(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) { + let l: ScoreSlideLineGlyph = new ScoreSlideLineGlyph(n.slideInType, n.slideOutType, n, this); + this.ties.push(l); + } + if (n.isSlurOrigin && n.slurDestination && n.slurDestination.isVisible) { + let tie: ScoreSlurGlyph = new ScoreSlurGlyph(n, n.slurDestination, false); + this.ties.push(tie); + } + if (n.isSlurDestination) { + let tie: ScoreSlurGlyph = new ScoreSlurGlyph(n.slurOrigin!, n, true); + this.ties.push(tie); + } + // start effect slur on first beat + if (!this._effectSlur && n.isEffectSlurOrigin && n.effectSlurDestination) { + this._effectSlur = new ScoreSlurGlyph(n, n.effectSlurDestination, false); + this.ties.push(this._effectSlur); + } + // end effect slur on last beat + if (!this._effectEndSlur && n.beat.isEffectSlurDestination && n.beat.effectSlurOrigin) { + let direction: BeamDirection = this.onNotes.beamingHelper.direction; + let startNote: Note = + direction === BeamDirection.Up ? n.beat.effectSlurOrigin.minNote! : n.beat.effectSlurOrigin.maxNote!; + let endNote: Note = direction === BeamDirection.Up ? n.beat.minNote! : n.beat.maxNote!; + this._effectEndSlur = new ScoreSlurGlyph(startNote, endNote, true); + this.ties.push(this._effectEndSlur); + } + if (n.hasBend) { + if (!this._bend) { + this._bend = new ScoreBendGlyph(n.beat); + this._bend.renderer = this.renderer; + this.ties.push(this._bend); + } + this._bend.addBends(n); + } + } +} diff --git a/src/rendering/ScoreRenderer.ts b/src/rendering/ScoreRenderer.ts new file mode 100644 index 000000000..2836d1a0b --- /dev/null +++ b/src/rendering/ScoreRenderer.ts @@ -0,0 +1,177 @@ +import { LayoutMode } from '@src/DisplaySettings'; +import { Environment } from '@src/Environment'; +import { EventEmitter, IEventEmitter, IEventEmitterOfT, EventEmitterOfT } from '@src/EventEmitter'; +import { Score } from '@src/model/Score'; +import { Track } from '@src/model/Track'; +import { ICanvas } from '@src/platform/ICanvas'; +import { IScoreRenderer } from '@src/rendering/IScoreRenderer'; +import { ScoreLayout } from '@src/rendering/layout/ScoreLayout'; +import { RenderFinishedEventArgs } from '@src/rendering/RenderFinishedEventArgs'; +import { BoundsLookup } from '@src/rendering/utils/BoundsLookup'; +import { Settings } from '@src/Settings'; +import { Logger } from '@src/Logger'; + +/** + * This is the main wrapper of the rendering engine which + * can render a single track of a score object into a notation sheet. + */ +export class ScoreRenderer implements IScoreRenderer { + private _currentLayoutMode: LayoutMode = LayoutMode.Page; + private _currentRenderEngine: string | null = null; + private _renderedTracks: Track[] | null = null; + + public canvas: ICanvas | null = null; + public score: Score | null = null; + public tracks: Track[] | null = null; + public layout: ScoreLayout | null = null; + public settings: Settings; + public boundsLookup: BoundsLookup | null = null; + public width: number = 0; + + /** + * Initializes a new instance of the {@link ScoreRenderer} class. + * @param settings The settings to use for rendering. + */ + public constructor(settings: Settings) { + this.settings = settings; + this.recreateCanvas(); + this.recreateLayout(); + } + + public destroy(): void { + this.score = null; + this.canvas = null; + this.layout = null; + this.boundsLookup = null; + this.tracks = null; + } + + private recreateCanvas(): boolean { + if (this._currentRenderEngine !== this.settings.core.engine) { + this.canvas = Environment.getRenderEngineFactory(this.settings).createCanvas(); + this._currentRenderEngine = this.settings.core.engine; + return true; + } + return false; + } + + private recreateLayout(): boolean { + if (!this.layout || this._currentLayoutMode !== this.settings.display.layoutMode) { + this.layout = Environment.getLayoutEngineFactory(this.settings).createLayout(this); + this._currentLayoutMode = this.settings.display.layoutMode; + return true; + } + return false; + } + + public renderScore(score: Score, trackIndexes: number[]): void { + try { + this.score = score; + let tracks: Track[]; + if (!trackIndexes) { + tracks = score.tracks.slice(0); + } else { + tracks = []; + for (let track of trackIndexes) { + if (track >= 0 && track < score.tracks.length) { + tracks.push(score.tracks[track]); + } + } + } + if (tracks.length === 0 && score.tracks.length > 0) { + tracks.push(score.tracks[0]); + } + this.tracks = tracks; + this.render(); + } catch (e) { + (this.error as EventEmitterOfT).trigger(e as Error); + } + } + + /** + * Initiates rendering fof the given tracks. + * @param tracks The tracks to render. + */ + public renderTracks(tracks: Track[]): void { + if (tracks.length === 0) { + this.score = null; + } else { + this.score = tracks[0].score; + } + this.tracks = tracks; + this.render(); + } + + public updateSettings(settings: Settings): void { + this.settings = settings; + } + + public render(): void { + if (this.width === 0) { + Logger.warning('Rendering', 'AlphaTab skipped rendering because of width=0 (element invisible)', null); + return; + } + this.boundsLookup = new BoundsLookup(); + if (!this.tracks || this.tracks.length === 0) { + return; + } + this.recreateCanvas(); + this.canvas!.lineWidth = this.settings.display.scale; + this.canvas!.settings = this.settings; + Logger.debug('Rendering', 'Rendering ' + this.tracks.length + ' tracks'); + for (let i: number = 0; i < this.tracks.length; i++) { + let track: Track = this.tracks[i]; + Logger.debug('Rendering', 'Track ' + i + ': ' + track.name); + } + (this.preRender as EventEmitterOfT).trigger(false); + this.recreateLayout(); + this.layoutAndRender(); + this._renderedTracks = this.tracks; + Logger.debug('Rendering', 'Rendering finished'); + } + + public resizeRender(): void { + if (this.recreateLayout() || this.recreateCanvas() || this._renderedTracks !== this.tracks || !this.tracks) { + Logger.debug('Rendering', 'Starting full rerendering due to layout or canvas change', null); + this.render(); + } else if (this.layout!.supportsResize) { + Logger.debug('Rendering', 'Starting optimized rerendering for resize'); + this.boundsLookup = new BoundsLookup(); + (this.preRender as EventEmitterOfT).trigger(true); + this.canvas!.settings = this.settings; + this.layout!.resize(); + this.layout!.renderAnnotation(); + this.onRenderFinished(); + (this.postRenderFinished as EventEmitter).trigger(); + } else { + Logger.warning('Rendering', 'Current layout does not support dynamic resizing, nothing was done', null); + } + Logger.debug('Rendering', 'Resize finished'); + } + + private layoutAndRender(): void { + Logger.debug( + 'Rendering', + 'Rendering at scale ' + this.settings.display.scale + ' with layout ' + this.layout!.name, + null + ); + this.layout!.layoutAndRender(); + this.layout!.renderAnnotation(); + this.onRenderFinished(); + (this.postRenderFinished as EventEmitter).trigger(); + } + + public readonly preRender: IEventEmitterOfT = new EventEmitterOfT(); + public readonly renderFinished: IEventEmitterOfT = new EventEmitterOfT(); + public readonly partialRenderFinished: IEventEmitterOfT = new EventEmitterOfT(); + public readonly postRenderFinished: IEventEmitter = new EventEmitter(); + public readonly error: IEventEmitterOfT = new EventEmitterOfT(); + + private onRenderFinished() { + const e = new RenderFinishedEventArgs(); + e.totalHeight = this.layout!.height; + e.totalWidth = this.layout!.width; + e.renderResult = this.canvas!.onRenderFinished(); + (this.renderFinished as EventEmitterOfT).trigger(e); + } +} diff --git a/src/rendering/TabBarRenderer.ts b/src/rendering/TabBarRenderer.ts new file mode 100644 index 000000000..469c5d5a7 --- /dev/null +++ b/src/rendering/TabBarRenderer.ts @@ -0,0 +1,540 @@ +import { Bar } from '@src/model/Bar'; +import { Beat } from '@src/model/Beat'; +import { Duration } from '@src/model/Duration'; +import { GraceType } from '@src/model/GraceType'; +import { TupletGroup } from '@src/model/TupletGroup'; +import { Voice } from '@src/model/Voice'; +import { TabRhythmMode } from '@src/NotationSettings'; +import { ICanvas, TextAlign } from '@src/platform/ICanvas'; +import { BarRendererBase, NoteYPosition } from '@src/rendering/BarRendererBase'; +import { BarNumberGlyph } from '@src/rendering/glyphs/BarNumberGlyph'; +import { BarSeperatorGlyph } from '@src/rendering/glyphs/BarSeperatorGlyph'; +import { BeamGlyph } from '@src/rendering/glyphs/BeamGlyph'; +import { BeatGlyphBase } from '@src/rendering/glyphs/BeatGlyphBase'; +import { RepeatCloseGlyph } from '@src/rendering/glyphs/RepeatCloseGlyph'; +import { RepeatCountGlyph } from '@src/rendering/glyphs/RepeatCountGlyph'; +import { RepeatOpenGlyph } from '@src/rendering/glyphs/RepeatOpenGlyph'; +import { SpacingGlyph } from '@src/rendering/glyphs/SpacingGlyph'; +import { TabBeatContainerGlyph } from '@src/rendering/glyphs/TabBeatContainerGlyph'; +import { TabBeatGlyph } from '@src/rendering/glyphs/TabBeatGlyph'; +import { TabBeatPreNotesGlyph } from '@src/rendering/glyphs/TabBeatPreNotesGlyph'; +import { TabClefGlyph } from '@src/rendering/glyphs/TabClefGlyph'; +import { TabNoteChordGlyph } from '@src/rendering/glyphs/TabNoteChordGlyph'; +import { TabTimeSignatureGlyph } from '@src/rendering/glyphs/TabTimeSignatureGlyph'; +import { VoiceContainerGlyph } from '@src/rendering/glyphs/VoiceContainerGlyph'; +import { ScoreRenderer } from '@src/rendering/ScoreRenderer'; +import { BeamDirection } from '@src/rendering/utils/BeamDirection'; +import { BeamingHelper } from '@src/rendering/utils/BeamingHelper'; +import { RenderingResources } from '@src/RenderingResources'; +import { ModelUtils } from '@src/model/ModelUtils'; + +/** + * This BarRenderer renders a bar using guitar tablature notation + */ +export class TabBarRenderer extends BarRendererBase { + public static readonly StaffId: string = 'tab'; + public static readonly LineSpacing: number = 10; + + private _tupletSize: number = 0; + + public showTimeSignature: boolean = false; + public showRests: boolean = false; + public showTiedNotes: boolean = false; + + public constructor(renderer: ScoreRenderer, bar: Bar) { + super(renderer, bar); + } + + public get lineOffset(): number { + return (TabBarRenderer.LineSpacing + 1) * this.scale; + } + + protected updateSizes(): void { + let res: RenderingResources = this.resources; + let numberOverflow: number = res.tablatureFont.size / 2 + res.tablatureFont.size * 0.2; + this.topPadding = numberOverflow; + this.bottomPadding = numberOverflow; + this.height = this.lineOffset * (this.bar.staff.tuning.length - 1) + numberOverflow * 2; + if (this.settings.notation.rhythmMode !== TabRhythmMode.Hidden) { + this.height += this.settings.notation.rhythmHeight * this.settings.display.scale; + this.bottomPadding += this.settings.notation.rhythmHeight * this.settings.display.scale; + } + super.updateSizes(); + } + + public doLayout(): void { + super.doLayout(); + if (this.settings.notation.rhythmMode !== TabRhythmMode.Hidden) { + let hasTuplets: boolean = false; + for (let voice of this.bar.voices) { + if (this.hasVoiceContainer(voice)) { + let c: VoiceContainerGlyph = this.getOrCreateVoiceContainer(voice); + if (c.tupletGroups.length > 0) { + hasTuplets = true; + break; + } + } + } + if (hasTuplets) { + this._tupletSize = this.resources.effectFont.size * 0.8; + this.registerOverflowBottom(this._tupletSize); + } + } + } + + protected createPreBeatGlyphs(): void { + super.createPreBeatGlyphs(); + if (this.bar.masterBar.isRepeatStart) { + this.addPreBeatGlyph(new RepeatOpenGlyph(0, 0, 1.5, 3)); + } + // Clef + if (this.isFirstOfLine) { + let center: number = (this.bar.staff.tuning.length + 1) / 2; + this.addPreBeatGlyph(new TabClefGlyph(5 * this.scale, this.getTabY(center, 0))); + } + // Time Signature + if ( + this.showTimeSignature && + (!this.bar.previousBar || + (this.bar.previousBar && + this.bar.masterBar.timeSignatureNumerator !== + this.bar.previousBar.masterBar.timeSignatureNumerator) || + (this.bar.previousBar && + this.bar.masterBar.timeSignatureDenominator !== + this.bar.previousBar.masterBar.timeSignatureDenominator)) + ) { + this.createStartSpacing(); + this.createTimeSignatureGlyphs(); + } + this.addPreBeatGlyph(new BarNumberGlyph(0, this.getTabY(-0.5, 0), this.bar.index + 1)); + if (this.bar.isEmpty) { + this.addPreBeatGlyph(new SpacingGlyph(0, 0, 30 * this.scale)); + } + } + + private _startSpacing: boolean = false; + + private createStartSpacing(): void { + if (this._startSpacing) { + return; + } + this.addPreBeatGlyph(new SpacingGlyph(0, 0, 2 * this.scale)); + this._startSpacing = true; + } + + private createTimeSignatureGlyphs(): void { + this.addPreBeatGlyph(new SpacingGlyph(0, 0, 5 * this.scale)); + this.addPreBeatGlyph( + new TabTimeSignatureGlyph( + 0, + this.getTabY(0, 0), + this.bar.masterBar.timeSignatureNumerator, + this.bar.masterBar.timeSignatureDenominator, + this.bar.masterBar.timeSignatureCommon + ) + ); + } + + protected createBeatGlyphs(): void { + for (let v: number = 0; v < this.bar.voices.length; v++) { + let voice: Voice = this.bar.voices[v]; + if (this.hasVoiceContainer(voice)) { + this.createVoiceGlyphs(this.bar.voices[v]); + } + } + } + + private createVoiceGlyphs(v: Voice): void { + for (let i: number = 0, j: number = v.beats.length; i < j; i++) { + let b: Beat = v.beats[i]; + let container: TabBeatContainerGlyph = new TabBeatContainerGlyph(b, this.getOrCreateVoiceContainer(v)); + container.preNotes = new TabBeatPreNotesGlyph(); + container.onNotes = new TabBeatGlyph(); + this.addBeatGlyph(container); + } + } + + protected createPostBeatGlyphs(): void { + super.createPostBeatGlyphs(); + if (this.bar.masterBar.isRepeatEnd) { + this.addPostBeatGlyph(new RepeatCloseGlyph(this.x, 0)); + if (this.bar.masterBar.repeatCount > 2) { + this.addPostBeatGlyph(new RepeatCountGlyph(0, this.getTabY(-0.5, -3), this.bar.masterBar.repeatCount)); + } + } else { + this.addPostBeatGlyph(new BarSeperatorGlyph(0, 0)); + } + } + + /** + * Gets the relative y position of the given steps relative to first line. + * @param line the amount of steps while 2 steps are one line + * @param correction + * @returns + */ + public getTabY(line: number, correction: number = 0): number { + return this.lineOffset * line + correction * this.scale; + } + + protected paintBackground(cx: number, cy: number, canvas: ICanvas): void { + super.paintBackground(cx, cy, canvas); + let res: RenderingResources = this.resources; + // + // draw string lines + // + canvas.color = res.staffLineColor; + let lineY: number = cy + this.y + this.topPadding; + let padding: number = this.scale; + // collect tab note position for spaces + let tabNotes: Float32Array[][] = []; + for (let i: number = 0, j: number = this.bar.staff.tuning.length; i < j; i++) { + tabNotes.push([]); + } + for (let voice of this.bar.voices) { + if (this.hasVoiceContainer(voice)) { + let vc: VoiceContainerGlyph = this.getOrCreateVoiceContainer(voice); + for (let bg of vc.beatGlyphs) { + let notes: TabBeatGlyph = bg.onNotes as TabBeatGlyph; + let noteNumbers: TabNoteChordGlyph | null = notes.noteNumbers; + if (noteNumbers) { + noteNumbers.notesPerString.forEach((noteNumber, str) => { + if (!noteNumber.isEmpty) { + tabNotes[this.bar.staff.tuning.length - str].push( + new Float32Array([ + vc.x + bg.x + notes.x + noteNumbers!.x, + noteNumbers!.width + padding + ]) + ); + } + }); + } + } + } + } + // if we have multiple voices we need to sort by X-position, otherwise have a wild mix in the list + // but painting relies on ascending X-position + for (let line of tabNotes) { + line.sort((a, b) => { + return a[0] > b[0] ? 1 : a[0] < b[0] ? -1 : 0; + }); + } + let lineOffset: number = this.lineOffset; + for (let i: number = 0, j: number = this.bar.staff.tuning.length; i < j; i++) { + if (i > 0) { + lineY += lineOffset; + } + let lineX: number = 0; + for (let line of tabNotes[i]) { + canvas.fillRect(cx + this.x + lineX, lineY | 0, line[0] - lineX, this.scale); + lineX = line[0] + line[1]; + } + canvas.fillRect(cx + this.x + lineX, lineY | 0, this.width - lineX, this.scale); + } + canvas.color = res.mainGlyphColor; + this.paintSimileMark(cx, cy, canvas); + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + super.paint(cx, cy, canvas); + if (this.settings.notation.rhythmMode !== TabRhythmMode.Hidden) { + this.paintBeams(cx, cy, canvas); + this.paintTuplets(cx, cy, canvas); + } + } + + private paintBeams(cx: number, cy: number, canvas: ICanvas): void { + for (let i: number = 0, j: number = this.helpers.beamHelpers.length; i < j; i++) { + let v: BeamingHelper[] = this.helpers.beamHelpers[i]; + for (let k: number = 0, l: number = v.length; k < l; k++) { + let h: BeamingHelper = v[k]; + this.paintBeamHelper(cx + this.beatGlyphsStart, cy, canvas, h); + } + } + } + + private paintTuplets(cx: number, cy: number, canvas: ICanvas): void { + for (let voice of this.bar.voices) { + if (this.hasVoiceContainer(voice)) { + let container: VoiceContainerGlyph = this.getOrCreateVoiceContainer(voice); + for (let tupletGroup of container.tupletGroups) { + this.paintTupletHelper(cx + this.beatGlyphsStart, cy, canvas, tupletGroup); + } + } + } + } + + private paintBeamHelper(cx: number, cy: number, canvas: ICanvas, h: BeamingHelper): void { + canvas.color = h.voice!.index === 0 ? this.resources.mainGlyphColor : this.resources.secondaryGlyphColor; + // check if we need to paint simple footer + if (h.beats.length === 1 || this.settings.notation.rhythmMode === TabRhythmMode.ShowWithBeams) { + this.paintFooter(cx, cy, canvas, h); + } else { + this.paintBar(cx, cy, canvas, h); + } + } + + private paintBar(cx: number, cy: number, canvas: ICanvas, h: BeamingHelper): void { + for (let i: number = 0, j: number = h.beats.length; i < j; i++) { + let beat: Beat = h.beats[i]; + if (h.hasBeatLineX(beat)) { + // + // draw line + // + let beatLineX: number = h.getBeatLineX(beat); + let y1: number = cy + this.y; + let y2: number = cy + this.y + this.height - this._tupletSize; + let startGlyph: TabBeatGlyph = this.getOnNotesGlyphForBeat(beat) as TabBeatGlyph; + if (!startGlyph.noteNumbers) { + y1 += + this.height - + this.settings.notation.rhythmHeight * this.settings.display.scale - + this._tupletSize; + } else { + y1 += + startGlyph.noteNumbers.getNoteY(startGlyph.noteNumbers.minStringNote!, NoteYPosition.Bottom) + + this.lineOffset / 2; + } + if (h.direction === BeamDirection.Up) { + beatLineX -= startGlyph.width / 2; + } else { + beatLineX += startGlyph.width / 2; + } + canvas.beginPath(); + canvas.moveTo(cx + this.x + beatLineX, y1); + canvas.lineTo(cx + this.x + beatLineX, y2); + canvas.stroke(); + let brokenBarOffset: number = 6 * this.scale; + let barSpacing: number = -6 * this.scale; + let barSize: number = 3 * this.scale; + let barCount: number = ModelUtils.getIndex(beat.duration) - 2; + let barStart: number = y2; + for (let barIndex: number = 0; barIndex < barCount; barIndex++) { + let barStartX: number = 0; + let barEndX: number = 0; + let barStartY: number = 0; + let barEndY: number = 0; + let barY: number = barStart + barIndex * barSpacing; + // + // Broken Bar to Next + // + if (h.beats.length === 1) { + barStartX = beatLineX; + barEndX = beatLineX + brokenBarOffset; + barStartY = barY; + barEndY = barY; + TabBarRenderer.paintSingleBar( + canvas, + cx + this.x + barStartX, + barStartY, + cx + this.x + barEndX, + barEndY, + barSize + ); + } else if (i < h.beats.length - 1) { + // full bar? + if (BeamingHelper.isFullBarJoin(beat, h.beats[i + 1], barIndex)) { + barStartX = beatLineX; + barEndX = h.getBeatLineX(h.beats[i + 1]) + this.scale; + let endGlyph: BeatGlyphBase = this.getOnNotesGlyphForBeat(h.beats[i + 1]); + if (h.direction === BeamDirection.Up) { + barEndX -= endGlyph.width / 2; + } else { + barEndX += endGlyph.width / 2; + } + } else if (i === 0 || !BeamingHelper.isFullBarJoin(h.beats[i - 1], beat, barIndex)) { + barStartX = beatLineX; + barEndX = barStartX + brokenBarOffset; + } else { + continue; + } + barStartY = barY; + barEndY = barY; + TabBarRenderer.paintSingleBar( + canvas, + cx + this.x + barStartX, + barStartY, + cx + this.x + barEndX, + barEndY, + barSize + ); + } else if (i > 0 && !BeamingHelper.isFullBarJoin(beat, h.beats[i - 1], barIndex)) { + barStartX = beatLineX - brokenBarOffset; + barEndX = beatLineX; + barStartY = barY; + barEndY = barY; + TabBarRenderer.paintSingleBar( + canvas, + cx + this.x + barStartX, + barStartY, + cx + this.x + barEndX, + barEndY, + barSize + ); + } + } + } + } + } + + private paintTupletHelper(cx: number, cy: number, canvas: ICanvas, h: TupletGroup): void { + let res: RenderingResources = this.resources; + let oldAlign: TextAlign = canvas.textAlign; + canvas.color = h.voice.index === 0 ? this.resources.mainGlyphColor : this.resources.secondaryGlyphColor; + canvas.textAlign = TextAlign.Center; + let s: string; + let num: number = h.beats[0].tupletNumerator; + let den: number = h.beats[0].tupletDenominator; + // list as in Guitar Pro 7. for certain tuplets only the numerator is shown + if (num === 2 && den === 3) { + s = '2'; + } else if (num === 3 && den === 2) { + s = '3'; + } else if (num === 4 && den === 6) { + s = '4'; + } else if (num === 5 && den === 4) { + s = '5'; + } else if (num === 6 && den === 4) { + s = '6'; + } else if (num === 7 && den === 4) { + s = '7'; + } else if (num === 9 && den === 8) { + s = '9'; + } else if (num === 10 && den === 8) { + s = '10'; + } else if (num === 11 && den === 8) { + s = '11'; + } else if (num === 12 && den === 8) { + s = '12'; + } else if (num === 13 && den === 8) { + s = '13'; + } else { + s = num + ':' + den; + } + // check if we need to paint simple footer + if (h.beats.length === 1 || !h.isFull) { + for (let i: number = 0, j: number = h.beats.length; i < j; i++) { + let beat: Beat = h.beats[i]; + let beamingHelper: BeamingHelper = this.helpers.beamHelperLookup[h.voice.index].get(beat.index)!; + if (!beamingHelper) { + continue; + } + let tupletX: number = beamingHelper.getBeatLineX(beat); + let startGlyph: TabBeatGlyph = this.getOnNotesGlyphForBeat(beat) as TabBeatGlyph; + if (beamingHelper.direction === BeamDirection.Up) { + tupletX -= startGlyph.width / 2; + } else { + tupletX += startGlyph.width / 2; + } + let tupletY: number = cy + this.y + this.height - this._tupletSize + res.effectFont.size * 0.5; + canvas.font = res.effectFont; + canvas.fillText(s, cx + this.x + tupletX, tupletY); + } + } else { + let firstBeat: Beat = h.beats[0]; + let lastBeat: Beat = h.beats[h.beats.length - 1]; + let firstBeamingHelper: BeamingHelper = this.helpers.beamHelperLookup[h.voice.index].get(firstBeat.index)!; + let lastBeamingHelper: BeamingHelper = this.helpers.beamHelperLookup[h.voice.index].get(lastBeat.index)!; + if (firstBeamingHelper && lastBeamingHelper) { + // + // Calculate the overall area of the tuplet bracket + let startX: number = firstBeamingHelper.getBeatLineX(firstBeat); + let endX: number = lastBeamingHelper.getBeatLineX(lastBeat); + let startGlyph: TabBeatGlyph = this.getOnNotesGlyphForBeat(firstBeat) as TabBeatGlyph; + let endGlyph: TabBeatGlyph = this.getOnNotesGlyphForBeat(firstBeat) as TabBeatGlyph; + if (firstBeamingHelper.direction === BeamDirection.Up) { + startX -= startGlyph.width / 2; + endX -= endGlyph.width / 2; + } else { + startX += startGlyph.width / 2; + endX += endGlyph.width / 2; + } + // + // Calculate how many space the text will need + canvas.font = res.effectFont; + let sw: number = canvas.measureText(s); + let sp: number = 3 * this.scale; + // + // Calculate the offsets where to break the bracket + let middleX: number = (startX + endX) / 2; + let offset1X: number = middleX - sw / 2 - sp; + let offset2X: number = middleX + sw / 2 + sp; + // + // calculate the y positions for our bracket + let startY: number = cy + this.y + this.height - this._tupletSize + res.effectFont.size * 0.5; + let offset: number = -res.effectFont.size * 0.25; + let size: number = -5 * this.scale; + // + // draw the bracket + canvas.beginPath(); + canvas.moveTo(cx + this.x + startX, (startY - offset) | 0); + canvas.lineTo(cx + this.x + startX, (startY - offset - size) | 0); + canvas.lineTo(cx + this.x + offset1X, (startY - offset - size) | 0); + canvas.stroke(); + canvas.beginPath(); + canvas.moveTo(cx + this.x + offset2X, (startY - offset - size) | 0); + canvas.lineTo(cx + this.x + endX, (startY - offset - size) | 0); + canvas.lineTo(cx + this.x + endX, (startY - offset) | 0); + canvas.stroke(); + // + // Draw the string + canvas.fillText(s, cx + this.x + middleX, startY); + } + } + canvas.textAlign = oldAlign; + } + + private static paintSingleBar(canvas: ICanvas, x1: number, y1: number, x2: number, y2: number, size: number): void { + canvas.beginPath(); + canvas.moveTo(x1, y1); + canvas.lineTo(x2, y2); + canvas.lineTo(x2, y2 - size); + canvas.lineTo(x1, y1 - size); + canvas.closePath(); + canvas.fill(); + } + + private paintFooter(cx: number, cy: number, canvas: ICanvas, h: BeamingHelper): void { + for (let beat of h.beats) { + if ( + beat.graceType !== GraceType.None || + beat.duration === Duration.Whole || + beat.duration === Duration.DoubleWhole || + beat.duration === Duration.QuadrupleWhole + ) { + return; + } + // + // draw line + // + let beatLineX: number = h.getBeatLineX(beat); + let y1: number = cy + this.y; + let y2: number = cy + this.y + this.height - this._tupletSize; + let startGlyph: TabBeatGlyph = this.getOnNotesGlyphForBeat(beat) as TabBeatGlyph; + if (!startGlyph.noteNumbers) { + y1 += + this.height - this.settings.notation.rhythmHeight * this.settings.display.scale - this._tupletSize; + } else { + y1 += + startGlyph.noteNumbers.getNoteY(startGlyph.noteNumbers.minStringNote!, NoteYPosition.Bottom); + } + if (h.direction === BeamDirection.Up) { + beatLineX -= startGlyph.width / 2; + } else { + beatLineX += startGlyph.width / 2; + } + canvas.beginPath(); + canvas.moveTo(cx + this.x + beatLineX, y1); + canvas.lineTo(cx + this.x + beatLineX, y2); + canvas.stroke(); + // + // Draw beam + // + if (beat.duration > Duration.Quarter) { + let glyph: BeamGlyph = new BeamGlyph(0, 0, beat.duration, BeamDirection.Down, false); + glyph.renderer = this; + glyph.doLayout(); + glyph.paint(cx + this.x + beatLineX, y2, canvas); + } + } + } +} diff --git a/src/rendering/TabBarRendererFactory.ts b/src/rendering/TabBarRendererFactory.ts new file mode 100644 index 000000000..f29b2a4bd --- /dev/null +++ b/src/rendering/TabBarRendererFactory.ts @@ -0,0 +1,40 @@ +import { Bar } from '@src/model/Bar'; +import { Staff } from '@src/model/Staff'; +import { Track } from '@src/model/Track'; +import { BarRendererBase } from '@src/rendering/BarRendererBase'; +import { BarRendererFactory } from '@src/rendering/BarRendererFactory'; +import { ScoreRenderer } from '@src/rendering/ScoreRenderer'; +import { TabBarRenderer } from '@src/rendering/TabBarRenderer'; + +/** + * This Factory produces TabBarRenderer instances + */ +export class TabBarRendererFactory extends BarRendererFactory { + private _showTimeSignature: boolean; + private _showRests: boolean; + private _showTiedNotes: boolean; + + public get staffId(): string { + return TabBarRenderer.StaffId; + } + + public constructor(showTimeSignature: boolean, showRests: boolean, showTiedNotes: boolean) { + super(); + this._showTimeSignature = showTimeSignature; + this._showRests = showRests; + this._showTiedNotes = showTiedNotes; + this.hideOnPercussionTrack = true; + } + + public canCreate(track: Track, staff: Staff): boolean { + return staff.tuning.length > 0 && super.canCreate(track, staff); + } + + public create(renderer: ScoreRenderer, bar: Bar): BarRendererBase { + let tabBarRenderer: TabBarRenderer = new TabBarRenderer(renderer, bar); + tabBarRenderer.showRests = this._showRests; + tabBarRenderer.showTimeSignature = this._showTimeSignature; + tabBarRenderer.showTiedNotes = this._showTiedNotes; + return tabBarRenderer; + } +} diff --git a/src/rendering/effects/AlternateEndingsEffectInfo.ts b/src/rendering/effects/AlternateEndingsEffectInfo.ts new file mode 100644 index 000000000..d5f5e308f --- /dev/null +++ b/src/rendering/effects/AlternateEndingsEffectInfo.ts @@ -0,0 +1,38 @@ +import { Beat } from '@src/model/Beat'; +import { BarRendererBase } from '@src/rendering/BarRendererBase'; +import { EffectBarGlyphSizing } from '@src/rendering/EffectBarGlyphSizing'; +import { AlternateEndingsGlyph } from '@src/rendering/glyphs/AlternateEndingsGlyph'; +import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph'; +import { EffectBarRendererInfo } from '@src/rendering/EffectBarRendererInfo'; +import { Settings } from '@src/Settings'; +import { NotationElement } from '@src/NotationSettings'; + +export class AlternateEndingsEffectInfo extends EffectBarRendererInfo { + public get notationElement(): NotationElement { + return NotationElement.EffectAlternateEndings; + } + + public get hideOnMultiTrack(): boolean { + return true; + } + + public get canShareBand(): boolean { + return false; + } + + public get sizingMode(): EffectBarGlyphSizing { + return EffectBarGlyphSizing.FullBar; + } + + public shouldCreateGlyph(settings: Settings, beat: Beat): boolean { + return beat.index === 0 && beat.voice.bar.masterBar.alternateEndings !== 0; + } + + public createNewGlyph(renderer: BarRendererBase, beat: Beat): EffectGlyph { + return new AlternateEndingsGlyph(0, 0, beat.voice.bar.masterBar.alternateEndings); + } + + public canExpand(from: Beat, to: Beat): boolean { + return true; + } +} diff --git a/src/rendering/effects/CapoEffectInfo.ts b/src/rendering/effects/CapoEffectInfo.ts new file mode 100644 index 000000000..851794257 --- /dev/null +++ b/src/rendering/effects/CapoEffectInfo.ts @@ -0,0 +1,45 @@ +import { Beat } from '@src/model/Beat'; +import { TextAlign } from '@src/platform/ICanvas'; +import { BarRendererBase } from '@src/rendering/BarRendererBase'; +import { EffectBarGlyphSizing } from '@src/rendering/EffectBarGlyphSizing'; +import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph'; +import { TextGlyph } from '@src/rendering/glyphs/TextGlyph'; +import { EffectBarRendererInfo } from '@src/rendering/EffectBarRendererInfo'; +import { Settings } from '@src/Settings'; +import { NotationElement } from '@src/NotationSettings'; + +export class CapoEffectInfo extends EffectBarRendererInfo { + public get notationElement(): NotationElement { + return NotationElement.EffectCapo; + } + + public get hideOnMultiTrack(): boolean { + return false; + } + + public get canShareBand(): boolean { + return false; + } + + public get sizingMode(): EffectBarGlyphSizing { + return EffectBarGlyphSizing.SingleOnBeat; + } + + public shouldCreateGlyph(settings: Settings, beat: Beat): boolean { + return beat.index === 0 && beat.voice.bar.index === 0 && beat.voice.bar.staff.capo !== 0; + } + + public createNewGlyph(renderer: BarRendererBase, beat: Beat): EffectGlyph { + return new TextGlyph( + 0, + 0, + 'Capo. fret ' + beat.voice.bar.staff.capo, + renderer.resources.effectFont, + TextAlign.Left + ); + } + + public canExpand(from: Beat, to: Beat): boolean { + return false; + } +} diff --git a/src/rendering/effects/ChordsEffectInfo.ts b/src/rendering/effects/ChordsEffectInfo.ts new file mode 100644 index 000000000..2a0c5a697 --- /dev/null +++ b/src/rendering/effects/ChordsEffectInfo.ts @@ -0,0 +1,39 @@ +import { Beat } from '@src/model/Beat'; +import { TextAlign } from '@src/platform/ICanvas'; +import { BarRendererBase } from '@src/rendering/BarRendererBase'; +import { EffectBarGlyphSizing } from '@src/rendering/EffectBarGlyphSizing'; +import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph'; +import { TextGlyph } from '@src/rendering/glyphs/TextGlyph'; +import { EffectBarRendererInfo } from '@src/rendering/EffectBarRendererInfo'; +import { Settings } from '@src/Settings'; +import { NotationElement } from '@src/NotationSettings'; + +export class ChordsEffectInfo extends EffectBarRendererInfo { + public get notationElement(): NotationElement { + return NotationElement.EffectChordNames; + } + + public get hideOnMultiTrack(): boolean { + return false; + } + + public get canShareBand(): boolean { + return true; + } + + public get sizingMode(): EffectBarGlyphSizing { + return EffectBarGlyphSizing.SingleOnBeat; + } + + public shouldCreateGlyph(settings: Settings, beat: Beat): boolean { + return beat.hasChord; + } + + public createNewGlyph(renderer: BarRendererBase, beat: Beat): EffectGlyph { + return new TextGlyph(0, 0, beat.chord!.name, renderer.resources.effectFont, TextAlign.Center); + } + + public canExpand(from: Beat, to: Beat): boolean { + return false; + } +} diff --git a/src/rendering/effects/CrescendoEffectInfo.ts b/src/rendering/effects/CrescendoEffectInfo.ts new file mode 100644 index 000000000..70bcfe4ee --- /dev/null +++ b/src/rendering/effects/CrescendoEffectInfo.ts @@ -0,0 +1,39 @@ +import { Beat } from '@src/model/Beat'; +import { CrescendoType } from '@src/model/CrescendoType'; +import { BarRendererBase } from '@src/rendering/BarRendererBase'; +import { EffectBarGlyphSizing } from '@src/rendering/EffectBarGlyphSizing'; +import { CrescendoGlyph } from '@src/rendering/glyphs/CrescendoGlyph'; +import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph'; +import { EffectBarRendererInfo } from '@src/rendering/EffectBarRendererInfo'; +import { Settings } from '@src/Settings'; +import { NotationElement } from '@src/NotationSettings'; + +export class CrescendoEffectInfo extends EffectBarRendererInfo { + public get notationElement(): NotationElement { + return NotationElement.EffectCrescendo; + } + + public get hideOnMultiTrack(): boolean { + return false; + } + + public get canShareBand(): boolean { + return true; + } + + public get sizingMode(): EffectBarGlyphSizing { + return EffectBarGlyphSizing.GroupedOnBeatToEnd; + } + + public shouldCreateGlyph(settings: Settings, beat: Beat): boolean { + return beat.crescendo !== CrescendoType.None; + } + + public createNewGlyph(renderer: BarRendererBase, beat: Beat): EffectGlyph { + return new CrescendoGlyph(0, 0, beat.crescendo); + } + + public canExpand(from: Beat, to: Beat): boolean { + return from.crescendo === to.crescendo; + } +} diff --git a/src/rendering/effects/DummyEffectGlyph.ts b/src/rendering/effects/DummyEffectGlyph.ts new file mode 100644 index 000000000..01c98b498 --- /dev/null +++ b/src/rendering/effects/DummyEffectGlyph.ts @@ -0,0 +1,23 @@ +import { ICanvas } from '@src/platform/ICanvas'; +import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph'; +import { RenderingResources } from '@src/RenderingResources'; + +export class DummyEffectGlyph extends EffectGlyph { + private _s: string; + + public constructor(x: number, y: number, s: string) { + super(x, y); + this._s = s; + } + + public doLayout(): void { + this.width = 20 * this.scale; + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + let res: RenderingResources = this.renderer.resources; + canvas.strokeRect(cx + this.x, cy + this.y, this.width, 20 * this.scale); + canvas.font = res.tablatureFont; + canvas.fillText(this._s, cx + this.x, cy + this.y); + } +} diff --git a/src/rendering/effects/DynamicsEffectInfo.ts b/src/rendering/effects/DynamicsEffectInfo.ts new file mode 100644 index 000000000..7526f114b --- /dev/null +++ b/src/rendering/effects/DynamicsEffectInfo.ts @@ -0,0 +1,63 @@ +import { Beat } from '@src/model/Beat'; +import { BarRendererBase } from '@src/rendering/BarRendererBase'; +import { EffectBarGlyphSizing } from '@src/rendering/EffectBarGlyphSizing'; +import { DynamicsGlyph } from '@src/rendering/glyphs/DynamicsGlyph'; +import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph'; +import { EffectBarRendererInfo } from '@src/rendering/EffectBarRendererInfo'; +import { Settings } from '@src/Settings'; +import { NotationElement } from '@src/NotationSettings'; + +export class DynamicsEffectInfo extends EffectBarRendererInfo { + public get notationElement(): NotationElement { + return NotationElement.EffectDynamics + } + + public get hideOnMultiTrack(): boolean { + return false; + } + + public get canShareBand(): boolean { + return false; + } + + public get sizingMode(): EffectBarGlyphSizing { + return EffectBarGlyphSizing.SingleOnBeat; + } + + public shouldCreateGlyph(settings: Settings, beat: Beat): boolean { + return this.internalShouldCreateGlyph(beat); + } + + private internalShouldCreateGlyph(beat: Beat): boolean { + if (beat.voice.bar.staff.track.score.stylesheet.hideDynamics || beat.isEmpty || beat.voice.isEmpty) { + return false; + } + let show: boolean = + (beat.voice.index === 0 && beat.index === 0 && beat.voice.bar.index === 0) || + (!!beat.previousBeat && beat.dynamics !== beat.previousBeat.dynamics); + // ensure we do not show duplicate dynamics + if (show && beat.voice.index > 0) { + for (let voice of beat.voice.bar.voices) { + if (voice.index < beat.voice.index) { + let beatAtSamePos = voice.getBeatAtDisplayStart(beat.displayStart); + if ( + beatAtSamePos && + beat.dynamics === beatAtSamePos.dynamics && + this.internalShouldCreateGlyph(beatAtSamePos) + ) { + show = false; + } + } + } + } + return show; + } + + public createNewGlyph(renderer: BarRendererBase, beat: Beat): EffectGlyph { + return new DynamicsGlyph(0, 0, beat.dynamics); + } + + public canExpand(from: Beat, to: Beat): boolean { + return true; + } +} diff --git a/src/rendering/effects/FadeInEffectInfo.ts b/src/rendering/effects/FadeInEffectInfo.ts new file mode 100644 index 000000000..62ee6c697 --- /dev/null +++ b/src/rendering/effects/FadeInEffectInfo.ts @@ -0,0 +1,38 @@ +import { Beat } from '@src/model/Beat'; +import { BarRendererBase } from '@src/rendering/BarRendererBase'; +import { EffectBarGlyphSizing } from '@src/rendering/EffectBarGlyphSizing'; +import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph'; +import { FadeInGlyph } from '@src/rendering/glyphs/FadeInGlyph'; +import { EffectBarRendererInfo } from '@src/rendering/EffectBarRendererInfo'; +import { Settings } from '@src/Settings'; +import { NotationElement } from '@src/NotationSettings'; + +export class FadeInEffectInfo extends EffectBarRendererInfo { + public get notationElement(): NotationElement { + return NotationElement.EffectFadeIn; + } + + public get hideOnMultiTrack(): boolean { + return false; + } + + public get canShareBand(): boolean { + return true; + } + + public get sizingMode(): EffectBarGlyphSizing { + return EffectBarGlyphSizing.SingleOnBeat; + } + + public shouldCreateGlyph(settings: Settings, beat: Beat): boolean { + return beat.fadeIn; + } + + public createNewGlyph(renderer: BarRendererBase, beat: Beat): EffectGlyph { + return new FadeInGlyph(); + } + + public canExpand(from: Beat, to: Beat): boolean { + return true; + } +} diff --git a/src/rendering/effects/FermataEffectInfo.ts b/src/rendering/effects/FermataEffectInfo.ts new file mode 100644 index 000000000..b8aaec4ac --- /dev/null +++ b/src/rendering/effects/FermataEffectInfo.ts @@ -0,0 +1,38 @@ +import { Beat } from '@src/model/Beat'; +import { BarRendererBase } from '@src/rendering/BarRendererBase'; +import { EffectBarGlyphSizing } from '@src/rendering/EffectBarGlyphSizing'; +import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph'; +import { FermataGlyph } from '@src/rendering/glyphs/FermataGlyph'; +import { EffectBarRendererInfo } from '@src/rendering/EffectBarRendererInfo'; +import { Settings } from '@src/Settings'; +import { NotationElement } from '@src/NotationSettings'; + +export class FermataEffectInfo extends EffectBarRendererInfo { + public get notationElement(): NotationElement { + return NotationElement.EffectFermata; + } + + public get hideOnMultiTrack(): boolean { + return false; + } + + public get canShareBand(): boolean { + return false; + } + + public get sizingMode(): EffectBarGlyphSizing { + return EffectBarGlyphSizing.SingleOnBeat; + } + + public shouldCreateGlyph(settings: Settings, beat: Beat): boolean { + return !!beat.fermata; + } + + public createNewGlyph(renderer: BarRendererBase, beat: Beat): EffectGlyph { + return new FermataGlyph(0, 0, beat.fermata!.type); + } + + public canExpand(from: Beat, to: Beat): boolean { + return true; + } +} diff --git a/src/rendering/effects/FingeringEffectInfo.ts b/src/rendering/effects/FingeringEffectInfo.ts new file mode 100644 index 000000000..f8c589efd --- /dev/null +++ b/src/rendering/effects/FingeringEffectInfo.ts @@ -0,0 +1,63 @@ +import { Beat } from '@src/model/Beat'; +import { Fingers } from '@src/model/Fingers'; +import { Note } from '@src/model/Note'; +import { FingeringMode, NotationElement } from '@src/NotationSettings'; +import { TextAlign } from '@src/platform/ICanvas'; +import { BarRendererBase } from '@src/rendering/BarRendererBase'; +import { EffectBarGlyphSizing } from '@src/rendering/EffectBarGlyphSizing'; +import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph'; +import { TextGlyph } from '@src/rendering/glyphs/TextGlyph'; +import { EffectBarRendererInfo } from '@src/rendering/EffectBarRendererInfo'; +import { Settings } from '@src/Settings'; +import { ModelUtils } from '@src/model/ModelUtils'; + +export class FingeringEffectInfo extends EffectBarRendererInfo { + public get notationElement(): NotationElement { + return NotationElement.EffectFingering; + } + + public get hideOnMultiTrack(): boolean { + return false; + } + + public get canShareBand(): boolean { + return true; + } + + public get sizingMode(): EffectBarGlyphSizing { + return EffectBarGlyphSizing.SingleOnBeat; + } + + public shouldCreateGlyph(settings: Settings, beat: Beat): boolean { + if ( + beat.voice.index !== 0 || + beat.isRest || + (settings.notation.fingeringMode !== FingeringMode.SingleNoteEffectBand && + settings.notation.fingeringMode !== FingeringMode.SingleNoteEffectBandForcePiano) + ) { + return false; + } + if (beat.notes.length !== 1) { + return false; + } + return beat.notes[0].isFingering; + } + + public createNewGlyph(renderer: BarRendererBase, beat: Beat): EffectGlyph { + let finger: Fingers = Fingers.Unknown; + let isLeft: boolean = false; + let note: Note = beat.notes[0]; + if (note.leftHandFinger !== Fingers.Unknown) { + finger = note.leftHandFinger; + isLeft = true; + } else if (note.rightHandFinger !== Fingers.Unknown) { + finger = note.rightHandFinger; + } + let s: string = ModelUtils.fingerToString(renderer.settings, beat, finger, isLeft) ?? ""; + return new TextGlyph(0, 0, s, renderer.resources.fingeringFont, TextAlign.Left); + } + + public canExpand(from: Beat, to: Beat): boolean { + return true; + } +} diff --git a/src/rendering/effects/HarmonicsEffectInfo.ts b/src/rendering/effects/HarmonicsEffectInfo.ts new file mode 100644 index 000000000..052deb8eb --- /dev/null +++ b/src/rendering/effects/HarmonicsEffectInfo.ts @@ -0,0 +1,88 @@ +import { Beat } from '@src/model/Beat'; +import { HarmonicType } from '@src/model/HarmonicType'; +import { Note } from '@src/model/Note'; +import { BarRendererBase } from '@src/rendering/BarRendererBase'; +import { EffectBarGlyphSizing } from '@src/rendering/EffectBarGlyphSizing'; +import { NoteEffectInfoBase } from '@src/rendering/effects/NoteEffectInfoBase'; +import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph'; +import { LineRangedGlyph } from '@src/rendering/glyphs/LineRangedGlyph'; +import { NotationElement } from '@src/NotationSettings'; + +export class HarmonicsEffectInfo extends NoteEffectInfoBase { + private _harmonicType: HarmonicType; + private _beat: Beat | null = null; + private _effectId: string; + + public get effectId(): string { + return this._effectId; + } + + public get notationElement(): NotationElement { + return NotationElement.EffectHarmonics; + } + + public constructor(harmonicType: HarmonicType) { + super(); + this._harmonicType = HarmonicType.None; + this._harmonicType = harmonicType; + switch (harmonicType) { + case HarmonicType.None: + this._effectId = 'harmonics-none'; + break; + case HarmonicType.Natural: + this._effectId = 'harmonics-natural'; + break; + case HarmonicType.Artificial: + this._effectId = 'harmonics-artificial'; + break; + case HarmonicType.Pinch: + this._effectId = 'harmonics-pinch'; + break; + case HarmonicType.Tap: + this._effectId = 'harmonics-tap'; + break; + case HarmonicType.Semi: + this._effectId = 'harmonics-semi'; + break; + case HarmonicType.Feedback: + this._effectId = 'harmonics-feedback'; + break; + } + } + + protected shouldCreateGlyphForNote(note: Note): boolean { + if (!note.isHarmonic || note.harmonicType !== this._harmonicType) { + return false; + } + if (note.beat !== this._beat) { + this._beat = note.beat; + } + return true; + } + + public get sizingMode(): EffectBarGlyphSizing { + return EffectBarGlyphSizing.GroupedOnBeat; + } + + public createNewGlyph(renderer: BarRendererBase, beat: Beat): EffectGlyph { + return new LineRangedGlyph(HarmonicsEffectInfo.harmonicToString(this._harmonicType)); + } + + public static harmonicToString(type: HarmonicType): string { + switch (type) { + case HarmonicType.Natural: + return 'N.H.'; + case HarmonicType.Artificial: + return 'A.H.'; + case HarmonicType.Pinch: + return 'P.H.'; + case HarmonicType.Tap: + return 'T.H.'; + case HarmonicType.Semi: + return 'S.H.'; + case HarmonicType.Feedback: + return 'Fdbk.'; + } + return ''; + } +} diff --git a/src/rendering/effects/LeftHandTapEffectInfo.ts b/src/rendering/effects/LeftHandTapEffectInfo.ts new file mode 100644 index 000000000..f79643bd1 --- /dev/null +++ b/src/rendering/effects/LeftHandTapEffectInfo.ts @@ -0,0 +1,26 @@ +import { Beat } from '@src/model/Beat'; +import { BarRendererBase } from '@src/rendering/BarRendererBase'; +import { EffectBarGlyphSizing } from '@src/rendering/EffectBarGlyphSizing'; +import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph'; +import { NotationElement } from '@src/NotationSettings'; +import { NoteEffectInfoBase } from './NoteEffectInfoBase'; +import { Note } from '@src/model/Note'; +import { LeftHandTapGlyph } from '@src/rendering/glyphs/LeftHandTapGlyph'; + +export class LeftHandTapEffectInfo extends NoteEffectInfoBase { + public get notationElement(): NotationElement { + return NotationElement.EffectTap; + } + + public get sizingMode(): EffectBarGlyphSizing { + return EffectBarGlyphSizing.SingleOnBeat; + } + + protected shouldCreateGlyphForNote(note: Note): boolean { + return note.isLeftHandTapped; + } + + public createNewGlyph(renderer: BarRendererBase, beat: Beat): EffectGlyph { + return new LeftHandTapGlyph(); + } +} diff --git a/src/rendering/effects/LetRingEffectInfo.ts b/src/rendering/effects/LetRingEffectInfo.ts new file mode 100644 index 000000000..822be0589 --- /dev/null +++ b/src/rendering/effects/LetRingEffectInfo.ts @@ -0,0 +1,38 @@ +import { Beat } from '@src/model/Beat'; +import { BarRendererBase } from '@src/rendering/BarRendererBase'; +import { EffectBarGlyphSizing } from '@src/rendering/EffectBarGlyphSizing'; +import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph'; +import { LineRangedGlyph } from '@src/rendering/glyphs/LineRangedGlyph'; +import { EffectBarRendererInfo } from '@src/rendering/EffectBarRendererInfo'; +import { Settings } from '@src/Settings'; +import { NotationElement } from '@src/NotationSettings'; + +export class LetRingEffectInfo extends EffectBarRendererInfo { + public get notationElement(): NotationElement { + return NotationElement.EffectLetRing; + } + + public get canShareBand(): boolean { + return false; + } + + public get hideOnMultiTrack(): boolean { + return false; + } + + public shouldCreateGlyph(settings: Settings, beat: Beat): boolean { + return beat.isLetRing; + } + + public get sizingMode(): EffectBarGlyphSizing { + return EffectBarGlyphSizing.GroupedOnBeat; + } + + public createNewGlyph(renderer: BarRendererBase, beat: Beat): EffectGlyph { + return new LineRangedGlyph('LetRing'); + } + + public canExpand(from: Beat, to: Beat): boolean { + return true; + } +} diff --git a/src/rendering/effects/LyricsEffectInfo.ts b/src/rendering/effects/LyricsEffectInfo.ts new file mode 100644 index 000000000..2b9446e92 --- /dev/null +++ b/src/rendering/effects/LyricsEffectInfo.ts @@ -0,0 +1,39 @@ +import { Beat } from '@src/model/Beat'; +import { TextAlign } from '@src/platform/ICanvas'; +import { BarRendererBase } from '@src/rendering/BarRendererBase'; +import { EffectBarGlyphSizing } from '@src/rendering/EffectBarGlyphSizing'; +import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph'; +import { LyricsGlyph } from '@src/rendering/glyphs/LyricsGlyph'; +import { EffectBarRendererInfo } from '@src/rendering/EffectBarRendererInfo'; +import { Settings } from '@src/Settings'; +import { NotationElement } from '@src/NotationSettings'; + +export class LyricsEffectInfo extends EffectBarRendererInfo { + public get notationElement(): NotationElement { + return NotationElement.EffectLyrics; + } + + public get hideOnMultiTrack(): boolean { + return false; + } + + public get canShareBand(): boolean { + return false; + } + + public get sizingMode(): EffectBarGlyphSizing { + return EffectBarGlyphSizing.SingleOnBeat; + } + + public shouldCreateGlyph(settings: Settings, beat: Beat): boolean { + return !!beat.lyrics; + } + + public createNewGlyph(renderer: BarRendererBase, beat: Beat): EffectGlyph { + return new LyricsGlyph(0, 0, beat.lyrics!, renderer.resources.effectFont, TextAlign.Center); + } + + public canExpand(from: Beat, to: Beat): boolean { + return true; + } +} diff --git a/src/rendering/effects/MarkerEffectInfo.ts b/src/rendering/effects/MarkerEffectInfo.ts new file mode 100644 index 000000000..44c1d20bd --- /dev/null +++ b/src/rendering/effects/MarkerEffectInfo.ts @@ -0,0 +1,52 @@ +import { Beat } from '@src/model/Beat'; +import { TextAlign } from '@src/platform/ICanvas'; +import { BarRendererBase } from '@src/rendering/BarRendererBase'; +import { EffectBarGlyphSizing } from '@src/rendering/EffectBarGlyphSizing'; +import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph'; +import { TextGlyph } from '@src/rendering/glyphs/TextGlyph'; +import { EffectBarRendererInfo } from '@src/rendering/EffectBarRendererInfo'; +import { Settings } from '@src/Settings'; +import { NotationElement } from '@src/NotationSettings'; + +export class MarkerEffectInfo extends EffectBarRendererInfo { + public get notationElement(): NotationElement { + return NotationElement.EffectMarker; + } + + public get hideOnMultiTrack(): boolean { + return true; + } + + public get canShareBand(): boolean { + return true; + } + + public get sizingMode(): EffectBarGlyphSizing { + return EffectBarGlyphSizing.SinglePreBeat; + } + + public shouldCreateGlyph(settings: Settings, beat: Beat): boolean { + return ( + beat.voice.bar.staff.index === 0 && + beat.voice.index === 0 && + beat.index === 0 && + beat.voice.bar.masterBar.isSectionStart + ); + } + + public createNewGlyph(renderer: BarRendererBase, beat: Beat): EffectGlyph { + return new TextGlyph( + 0, + 0, + !beat.voice.bar.masterBar.section!.marker + ? beat.voice.bar.masterBar.section!.text + : '[' + beat.voice.bar.masterBar.section!.marker + '] ' + beat.voice.bar.masterBar.section!.text, + renderer.resources.markerFont, + TextAlign.Left + ); + } + + public canExpand(from: Beat, to: Beat): boolean { + return true; + } +} diff --git a/src/rendering/effects/NoteEffectInfoBase.ts b/src/rendering/effects/NoteEffectInfoBase.ts new file mode 100644 index 000000000..14cfb7fa1 --- /dev/null +++ b/src/rendering/effects/NoteEffectInfoBase.ts @@ -0,0 +1,33 @@ +import { Beat } from '@src/model/Beat'; +import { Note } from '@src/model/Note'; +import { EffectBarRendererInfo } from '@src/rendering/EffectBarRendererInfo'; +import { Settings } from '@src/Settings'; + +export abstract class NoteEffectInfoBase extends EffectBarRendererInfo { + protected lastCreateInfo: Note[] | null = null; + + public shouldCreateGlyph(settings: Settings, beat: Beat): boolean { + this.lastCreateInfo = []; + for (let i: number = 0, j: number = beat.notes.length; i < j; i++) { + let n: Note = beat.notes[i]; + if (this.shouldCreateGlyphForNote(n)) { + this.lastCreateInfo.push(n); + } + } + return this.lastCreateInfo.length > 0; + } + + protected abstract shouldCreateGlyphForNote(note: Note): boolean; + + public get hideOnMultiTrack(): boolean { + return false; + } + + public get canShareBand(): boolean { + return true; + } + + public canExpand(from: Beat, to: Beat): boolean { + return true; + } +} diff --git a/src/rendering/effects/OttaviaEffectInfo.ts b/src/rendering/effects/OttaviaEffectInfo.ts new file mode 100644 index 000000000..34da9c1b8 --- /dev/null +++ b/src/rendering/effects/OttaviaEffectInfo.ts @@ -0,0 +1,60 @@ +import { Beat } from '@src/model/Beat'; +import { Ottavia } from '@src/model/Ottavia'; +import { BarRendererBase } from '@src/rendering/BarRendererBase'; +import { EffectBarGlyphSizing } from '@src/rendering/EffectBarGlyphSizing'; +import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph'; +import { OttavaGlyph } from '@src/rendering/glyphs/OttavaGlyph'; +import { EffectBarRendererInfo } from '@src/rendering/EffectBarRendererInfo'; +import { Settings } from '@src/Settings'; +import { NotationElement } from '@src/NotationSettings'; + +export class OttaviaEffectInfo extends EffectBarRendererInfo { + private _aboveStaff: boolean; + + public get effectId(): string { + return 'ottavia-' + (this._aboveStaff ? 'above' : 'below'); + } + + public get notationElement(): NotationElement { + return NotationElement.EffectOttavia; + } + + public get hideOnMultiTrack(): boolean { + return false; + } + + public get canShareBand(): boolean { + return true; + } + + public get sizingMode(): EffectBarGlyphSizing { + return EffectBarGlyphSizing.GroupedOnBeat; + } + + public constructor(aboveStaff: boolean) { + super(); + this._aboveStaff = aboveStaff; + } + + public shouldCreateGlyph(settings: Settings, beat: Beat): boolean { + switch (beat.ottava) { + case Ottavia._15ma: + return this._aboveStaff; + case Ottavia._8va: + return this._aboveStaff; + case Ottavia._8vb: + return !this._aboveStaff; + case Ottavia._15mb: + return !this._aboveStaff; + } + return false; + } + + public createNewGlyph(renderer: BarRendererBase, beat: Beat): EffectGlyph { + return new OttavaGlyph(beat.ottava, this._aboveStaff); + } + + public canExpand(from: Beat, to: Beat): boolean { + return from.ottava === to.ottava; + } +} diff --git a/src/rendering/effects/PalmMuteEffectInfo.ts b/src/rendering/effects/PalmMuteEffectInfo.ts new file mode 100644 index 000000000..6c9103095 --- /dev/null +++ b/src/rendering/effects/PalmMuteEffectInfo.ts @@ -0,0 +1,30 @@ +import { Beat } from '@src/model/Beat'; +import { Note } from '@src/model/Note'; +import { BarRendererBase } from '@src/rendering/BarRendererBase'; +import { EffectBarGlyphSizing } from '@src/rendering/EffectBarGlyphSizing'; +import { NoteEffectInfoBase } from '@src/rendering/effects/NoteEffectInfoBase'; +import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph'; +import { LineRangedGlyph } from '@src/rendering/glyphs/LineRangedGlyph'; +import { NotationElement } from '@src/NotationSettings'; + +export class PalmMuteEffectInfo extends NoteEffectInfoBase { + public get notationElement(): NotationElement { + return NotationElement.EffectPalmMute; + } + + protected shouldCreateGlyphForNote(note: Note): boolean { + return note.isPalmMute; + } + + public get sizingMode(): EffectBarGlyphSizing { + return EffectBarGlyphSizing.GroupedOnBeat; + } + + public createNewGlyph(renderer: BarRendererBase, beat: Beat): EffectGlyph { + return new LineRangedGlyph('P.M.'); + } + + public constructor() { + super(); + } +} diff --git a/src/rendering/effects/PickSlideEffectInfo.ts b/src/rendering/effects/PickSlideEffectInfo.ts new file mode 100644 index 000000000..f6337dd5b --- /dev/null +++ b/src/rendering/effects/PickSlideEffectInfo.ts @@ -0,0 +1,31 @@ +import { Beat } from '@src/model/Beat'; +import { Note } from '@src/model/Note'; +import { SlideOutType } from '@src/model/SlideOutType'; +import { BarRendererBase } from '@src/rendering/BarRendererBase'; +import { EffectBarGlyphSizing } from '@src/rendering/EffectBarGlyphSizing'; +import { NoteEffectInfoBase } from '@src/rendering/effects/NoteEffectInfoBase'; +import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph'; +import { LineRangedGlyph } from '@src/rendering/glyphs/LineRangedGlyph'; +import { NotationElement } from '@src/NotationSettings'; + +export class PickSlideEffectInfo extends NoteEffectInfoBase { + public get notationElement(): NotationElement { + return NotationElement.EffectPickSlide; + } + + protected shouldCreateGlyphForNote(note: Note): boolean { + return note.slideOutType === SlideOutType.PickSlideDown || note.slideOutType === SlideOutType.PickSlideUp; + } + + public get sizingMode(): EffectBarGlyphSizing { + return EffectBarGlyphSizing.GroupedOnBeat; + } + + public createNewGlyph(renderer: BarRendererBase, beat: Beat): EffectGlyph { + return new LineRangedGlyph('P.S.'); + } + + public constructor() { + super(); + } +} diff --git a/src/rendering/effects/PickStrokeEffectInfo.ts b/src/rendering/effects/PickStrokeEffectInfo.ts new file mode 100644 index 000000000..288c27cdd --- /dev/null +++ b/src/rendering/effects/PickStrokeEffectInfo.ts @@ -0,0 +1,39 @@ +import { Beat } from '@src/model/Beat'; +import { PickStroke } from '@src/model/PickStroke'; +import { BarRendererBase } from '@src/rendering/BarRendererBase'; +import { EffectBarGlyphSizing } from '@src/rendering/EffectBarGlyphSizing'; +import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph'; +import { PickStrokeGlyph } from '@src/rendering/glyphs/PickStrokeGlyph'; +import { EffectBarRendererInfo } from '@src/rendering/EffectBarRendererInfo'; +import { Settings } from '@src/Settings'; +import { NotationElement } from '@src/NotationSettings'; + +export class PickStrokeEffectInfo extends EffectBarRendererInfo { + public get notationElement(): NotationElement { + return NotationElement.EffectPickStroke; + } + + public get hideOnMultiTrack(): boolean { + return false; + } + + public get canShareBand(): boolean { + return true; + } + + public get sizingMode(): EffectBarGlyphSizing { + return EffectBarGlyphSizing.SingleOnBeat; + } + + public shouldCreateGlyph(settings: Settings, beat: Beat): boolean { + return beat.pickStroke !== PickStroke.None; + } + + public createNewGlyph(renderer: BarRendererBase, beat: Beat): EffectGlyph { + return new PickStrokeGlyph(0, 0, beat.pickStroke); + } + + public canExpand(from: Beat, to: Beat): boolean { + return true; + } +} diff --git a/src/rendering/effects/SlightBeatVibratoEffectInfo.ts b/src/rendering/effects/SlightBeatVibratoEffectInfo.ts new file mode 100644 index 000000000..a4028c44d --- /dev/null +++ b/src/rendering/effects/SlightBeatVibratoEffectInfo.ts @@ -0,0 +1,39 @@ +import { Beat } from '@src/model/Beat'; +import { VibratoType } from '@src/model/VibratoType'; +import { BarRendererBase } from '@src/rendering/BarRendererBase'; +import { EffectBarGlyphSizing } from '@src/rendering/EffectBarGlyphSizing'; +import { BeatVibratoGlyph } from '@src/rendering/glyphs/BeatVibratoGlyph'; +import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph'; +import { EffectBarRendererInfo } from '@src/rendering/EffectBarRendererInfo'; +import { Settings } from '@src/Settings'; +import { NotationElement } from '@src/NotationSettings'; + +export class SlightBeatVibratoEffectInfo extends EffectBarRendererInfo { + public get notationElement(): NotationElement { + return NotationElement.EffectSlightBeatVibrato; + } + + public get hideOnMultiTrack(): boolean { + return false; + } + + public get canShareBand(): boolean { + return true; + } + + public get sizingMode(): EffectBarGlyphSizing { + return EffectBarGlyphSizing.GroupedOnBeatToEnd; + } + + public shouldCreateGlyph(settings: Settings, beat: Beat): boolean { + return beat.vibrato === VibratoType.Slight; + } + + public createNewGlyph(renderer: BarRendererBase, beat: Beat): EffectGlyph { + return new BeatVibratoGlyph(VibratoType.Slight); + } + + public canExpand(from: Beat, to: Beat): boolean { + return true; + } +} diff --git a/src/rendering/effects/SlightNoteVibratoEffectInfo.ts b/src/rendering/effects/SlightNoteVibratoEffectInfo.ts new file mode 100644 index 000000000..31a93d05e --- /dev/null +++ b/src/rendering/effects/SlightNoteVibratoEffectInfo.ts @@ -0,0 +1,34 @@ +import { Beat } from '@src/model/Beat'; +import { Note } from '@src/model/Note'; +import { VibratoType } from '@src/model/VibratoType'; +import { BarRendererBase } from '@src/rendering/BarRendererBase'; +import { EffectBarGlyphSizing } from '@src/rendering/EffectBarGlyphSizing'; +import { NoteEffectInfoBase } from '@src/rendering/effects/NoteEffectInfoBase'; +import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph'; +import { NoteVibratoGlyph } from '@src/rendering/glyphs/NoteVibratoGlyph'; +import { NotationElement } from '@src/NotationSettings'; + +export class SlightNoteVibratoEffectInfo extends NoteEffectInfoBase { + public get notationElement(): NotationElement { + return NotationElement.EffectSlightNoteVibrato; + } + + protected shouldCreateGlyphForNote(note: Note): boolean { + return ( + note.vibrato === VibratoType.Slight || + (note.isTieDestination && note.tieOrigin!.vibrato === VibratoType.Slight) + ); + } + + public get sizingMode(): EffectBarGlyphSizing { + return EffectBarGlyphSizing.GroupedOnBeatToEnd; + } + + public createNewGlyph(renderer: BarRendererBase, beat: Beat): EffectGlyph { + return new NoteVibratoGlyph(0, 0, VibratoType.Slight, 1.2); + } + + public constructor() { + super(); + } +} diff --git a/src/rendering/effects/TapEffectInfo.ts b/src/rendering/effects/TapEffectInfo.ts new file mode 100644 index 000000000..cd78237af --- /dev/null +++ b/src/rendering/effects/TapEffectInfo.ts @@ -0,0 +1,47 @@ +import { Beat } from '@src/model/Beat'; +import { TextAlign } from '@src/platform/ICanvas'; +import { BarRendererBase } from '@src/rendering/BarRendererBase'; +import { EffectBarGlyphSizing } from '@src/rendering/EffectBarGlyphSizing'; +import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph'; +import { TextGlyph } from '@src/rendering/glyphs/TextGlyph'; +import { EffectBarRendererInfo } from '@src/rendering/EffectBarRendererInfo'; +import { RenderingResources } from '@src/RenderingResources'; +import { Settings } from '@src/Settings'; +import { NotationElement } from '@src/NotationSettings'; + +export class TapEffectInfo extends EffectBarRendererInfo { + public get notationElement(): NotationElement { + return NotationElement.EffectTap; + } + + public get hideOnMultiTrack(): boolean { + return false; + } + + public get canShareBand(): boolean { + return true; + } + + public get sizingMode(): EffectBarGlyphSizing { + return EffectBarGlyphSizing.SingleOnBeat; + } + + public shouldCreateGlyph(settings: Settings, beat: Beat): boolean { + return beat.slap || beat.pop || beat.tap; + } + + public createNewGlyph(renderer: BarRendererBase, beat: Beat): EffectGlyph { + let res: RenderingResources = renderer.resources; + if (beat.slap) { + return new TextGlyph(0, 0, 'S', res.effectFont, TextAlign.Left); + } + if (beat.pop) { + return new TextGlyph(0, 0, 'P', res.effectFont, TextAlign.Left); + } + return new TextGlyph(0, 0, 'T', res.effectFont, TextAlign.Left); + } + + public canExpand(from: Beat, to: Beat): boolean { + return true; + } +} diff --git a/src/rendering/effects/TempoEffectInfo.ts b/src/rendering/effects/TempoEffectInfo.ts new file mode 100644 index 000000000..cdf18de83 --- /dev/null +++ b/src/rendering/effects/TempoEffectInfo.ts @@ -0,0 +1,49 @@ +import { Beat } from '@src/model/Beat'; +import { BarRendererBase } from '@src/rendering/BarRendererBase'; +import { EffectBarGlyphSizing } from '@src/rendering/EffectBarGlyphSizing'; +import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph'; +import { TempoGlyph } from '@src/rendering/glyphs/TempoGlyph'; +import { EffectBarRendererInfo } from '@src/rendering/EffectBarRendererInfo'; +import { Settings } from '@src/Settings'; +import { NotationElement } from '@src/NotationSettings'; + +export class TempoEffectInfo extends EffectBarRendererInfo { + public get notationElement(): NotationElement { + return NotationElement.EffectTempo; + } + + public get hideOnMultiTrack(): boolean { + return true; + } + + public get canShareBand(): boolean { + return false; + } + + public get sizingMode(): EffectBarGlyphSizing { + return EffectBarGlyphSizing.SinglePreBeat; + } + + public shouldCreateGlyph(settings: Settings, beat: Beat): boolean { + return ( + beat.voice.bar.staff.index === 0 && + beat.voice.index === 0 && + beat.index === 0 && + (!!beat.voice.bar.masterBar.tempoAutomation || beat.voice.bar.index === 0) + ); + } + + public createNewGlyph(renderer: BarRendererBase, beat: Beat): EffectGlyph { + let tempo: number = 0; + if (beat.voice.bar.masterBar.tempoAutomation) { + tempo = beat.voice.bar.masterBar.tempoAutomation.value; + } else { + tempo = beat.voice.bar.staff.track.score.tempo; + } + return new TempoGlyph(0, 0, tempo); + } + + public canExpand(from: Beat, to: Beat): boolean { + return true; + } +} diff --git a/src/rendering/effects/TextEffectInfo.ts b/src/rendering/effects/TextEffectInfo.ts new file mode 100644 index 000000000..62a133dea --- /dev/null +++ b/src/rendering/effects/TextEffectInfo.ts @@ -0,0 +1,39 @@ +import { Beat } from '@src/model/Beat'; +import { TextAlign } from '@src/platform/ICanvas'; +import { BarRendererBase } from '@src/rendering/BarRendererBase'; +import { EffectBarGlyphSizing } from '@src/rendering/EffectBarGlyphSizing'; +import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph'; +import { TextGlyph } from '@src/rendering/glyphs/TextGlyph'; +import { EffectBarRendererInfo } from '@src/rendering/EffectBarRendererInfo'; +import { Settings } from '@src/Settings'; +import { NotationElement } from '@src/NotationSettings'; + +export class TextEffectInfo extends EffectBarRendererInfo { + public get notationElement(): NotationElement { + return NotationElement.EffectText; + } + + public get hideOnMultiTrack(): boolean { + return false; + } + + public get canShareBand(): boolean { + return false; + } + + public get sizingMode(): EffectBarGlyphSizing { + return EffectBarGlyphSizing.SingleOnBeat; + } + + public shouldCreateGlyph(settings: Settings, beat: Beat): boolean { + return !!beat.text; + } + + public createNewGlyph(renderer: BarRendererBase, beat: Beat): EffectGlyph { + return new TextGlyph(0, 0, beat.text!, renderer.resources.effectFont, TextAlign.Left); + } + + public canExpand(from: Beat, to: Beat): boolean { + return true; + } +} diff --git a/src/rendering/effects/TrillEffectInfo.ts b/src/rendering/effects/TrillEffectInfo.ts new file mode 100644 index 000000000..3292425c3 --- /dev/null +++ b/src/rendering/effects/TrillEffectInfo.ts @@ -0,0 +1,26 @@ +import { Beat } from '@src/model/Beat'; +import { Note } from '@src/model/Note'; +import { BarRendererBase } from '@src/rendering/BarRendererBase'; +import { EffectBarGlyphSizing } from '@src/rendering/EffectBarGlyphSizing'; +import { NoteEffectInfoBase } from '@src/rendering/effects/NoteEffectInfoBase'; +import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph'; +import { TrillGlyph } from '@src/rendering/glyphs/TrillGlyph'; +import { NotationElement } from '@src/NotationSettings'; + +export class TrillEffectInfo extends NoteEffectInfoBase { + public get notationElement(): NotationElement { + return NotationElement.EffectTrill; + } + + protected shouldCreateGlyphForNote(note: Note): boolean { + return note.isTrill; + } + + public get sizingMode(): EffectBarGlyphSizing { + return EffectBarGlyphSizing.SingleOnBeat; + } + + public createNewGlyph(renderer: BarRendererBase, beat: Beat): EffectGlyph { + return new TrillGlyph(0, 0); + } +} diff --git a/src/rendering/effects/TripletFeelEffectInfo.ts b/src/rendering/effects/TripletFeelEffectInfo.ts new file mode 100644 index 000000000..0f33bbb0f --- /dev/null +++ b/src/rendering/effects/TripletFeelEffectInfo.ts @@ -0,0 +1,45 @@ +import { Beat } from '@src/model/Beat'; +import { TripletFeel } from '@src/model/TripletFeel'; +import { BarRendererBase } from '@src/rendering/BarRendererBase'; +import { EffectBarGlyphSizing } from '@src/rendering/EffectBarGlyphSizing'; +import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph'; +import { TripletFeelGlyph } from '@src/rendering/glyphs/TripletFeelGlyph'; +import { EffectBarRendererInfo } from '@src/rendering/EffectBarRendererInfo'; +import { Settings } from '@src/Settings'; +import { NotationElement } from '@src/NotationSettings'; + +export class TripletFeelEffectInfo extends EffectBarRendererInfo { + public get notationElement(): NotationElement { + return NotationElement.EffectTripletFeel; + } + + public get hideOnMultiTrack(): boolean { + return true; + } + + public get canShareBand(): boolean { + return false; + } + + public get sizingMode(): EffectBarGlyphSizing { + return EffectBarGlyphSizing.SinglePreBeat; + } + + public shouldCreateGlyph(settings: Settings, beat: Beat): boolean { + return ( + beat.index === 0 && + ((beat.voice.bar.masterBar.index === 0 && + beat.voice.bar.masterBar.tripletFeel !== TripletFeel.NoTripletFeel) || + (beat.voice.bar.masterBar.index > 0 && + beat.voice.bar.masterBar.tripletFeel !== beat.voice.bar.masterBar.previousMasterBar!.tripletFeel)) + ); + } + + public createNewGlyph(renderer: BarRendererBase, beat: Beat): EffectGlyph { + return new TripletFeelGlyph(beat.voice.bar.masterBar.tripletFeel); + } + + public canExpand(from: Beat, to: Beat): boolean { + return true; + } +} diff --git a/src/rendering/effects/WhammyBarEffectInfo.ts b/src/rendering/effects/WhammyBarEffectInfo.ts new file mode 100644 index 000000000..a9eaf9fa5 --- /dev/null +++ b/src/rendering/effects/WhammyBarEffectInfo.ts @@ -0,0 +1,38 @@ +import { Beat } from '@src/model/Beat'; +import { BarRendererBase } from '@src/rendering/BarRendererBase'; +import { EffectBarGlyphSizing } from '@src/rendering/EffectBarGlyphSizing'; +import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph'; +import { LineRangedGlyph } from '@src/rendering/glyphs/LineRangedGlyph'; +import { EffectBarRendererInfo } from '@src/rendering/EffectBarRendererInfo'; +import { Settings } from '@src/Settings'; +import { NotationElement } from '@src/NotationSettings'; + +export class WhammyBarEffectInfo extends EffectBarRendererInfo { + public get notationElement(): NotationElement{ + return NotationElement.EffectWhammyBar; + } + + public get hideOnMultiTrack(): boolean { + return false; + } + + public get canShareBand(): boolean { + return false; + } + + public get sizingMode(): EffectBarGlyphSizing { + return EffectBarGlyphSizing.GroupedOnBeat; + } + + public shouldCreateGlyph(settings: Settings, beat: Beat): boolean { + return beat.hasWhammyBar; + } + + public createNewGlyph(renderer: BarRendererBase, beat: Beat): EffectGlyph { + return new LineRangedGlyph('w/bar'); + } + + public canExpand(from: Beat, to: Beat): boolean { + return true; + } +} diff --git a/src/rendering/effects/WideBeatVibratoEffectInfo.ts b/src/rendering/effects/WideBeatVibratoEffectInfo.ts new file mode 100644 index 000000000..6325b7dea --- /dev/null +++ b/src/rendering/effects/WideBeatVibratoEffectInfo.ts @@ -0,0 +1,39 @@ +import { Beat } from '@src/model/Beat'; +import { VibratoType } from '@src/model/VibratoType'; +import { BarRendererBase } from '@src/rendering/BarRendererBase'; +import { EffectBarGlyphSizing } from '@src/rendering/EffectBarGlyphSizing'; +import { BeatVibratoGlyph } from '@src/rendering/glyphs/BeatVibratoGlyph'; +import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph'; +import { EffectBarRendererInfo } from '@src/rendering/EffectBarRendererInfo'; +import { Settings } from '@src/Settings'; +import { NotationElement } from '@src/NotationSettings'; + +export class WideBeatVibratoEffectInfo extends EffectBarRendererInfo { + public get notationElement(): NotationElement { + return NotationElement.EffectWideBeatVibrato; + } + + public get hideOnMultiTrack(): boolean { + return false; + } + + public get canShareBand(): boolean { + return true; + } + + public get sizingMode(): EffectBarGlyphSizing { + return EffectBarGlyphSizing.GroupedOnBeatToEnd; + } + + public shouldCreateGlyph(settings: Settings, beat: Beat): boolean { + return beat.vibrato === VibratoType.Wide; + } + + public createNewGlyph(renderer: BarRendererBase, beat: Beat): EffectGlyph { + return new BeatVibratoGlyph(VibratoType.Wide); + } + + public canExpand(from: Beat, to: Beat): boolean { + return true; + } +} diff --git a/src/rendering/effects/WideNoteVibratoEffectInfo.ts b/src/rendering/effects/WideNoteVibratoEffectInfo.ts new file mode 100644 index 000000000..cb68a90ce --- /dev/null +++ b/src/rendering/effects/WideNoteVibratoEffectInfo.ts @@ -0,0 +1,29 @@ +import { Beat } from '@src/model/Beat'; +import { Note } from '@src/model/Note'; +import { VibratoType } from '@src/model/VibratoType'; +import { BarRendererBase } from '@src/rendering/BarRendererBase'; +import { EffectBarGlyphSizing } from '@src/rendering/EffectBarGlyphSizing'; +import { NoteEffectInfoBase } from '@src/rendering/effects/NoteEffectInfoBase'; +import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph'; +import { NoteVibratoGlyph } from '@src/rendering/glyphs/NoteVibratoGlyph'; +import { NotationElement } from '@src/NotationSettings'; + +export class WideNoteVibratoEffectInfo extends NoteEffectInfoBase { + public get notationElement(): NotationElement { + return NotationElement.EffectWideNoteVibrato; + } + + protected shouldCreateGlyphForNote(note: Note): boolean { + return ( + note.vibrato === VibratoType.Wide || (note.isTieDestination && note.tieOrigin!.vibrato === VibratoType.Wide) + ); + } + + public get sizingMode(): EffectBarGlyphSizing { + return EffectBarGlyphSizing.GroupedOnBeatToEnd; + } + + public createNewGlyph(renderer: BarRendererBase, beat: Beat): EffectGlyph { + return new NoteVibratoGlyph(0, 0, VibratoType.Wide, 1.2); + } +} diff --git a/src/rendering/glyphs/AccentuationGlyph.ts b/src/rendering/glyphs/AccentuationGlyph.ts new file mode 100644 index 000000000..64de8c08c --- /dev/null +++ b/src/rendering/glyphs/AccentuationGlyph.ts @@ -0,0 +1,26 @@ +import { AccentuationType } from '@src/model/AccentuationType'; +import { MusicFontGlyph } from '@src/rendering/glyphs/MusicFontGlyph'; +import { MusicFontSymbol } from '@src/rendering/glyphs/MusicFontSymbol'; + +export class AccentuationGlyph extends MusicFontGlyph { + public constructor(x: number, y: number, accentuation: AccentuationType) { + super(x, y, 1, AccentuationGlyph.getSymbol(accentuation)); + } + + private static getSymbol(accentuation: AccentuationType): MusicFontSymbol { + switch (accentuation) { + case AccentuationType.None: + return MusicFontSymbol.None; + case AccentuationType.Normal: + return MusicFontSymbol.Accentuation; + case AccentuationType.Heavy: + return MusicFontSymbol.HeavyAccentuation; + default: + return MusicFontSymbol.None; + } + } + + public doLayout(): void { + this.width = 9 * this.scale; + } +} diff --git a/src/rendering/glyphs/AccidentalGlyph.ts b/src/rendering/glyphs/AccidentalGlyph.ts new file mode 100644 index 000000000..1a4b6b7c6 --- /dev/null +++ b/src/rendering/glyphs/AccidentalGlyph.ts @@ -0,0 +1,36 @@ +import { AccidentalType } from '@src/model/AccidentalType'; +import { MusicFontGlyph } from '@src/rendering/glyphs/MusicFontGlyph'; +import { MusicFontSymbol } from '@src/rendering/glyphs/MusicFontSymbol'; +import { NoteHeadGlyph } from '@src/rendering/glyphs/NoteHeadGlyph'; + +export class AccidentalGlyph extends MusicFontGlyph { + private _isGrace: boolean; + + 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; + } + + private static getMusicSymbol(accidentalType: AccidentalType): MusicFontSymbol { + switch (accidentalType) { + case AccidentalType.Natural: + return MusicFontSymbol.AccidentalNatural; + case AccidentalType.Sharp: + return MusicFontSymbol.AccidentalSharp; + case AccidentalType.Flat: + return MusicFontSymbol.AccidentalFlat; + case AccidentalType.NaturalQuarterNoteUp: + return MusicFontSymbol.AccidentalQuarterToneNaturalArrowUp; + case AccidentalType.SharpQuarterNoteUp: + return MusicFontSymbol.AccidentalQuarterToneSharpArrowUp; + case AccidentalType.FlatQuarterNoteUp: + return MusicFontSymbol.AccidentalQuarterToneFlatArrowUp; + } + return MusicFontSymbol.None; + } + + public doLayout(): void { + this.width = 8 * (this._isGrace ? NoteHeadGlyph.GraceScale : 1) * this.scale; + } +} diff --git a/src/rendering/glyphs/AccidentalGroupGlyph.ts b/src/rendering/glyphs/AccidentalGroupGlyph.ts new file mode 100644 index 000000000..731adc31d --- /dev/null +++ b/src/rendering/glyphs/AccidentalGroupGlyph.ts @@ -0,0 +1,66 @@ +import { Glyph } from '@src/rendering/glyphs/Glyph'; +import { GlyphGroup } from '@src/rendering/glyphs/GlyphGroup'; + +export class AccidentalGroupGlyph extends GlyphGroup { + private static readonly NonReserved: number = -3000; + + public constructor() { + super(0, 0); + } + + public doLayout(): void { + if (!this.glyphs) { + this.width = 0; + return; + } + // + // Determine Columns for accidentals + // + this.glyphs.sort((a, b) => { + if (a.y < b.y) { + return -1; + } + if (a.y > b.y) { + return 1; + } + return 0; + }); + // defines the reserved y position of the columns + let columns: number[] = []; + columns.push(AccidentalGroupGlyph.NonReserved); + let accidentalSize: number = 21 * this.scale; + for (let i: number = 0, j: number = this.glyphs.length; i < j; i++) { + let g: Glyph = this.glyphs[i]; + 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) { + // move to next column + gColumn++; + // and create the new column if needed + if (gColumn === columns.length) { + columns.push(AccidentalGroupGlyph.NonReserved); + } + } + // temporary save column as X + g.x = gColumn; + columns[gColumn] = g.y + accidentalSize; + } + // + // 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; + } + 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); + } + } +} diff --git a/src/rendering/glyphs/AlternateEndingsGlyph.ts b/src/rendering/glyphs/AlternateEndingsGlyph.ts new file mode 100644 index 000000000..3f4740724 --- /dev/null +++ b/src/rendering/glyphs/AlternateEndingsGlyph.ts @@ -0,0 +1,47 @@ +import { ICanvas, TextBaseline } from '@src/platform/ICanvas'; +import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph'; +import { RenderingResources } from '@src/RenderingResources'; +import { MasterBar } from '@src/model/MasterBar'; + +export class AlternateEndingsGlyph extends EffectGlyph { + private static readonly Padding: number = 3; + private _endings: number[]; + private _endingsString: string = ""; + + public constructor(x: number, y: number, alternateEndings: number) { + super(x, y); + this._endings = []; + for (let i: number = 0; i < MasterBar.MaxAlternateEndings; i++) { + if ((alternateEndings & (0x01 << i)) !== 0) { + this._endings.push(i); + } + } + } + + public doLayout(): void { + super.doLayout(); + this.height = this.renderer.resources.wordsFont.size + (AlternateEndingsGlyph.Padding * this.scale + 2); + let endingsStrings: string = ''; + for (let i: number = 0, j: number = this._endings.length; i < j; i++) { + endingsStrings += this._endings[i] + 1; + endingsStrings += '. '; + } + this._endingsString = endingsStrings; + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + super.paint(cx, cy, canvas); + let baseline: TextBaseline = canvas.textBaseline; + canvas.textBaseline = TextBaseline.Top; + if (this._endings.length > 0) { + let res: RenderingResources = this.renderer.resources; + canvas.font = res.wordsFont; + canvas.moveTo(cx + this.x, cy + this.y + this.height); + canvas.lineTo(cx + this.x, cy + this.y); + canvas.lineTo(cx + this.x + this.width, cy + this.y); + canvas.stroke(); + canvas.fillText(this._endingsString, cx + this.x + AlternateEndingsGlyph.Padding * this.scale, cy + this.y * this.scale); + } + canvas.textBaseline = baseline; + } +} diff --git a/src/rendering/glyphs/BarNumberGlyph.ts b/src/rendering/glyphs/BarNumberGlyph.ts new file mode 100644 index 000000000..f285be205 --- /dev/null +++ b/src/rendering/glyphs/BarNumberGlyph.ts @@ -0,0 +1,31 @@ +import { Color } from '@src/model/Color'; +import { ICanvas } from '@src/platform/ICanvas'; +import { Glyph } from '@src/rendering/glyphs/Glyph'; +import { RenderingResources } from '@src/RenderingResources'; + +export class BarNumberGlyph extends Glyph { + private _number: number = 0; + + public constructor(x: number, y: number, num: number) { + super(x, y); + this._number = num; + } + + public doLayout(): void { + this.renderer.scoreRenderer.canvas!.font = this.renderer.resources.barNumberFont; + this.width = this.renderer.scoreRenderer.canvas!.measureText(this._number.toString()) + 5 * this.scale; + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + if (!this.renderer.staff.isFirstInAccolade) { + return; + } + let res: RenderingResources = this.renderer.resources; + let c: Color = canvas.color; + canvas.color = res.barNumberColor; + canvas.font = res.barNumberFont; + canvas.fillText(this._number.toString(), cx + this.x, cy + this.y); + canvas.color = c; + } + +} diff --git a/src/rendering/glyphs/BarSeperatorGlyph.ts b/src/rendering/glyphs/BarSeperatorGlyph.ts new file mode 100644 index 000000000..ca1eb9698 --- /dev/null +++ b/src/rendering/glyphs/BarSeperatorGlyph.ts @@ -0,0 +1,49 @@ +import { ICanvas } from '@src/platform/ICanvas'; +import { Glyph } from '@src/rendering/glyphs/Glyph'; + +export class BarSeperatorGlyph extends Glyph { + public constructor(x: number, y: number) { + super(x, y); + } + + public doLayout(): void { + if (this.renderer.isLast) { + this.width = 15 * this.scale; + } else if ( + !this.renderer.nextRenderer || + this.renderer.nextRenderer.staff !== this.renderer.staff || + !this.renderer.nextRenderer.bar.masterBar.isRepeatStart + ) { + this.width = 2 * this.scale; + if (this.renderer.bar.masterBar.isDoubleBar) { + this.width += 2 * this.scale; + } + } else { + this.width = 2 * this.scale; + } + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + let blockWidth: number = 4 * this.scale; + let top: number = cy + this.y + this.renderer.topPadding; + let bottom: number = cy + this.y + this.renderer.height - this.renderer.bottomPadding; + let left: number = cx + this.x; + let h: number = bottom - top; + if (this.renderer.isLast) { + // small bar + canvas.fillRect(left + this.width - blockWidth - blockWidth, top, this.scale, h); + // big bar + canvas.fillRect(left + this.width - blockWidth, top, blockWidth, h); + } else if ( + !this.renderer.nextRenderer || + this.renderer.nextRenderer.staff !== this.renderer.staff || + !this.renderer.nextRenderer.bar.masterBar.isRepeatStart + ) { + // small bar + canvas.fillRect(left + this.width - this.scale, top, this.scale, h); + if (this.renderer.bar.masterBar.isDoubleBar) { + canvas.fillRect(left + this.width - 5 * this.scale, top, this.scale, h); + } + } + } +} diff --git a/src/rendering/glyphs/BeamGlyph.ts b/src/rendering/glyphs/BeamGlyph.ts new file mode 100644 index 000000000..cfde9247d --- /dev/null +++ b/src/rendering/glyphs/BeamGlyph.ts @@ -0,0 +1,55 @@ +import { Duration } from '@src/model/Duration'; +import { MusicFontGlyph } from '@src/rendering/glyphs/MusicFontGlyph'; +import { MusicFontSymbol } from '@src/rendering/glyphs/MusicFontSymbol'; +import { BeamDirection } from '@src/rendering/utils/BeamDirection'; +import { NoteHeadGlyph } from '@src/rendering/glyphs/NoteHeadGlyph'; + +export class BeamGlyph extends MusicFontGlyph { + public constructor(x: number, y: number, duration: Duration, direction: BeamDirection, isGrace: boolean) { + super(x, y, isGrace ? NoteHeadGlyph.GraceScale : 1, BeamGlyph.getSymbol(duration, direction, isGrace)); + } + + public doLayout(): void { + this.width = 0; + } + + private static getSymbol(duration: Duration, direction: BeamDirection, isGrace: boolean): MusicFontSymbol { + if (isGrace) { + duration = Duration.Eighth; + } + if (direction === BeamDirection.Up) { + switch (duration) { + case Duration.Eighth: + return MusicFontSymbol.FooterUpEighth; + case Duration.Sixteenth: + return MusicFontSymbol.FooterUpSixteenth; + case Duration.ThirtySecond: + return MusicFontSymbol.FooterUpThirtySecond; + case Duration.SixtyFourth: + return MusicFontSymbol.FooterUpSixtyFourth; + case Duration.OneHundredTwentyEighth: + return MusicFontSymbol.FooterUpOneHundredTwentyEighth; + case Duration.TwoHundredFiftySixth: + return MusicFontSymbol.FooterUpTwoHundredFiftySixth; + default: + return MusicFontSymbol.FooterUpEighth; + } + } + switch (duration) { + case Duration.Eighth: + return MusicFontSymbol.FooterDownEighth; + case Duration.Sixteenth: + return MusicFontSymbol.FooterDownSixteenth; + case Duration.ThirtySecond: + return MusicFontSymbol.FooterDownThirtySecond; + case Duration.SixtyFourth: + return MusicFontSymbol.FooterDownSixtyFourth; + case Duration.OneHundredTwentyEighth: + return MusicFontSymbol.FooterDownOneHundredTwentyEighth; + case Duration.TwoHundredFiftySixth: + return MusicFontSymbol.FooterDownOneHundredTwentyEighth; + default: + return MusicFontSymbol.FooterDownEighth; + } + } +} diff --git a/src/rendering/glyphs/BeatContainerGlyph.ts b/src/rendering/glyphs/BeatContainerGlyph.ts new file mode 100644 index 000000000..f53e35ab7 --- /dev/null +++ b/src/rendering/glyphs/BeatContainerGlyph.ts @@ -0,0 +1,199 @@ +import { Beat } from '@src/model/Beat'; +import { Duration } from '@src/model/Duration'; +import { Note } from '@src/model/Note'; +import { ICanvas } from '@src/platform/ICanvas'; +import { BeatGlyphBase } from '@src/rendering/glyphs/BeatGlyphBase'; +import { BeatOnNoteGlyphBase } from '@src/rendering/glyphs/BeatOnNoteGlyphBase'; +import { Glyph } from '@src/rendering/glyphs/Glyph'; +import { VoiceContainerGlyph } from '@src/rendering/glyphs/VoiceContainerGlyph'; +import { BarLayoutingInfo } from '@src/rendering/staves/BarLayoutingInfo'; +import { BarBounds } from '../utils/BarBounds'; +import { BeatBounds } from '../utils/BeatBounds'; +import { Bounds } from '../utils/Bounds'; + +export class BeatContainerGlyph extends Glyph { + public voiceContainer: VoiceContainerGlyph; + public beat: Beat; + public preNotes!: BeatGlyphBase; + public onNotes!: BeatOnNoteGlyphBase; + public ties: Glyph[] = []; + public minWidth: number = 0; + + public get onTimeX(): number { + return this.onNotes.x + this.onNotes.centerX; + } + + public constructor(beat: Beat, voiceContainer: VoiceContainerGlyph) { + super(0, 0); + this.beat = beat; + this.ties = []; + this.voiceContainer = voiceContainer; + } + + public registerLayoutingInfo(layoutings: BarLayoutingInfo): void { + let preBeatStretch: number = this.onTimeX; + let postBeatStretch: number = 0; + for (let tie of this.ties) { + if (tie.width > postBeatStretch) { + postBeatStretch = tie.width; + } + } + postBeatStretch += this.onNotes.x + (this.onNotes.width - this.onNotes.centerX); + layoutings.addBeatSpring(this.beat, preBeatStretch, postBeatStretch); + // store sizes for special renderers like the EffectBarRenderer + layoutings.setPreBeatSize(this.beat, this.preNotes.width); + layoutings.setOnBeatSize(this.beat, this.onNotes.width); + layoutings.setBeatCenterX(this.beat, this.onNotes.centerX); + } + + public applyLayoutingInfo(info: BarLayoutingInfo): void { + let offset: number = info.getBeatCenterX(this.beat) - this.onNotes.centerX; + this.preNotes.x = offset; + this.preNotes.width = info.getPreBeatSize(this.beat); + this.onNotes.width = info.getOnBeatSize(this.beat); + this.onNotes.x = this.preNotes.x + this.preNotes.width; + this.onNotes.updateBeamingHelper(); + } + + public doLayout(): void { + this.preNotes.x = 0; + this.preNotes.renderer = this.renderer; + this.preNotes.container = this; + this.preNotes.doLayout(); + this.onNotes.x = this.preNotes.x + this.preNotes.width; + this.onNotes.renderer = this.renderer; + this.onNotes.container = this; + this.onNotes.doLayout(); + let i: number = this.beat.notes.length - 1; + while (i >= 0) { + this.createTies(this.beat.notes[i--]); + } + this.updateWidth(); + } + + protected updateWidth(): void { + this.minWidth = this.preNotes.width + this.onNotes.width; + if (!this.beat.isRest) { + if (this.onNotes.beamingHelper.beats.length === 1) { + // make space for footer + if (this.beat.duration >= Duration.Eighth) { + this.minWidth += 20 * this.scale; + } + } else { + // ensure some space for small notes + switch (this.beat.duration) { + case Duration.OneHundredTwentyEighth: + case Duration.TwoHundredFiftySixth: + this.minWidth += 10 * this.scale; + break; + } + } + } + let tieWidth: number = 0; + for (let tie of this.ties) { + if (tie.width > tieWidth) { + tieWidth = tie.width; + } + } + this.minWidth += tieWidth; + this.width = this.minWidth; + } + + public scaleToWidth(beatWidth: number): void { + for (let tie of this.ties) { + tie.doLayout(); + } + this.onNotes.updateBeamingHelper(); + this.width = beatWidth; + } + + protected createTies(n: Note): void { + // no default ties + } + + public static getGroupId(beat: Beat): string { + return 'b' + beat.id; + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + if (this.beat.voice.isEmpty) { + return; + } + let isEmptyGlyph: boolean = this.preNotes.isEmpty && this.onNotes.isEmpty && this.ties.length === 0; + if (isEmptyGlyph) { + return; + } + canvas.beginGroup(BeatContainerGlyph.getGroupId(this.beat)); + // var c = canvas.Color; + // var ta = canvas.TextAlign; + // canvas.Color = new Color(255, 0, 0); + // canvas.TextAlign = TextAlign.Left; + // canvas.FillText(Beat.DisplayStart.ToString(), cx + X, cy + Y - 10); + // canvas.Color = c; + // canvas.TextAlign = ta; + // canvas.Color = Color.Random(); + // canvas.FillRect(cx + X, cy + Y, Width, Renderer.Height); + // var oldColor = canvas.Color; + // canvas.Color = new Color((byte)Platform.Platform.Random(255), (byte)Platform.Platform.Random(255), (byte)Platform.Platform.Random(255), 100); + // canvas.FillRect(cx + X, cy + Y, Width, Renderer.Height); + // canvas.Color = oldColor; + // canvas.Color = new Color(200, 0, 0, 100); + // canvas.StrokeRect(cx + X, cy + Y + 15 * Beat.Voice.Index, Width, 10); + // canvas.Font = new Font("Arial", 10); + // canvas.Color = new Color(0, 0, 0); + // canvas.FillText(Beat.Voice.Index + ":" + Beat.Index, cx + X, cy + Y + 15 * Beat.Voice.Index); + // if (Beat.Voice.Index===0) + // { + // canvas.Color = new Color(200, 0, 0, 100); + // canvas.StrokeRect(cx + X, cy + Y + PreNotes.Y + 30, Width, 10); + // } + this.preNotes.paint(cx + this.x, cy + this.y, canvas); + // if (Beat.Voice.Index===0) + // { + // canvas.Color = new Color(200, 0, 0, 100); + // canvas.StrokeRect(cx + X + PreNotes.X, cy + Y + PreNotes.Y, PreNotes.Width, 10); + // } + this.onNotes.paint(cx + this.x, cy + this.y, canvas); + // if (Beat.Voice.Index===0) + // { + // canvas.Color = new Color(0, 200, 0, 100); + // canvas.StrokeRect(cx + X + OnNotes.X, cy + Y + OnNotes.Y + 10, OnNotes.Width, 10); + // } + // paint the ties relative to the whole staff, + // reason: we have possibly multiple staves involved and need to calculate the correct positions. + let staffX: number = cx - this.voiceContainer.x - this.renderer.x; + let staffY: number = cy - this.voiceContainer.y - this.renderer.y; + for (let i: number = 0, j: number = this.ties.length; i < j; i++) { + let t: Glyph = this.ties[i]; + t.renderer = this.renderer; + t.paint(staffX, staffY, canvas); + } + canvas.endGroup(); + } + + public buildBoundingsLookup(barBounds:BarBounds, cx:number, cy:number, isEmptyBar:boolean) { + let beatBoundings: BeatBounds = new BeatBounds(); + beatBoundings.beat = this.beat; + beatBoundings.visualBounds = new Bounds(); + beatBoundings.visualBounds.x = cx + this.x + this.onNotes.x; + beatBoundings.visualBounds.y = barBounds.visualBounds.y; + beatBoundings.visualBounds.w = this.onNotes.width; + beatBoundings.visualBounds.h = barBounds.visualBounds.h; + + beatBoundings.realBounds = new Bounds(); + beatBoundings.realBounds.x = cx + this.x; + beatBoundings.realBounds.y = barBounds.realBounds.y; + beatBoundings.realBounds.w = this.width; + beatBoundings.realBounds.h = barBounds.realBounds.h; + + if (isEmptyBar) { + beatBoundings.visualBounds.x = cx + this.x; + beatBoundings.realBounds.x = beatBoundings.visualBounds.x; + } + barBounds.addBeat(beatBoundings); + + if(this.renderer.settings.core.includeNoteBounds) { + this.onNotes.buildBoundingsLookup(beatBoundings, cx + this.x, cy + this.y); + } + } +} diff --git a/src/rendering/glyphs/BeatGlyphBase.ts b/src/rendering/glyphs/BeatGlyphBase.ts new file mode 100644 index 000000000..333a93940 --- /dev/null +++ b/src/rendering/glyphs/BeatGlyphBase.ts @@ -0,0 +1,33 @@ +import { Note } from '@src/model/Note'; +import { BeatContainerGlyph } from '@src/rendering/glyphs/BeatContainerGlyph'; +import { Glyph } from '@src/rendering/glyphs/Glyph'; +import { GlyphGroup } from '@src/rendering/glyphs/GlyphGroup'; + +export class BeatGlyphBase extends GlyphGroup { + public container!: BeatContainerGlyph; + + public constructor() { + super(0, 0); + } + + public doLayout(): void { + // left to right layout + let w: number = 0; + if (this.glyphs) { + for (let i: number = 0, j: number = this.glyphs.length; i < j; i++) { + let g: Glyph = this.glyphs[i]; + g.x = w; + g.renderer = this.renderer; + g.doLayout(); + w += g.width; + } + } + this.width = w; + } + + protected noteLoop(action: (note: Note) => void): void { + for (let i: number = this.container.beat.notes.length - 1; i >= 0; i--) { + action(this.container.beat.notes[i]); + } + } +} diff --git a/src/rendering/glyphs/BeatOnNoteGlyphBase.ts b/src/rendering/glyphs/BeatOnNoteGlyphBase.ts new file mode 100644 index 000000000..9751ff06e --- /dev/null +++ b/src/rendering/glyphs/BeatOnNoteGlyphBase.ts @@ -0,0 +1,25 @@ +import { BeatGlyphBase } from '@src/rendering/glyphs/BeatGlyphBase'; +import { BeamingHelper } from '@src/rendering/utils/BeamingHelper'; +import { NoteXPosition, NoteYPosition } from '../BarRendererBase'; +import { Note } from '@src/model/Note'; +import { BeatBounds } from '../utils/BeatBounds'; + +export class BeatOnNoteGlyphBase extends BeatGlyphBase { + public beamingHelper!: BeamingHelper; + public centerX: number = 0; + + public updateBeamingHelper(): void { + // + } + + public buildBoundingsLookup(beatBounds:BeatBounds, cx:number, cy:number) { + // implemented in subclasses + } + + public getNoteX(note: Note, requestedPosition: NoteXPosition): number { + return 0; + } + public getNoteY(note: Note, requestedPosition: NoteYPosition): number { + return 0; + } +} diff --git a/src/rendering/glyphs/BeatVibratoGlyph.ts b/src/rendering/glyphs/BeatVibratoGlyph.ts new file mode 100644 index 000000000..2bf97313c --- /dev/null +++ b/src/rendering/glyphs/BeatVibratoGlyph.ts @@ -0,0 +1,41 @@ +import { VibratoType } from '@src/model/VibratoType'; +import { ICanvas } from '@src/platform/ICanvas'; +import { BeatXPosition } from '@src/rendering/BeatXPosition'; +import { GroupedEffectGlyph } from '@src/rendering/glyphs/GroupedEffectGlyph'; + +export class BeatVibratoGlyph extends GroupedEffectGlyph { + private _type: VibratoType; + private _stepSize: number = 0; + + public constructor(type: VibratoType) { + super(BeatXPosition.EndBeat); + this._type = type; + } + + public doLayout(): void { + super.doLayout(); + switch (this._type) { + case VibratoType.Slight: + this._stepSize = 12 * this.scale; + break; + case VibratoType.Wide: + this._stepSize = 23 * this.scale; + break; + } + this.height = 18 * this.scale; + } + + protected paintGrouped(cx: number, cy: number, endX: number, canvas: ICanvas): void { + let startX: number = cx + this.x; + let width: number = endX - startX; + let loops: number = Math.max(1, width / this._stepSize); + canvas.beginPath(); + canvas.moveTo(startX, cy + this.y); + for (let i: number = 0; i < loops; i++) { + canvas.lineTo(startX + this._stepSize / 2, cy + this.y + this.height); + canvas.lineTo(startX + this._stepSize, cy + this.y); + startX += this._stepSize; + } + canvas.stroke(); + } +} diff --git a/src/rendering/glyphs/BendNoteHeadGroupGlyph.ts b/src/rendering/glyphs/BendNoteHeadGroupGlyph.ts new file mode 100644 index 000000000..78f7c2b8b --- /dev/null +++ b/src/rendering/glyphs/BendNoteHeadGroupGlyph.ts @@ -0,0 +1,119 @@ +import { AccidentalType } from '@src/model/AccidentalType'; +import { Beat } from '@src/model/Beat'; +import { Duration } from '@src/model/Duration'; +import { ICanvas } from '@src/platform/ICanvas'; +import { AccidentalGlyph } from '@src/rendering/glyphs/AccidentalGlyph'; +import { AccidentalGroupGlyph } from '@src/rendering/glyphs/AccidentalGroupGlyph'; +import { GhostNoteContainerGlyph } from '@src/rendering/glyphs/GhostNoteContainerGlyph'; +import { Glyph } from '@src/rendering/glyphs/Glyph'; +import { NoteHeadGlyph } from '@src/rendering/glyphs/NoteHeadGlyph'; +import { ScoreNoteChordGlyphBase } from '@src/rendering/glyphs/ScoreNoteChordGlyphBase'; +import { ScoreBarRenderer } from '@src/rendering/ScoreBarRenderer'; +import { BeamDirection } from '@src/rendering/utils/BeamDirection'; + +export class BendNoteHeadGroupGlyph extends ScoreNoteChordGlyphBase { + private static readonly ElementPadding: number = 2; + + private _beat: Beat; + private _showParenthesis: boolean = false; + private _noteValueLookup: Map = new Map(); + private _accidentals: AccidentalGroupGlyph = new AccidentalGroupGlyph(); + private _preNoteParenthesis: GhostNoteContainerGlyph | null = null; + private _postNoteParenthesis: GhostNoteContainerGlyph | null = null; + public isEmpty: boolean = true; + + public get direction(): BeamDirection { + return BeamDirection.Up; + } + + public noteHeadOffset: number = 0; + + public constructor(beat: Beat, showParenthesis: boolean = false) { + super(); + this._beat = beat; + this._showParenthesis = showParenthesis; + if (showParenthesis) { + this._preNoteParenthesis = new GhostNoteContainerGlyph(true); + this._postNoteParenthesis = new GhostNoteContainerGlyph(false); + } + } + + public containsNoteValue(noteValue: number): boolean { + return this._noteValueLookup.has(noteValue); + } + + public getNoteValueY(noteValue: number): number { + if (this._noteValueLookup.has(noteValue)) { + return ( + this.y + + this._noteValueLookup.get(noteValue)!.y + ); + } + return 0; + } + + public addGlyph(noteValue: number, quarterBend: boolean = false): void { + let sr: ScoreBarRenderer = this.renderer as ScoreBarRenderer; + let noteHeadGlyph: NoteHeadGlyph = new NoteHeadGlyph(0, 0, Duration.Quarter, true); + let accidental: AccidentalType = sr.accidentalHelper.applyAccidentalForValue( + this._beat, + noteValue, + quarterBend + ); + let line: number = sr.accidentalHelper.getNoteLineForValue(noteValue, false); + noteHeadGlyph.y = sr.getScoreY(line, 0); + if (this._showParenthesis) { + this._preNoteParenthesis!.renderer = this.renderer; + this._postNoteParenthesis!.renderer = this.renderer; + this._preNoteParenthesis!.addParenthesisOnLine(line, true); + this._postNoteParenthesis!.addParenthesisOnLine(line, true); + } + if (accidental !== AccidentalType.None) { + let g = new AccidentalGlyph(0, noteHeadGlyph.y, accidental, true); + g.renderer = this.renderer; + this._accidentals.addGlyph(g); + } + this._noteValueLookup.set(noteValue, noteHeadGlyph); + this.add(noteHeadGlyph, line); + this.isEmpty = false; + } + + public doLayout(): void { + let x: number = 0; + if (this._showParenthesis) { + this._preNoteParenthesis!.x = x; + this._preNoteParenthesis!.renderer = this.renderer; + this._preNoteParenthesis!.doLayout(); + x += this._preNoteParenthesis!.width + BendNoteHeadGroupGlyph.ElementPadding * this.scale; + } + if (!this._accidentals.isEmpty) { + this._accidentals.x = x; + this._accidentals.renderer = this.renderer; + this._accidentals.doLayout(); + x += this._accidentals.width + BendNoteHeadGroupGlyph.ElementPadding * this.scale; + } + this.noteStartX = x; + super.doLayout(); + this.noteHeadOffset = this.noteStartX + (this.width - this.noteStartX) / 2; + if (this._showParenthesis) { + this._postNoteParenthesis!.x = this.width + BendNoteHeadGroupGlyph.ElementPadding * this.scale; + this._postNoteParenthesis!.renderer = this.renderer; + this._postNoteParenthesis!.doLayout(); + this.width += this._postNoteParenthesis!.width + BendNoteHeadGroupGlyph.ElementPadding * this.scale; + } + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + // canvas.Color = Color.Random(); + // canvas.FillRect(cx + X, cy + Y, Width, 10); + // canvas.Color = Renderer.Resources.MainGlyphColor; + if (!this._accidentals.isEmpty) { + this._accidentals.paint(cx + this.x, cy + this.y, canvas); + } + if (this._showParenthesis) { + this._preNoteParenthesis!.paint(cx + this.x, cy + this.y, canvas); + this._postNoteParenthesis!.paint(cx + this.x, cy + this.y, canvas); + } + super.paint(cx, cy, canvas); + } +} diff --git a/src/rendering/glyphs/ChineseCymbalGlyph.ts b/src/rendering/glyphs/ChineseCymbalGlyph.ts new file mode 100644 index 000000000..07017d6cb --- /dev/null +++ b/src/rendering/glyphs/ChineseCymbalGlyph.ts @@ -0,0 +1,17 @@ +import { MusicFontGlyph } from '@src/rendering/glyphs/MusicFontGlyph'; +import { MusicFontSymbol } from '@src/rendering/glyphs/MusicFontSymbol'; +import { NoteHeadGlyph } from '@src/rendering/glyphs/NoteHeadGlyph'; + +export class ChineseCymbalGlyph extends MusicFontGlyph { + private _isGrace: boolean; + + public constructor(x: number, y: number, isGrace: boolean) { + super(x, y, isGrace ? NoteHeadGlyph.GraceScale : 1, MusicFontSymbol.NoteHarmonic); + this._isGrace = isGrace; + } + + public doLayout(): void { + this.width = 9 * (this._isGrace ? NoteHeadGlyph.GraceScale : 1) * this.scale; + this.height = NoteHeadGlyph.NoteHeadHeight * this.scale; + } +} diff --git a/src/rendering/glyphs/ChordDiagramContainerGlyph.ts b/src/rendering/glyphs/ChordDiagramContainerGlyph.ts new file mode 100644 index 000000000..30f0cf760 --- /dev/null +++ b/src/rendering/glyphs/ChordDiagramContainerGlyph.ts @@ -0,0 +1,64 @@ +import { Chord } from '@src/model/Chord'; +import { ICanvas } from '@src/platform/ICanvas'; +import { ChordDiagramGlyph } from '@src/rendering/glyphs/ChordDiagramGlyph'; +import { ChordDiagramRowGlyph } from '@src/rendering/glyphs/ChordDiagramRowGlyph'; +import { GlyphGroup } from '@src/rendering/glyphs/GlyphGroup'; + +export class ChordDiagramContainerGlyph extends GlyphGroup { + private static readonly Padding: number = 3; + private _rows: ChordDiagramRowGlyph[] = []; + public height: number = 0; + + public constructor(x: number, y: number) { + super(x, y); + this.height = 0.0; + this.glyphs = []; + } + + public addChord(chord: Chord): void { + if (chord.strings.length > 0) { + let chordDiagram: ChordDiagramGlyph = new ChordDiagramGlyph(0, 0, chord); + chordDiagram.renderer = this.renderer; + chordDiagram.doLayout(); + this.glyphs!.push(chordDiagram); + } + } + + public doLayout(): void { + let x: number = 0; + let y: number = 0; + let padding: number = 2 * ChordDiagramContainerGlyph.Padding * this.scale; + this._rows = []; + let row: ChordDiagramRowGlyph = new ChordDiagramRowGlyph(x, y); + row.width = this.width; + for (let g of this.glyphs!) { + if (x + g.width < this.width) { + row.addChord(g as ChordDiagramGlyph); + x += g.width; + } else { + if (!row.isEmpty) { + row.doLayout(); + this._rows.push(row); + y += row.height + padding; + } + x = 0; + row = new ChordDiagramRowGlyph(x, y); + row.width = this.width; + row.addChord(g as ChordDiagramGlyph); + x += g.width; + } + } + if (!row.isEmpty) { + row.doLayout(); + this._rows.push(row); + y += row.height + padding; + } + this.height = y + padding; + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + for (let row of this._rows) { + row.paint(cx + this.x, cy + this.y + ChordDiagramContainerGlyph.Padding * this.scale, canvas); + } + } +} diff --git a/src/rendering/glyphs/ChordDiagramGlyph.ts b/src/rendering/glyphs/ChordDiagramGlyph.ts new file mode 100644 index 000000000..fd0ab400f --- /dev/null +++ b/src/rendering/glyphs/ChordDiagramGlyph.ts @@ -0,0 +1,133 @@ +import { Chord } from '@src/model/Chord'; +import { ICanvas, TextAlign, TextBaseline } from '@src/platform/ICanvas'; +import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph'; +import { MusicFontSymbol } from '@src/rendering/glyphs/MusicFontSymbol'; +import { RenderingResources } from '@src/RenderingResources'; + +export class ChordDiagramGlyph extends EffectGlyph { + private static readonly Padding: number = 5; + private static readonly Frets: number = 5; + private static readonly CircleRadius: number = 2.5; + private static readonly StringSpacing: number = 10; + private static readonly FretSpacing: number = 12; + + private _chord: Chord; + private _textRow: number = 0; + private _fretRow: number = 0; + private _firstFretSpacing: number = 0; + + public constructor(x: number, y: number, chord: Chord) { + super(x, y); + this._chord = chord; + } + + public doLayout(): void { + super.doLayout(); + let res: RenderingResources = this.renderer.resources; + this._textRow = res.effectFont.size * 1.5; + this._fretRow = res.effectFont.size * 1.5; + if (this._chord.firstFret > 1) { + this._firstFretSpacing = ChordDiagramGlyph.FretSpacing * this.scale; + } else { + this._firstFretSpacing = 0; + } + this.height = + this._textRow + + this._fretRow + + (ChordDiagramGlyph.Frets - 1) * ChordDiagramGlyph.FretSpacing * this.scale + + 2 * ChordDiagramGlyph.Padding; + this.width = + this._firstFretSpacing + + (this._chord.staff.tuning.length - 1) * ChordDiagramGlyph.StringSpacing * this.scale + + 2 * ChordDiagramGlyph.Padding; + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + cx += this.x + ChordDiagramGlyph.Padding * this.scale + this._firstFretSpacing; + cy += this.y; + let w: number = this.width - 2 * ChordDiagramGlyph.Padding * this.scale + this.scale - this._firstFretSpacing; + let stringSpacing: number = ChordDiagramGlyph.StringSpacing * this.scale; + let fretSpacing: number = ChordDiagramGlyph.FretSpacing * this.scale; + let res: RenderingResources = this.renderer.resources; + let circleRadius: number = ChordDiagramGlyph.CircleRadius * this.scale; + + let align: TextAlign = canvas.textAlign; + let baseline: TextBaseline = canvas.textBaseline; + canvas.font = res.effectFont; + canvas.textAlign = TextAlign.Center; + canvas.textBaseline = TextBaseline.Top; + if (this._chord.showName) { + canvas.fillText(this._chord.name, cx + this.width / 2, cy + res.effectFont.size / 2); + } + + cy += this._textRow; + cx += stringSpacing / 2; + canvas.font = res.fretboardNumberFont; + canvas.textBaseline = TextBaseline.Middle; + for (let i: number = 0; i < this._chord.staff.tuning.length; i++) { + let x: number = cx + i * stringSpacing; + let y: number = cy + this._fretRow / 2; + let fret: number = this._chord.strings[this._chord.staff.tuning.length - i - 1]; + if (fret < 0) { + canvas.fillMusicFontSymbol(x, y, this.scale, MusicFontSymbol.FretboardX, true); + } else if (fret === 0) { + canvas.fillMusicFontSymbol(x, y, this.scale, MusicFontSymbol.FretboardO, true); + } else { + fret -= this._chord.firstFret - 1; + canvas.fillText(fret.toString(), x, y); + } + } + + cy += this._fretRow; + for (let i: number = 0; i < this._chord.staff.tuning.length; i++) { + let x: number = cx + i * stringSpacing; + canvas.fillRect(x, cy, 1, fretSpacing * ChordDiagramGlyph.Frets + this.scale); + } + + if (this._chord.firstFret > 1) { + canvas.textAlign = TextAlign.Left; + canvas.fillText(this._chord.firstFret.toString(), cx - this._firstFretSpacing, cy + fretSpacing / 2); + } + + canvas.fillRect(cx, cy - this.scale, w, 2 * this.scale); + for (let i: number = 0; i <= ChordDiagramGlyph.Frets; i++) { + let y: number = cy + i * fretSpacing; + canvas.fillRect(cx, y, w, this.scale); + } + + let barreLookup = new Map(); + for (let barreFret of this._chord.barreFrets) { + let strings: number[] = [-1, -1]; + barreLookup.set(barreFret - this._chord.firstFret, strings); + } + + for (let guitarString: number = 0; guitarString < this._chord.strings.length; guitarString++) { + let fret: number = this._chord.strings[guitarString]; + if (fret > 0) { + fret -= this._chord.firstFret; + if (barreLookup.has(fret)) { + let info = barreLookup.get(fret)!; + if (info[0] === -1 || guitarString < info[0]) { + info[0] = guitarString; + } + if (info[1] === -1 || guitarString > info[1]) { + info[1] = guitarString; + } + } + let y: number = cy + fret * fretSpacing + fretSpacing / 2 + 0.5; + let x: number = cx + (this._chord.strings.length - guitarString - 1) * stringSpacing; + canvas.fillCircle(x, y, circleRadius); + } + } + + barreLookup.forEach((strings, fret) => { + let y: number = cy + fret * fretSpacing + fretSpacing / 2 + this.scale; + let xLeft: number = cx + (this._chord.strings.length - strings[1] - 1) * stringSpacing; + let xRight: number = cx + (this._chord.strings.length - strings[0] - 1) * stringSpacing; + canvas.fillRect(xLeft, y - circleRadius, xRight - xLeft, circleRadius * 2); + }); + + canvas.textAlign = align; + canvas.textBaseline = baseline; + } +} diff --git a/src/rendering/glyphs/ChordDiagramRowGlyph.ts b/src/rendering/glyphs/ChordDiagramRowGlyph.ts new file mode 100644 index 000000000..6453e413b --- /dev/null +++ b/src/rendering/glyphs/ChordDiagramRowGlyph.ts @@ -0,0 +1,28 @@ +import { ChordDiagramGlyph } from '@src/rendering/glyphs/ChordDiagramGlyph'; +import { GlyphGroup } from '@src/rendering/glyphs/GlyphGroup'; + +export class ChordDiagramRowGlyph extends GlyphGroup { + private _glyphWidth: number = 0; + public height: number = 0; + + public constructor(x: number, y: number) { + super(x, y); + this.glyphs = []; + } + + public doLayout(): void { + let x: number = (this.width - this._glyphWidth) / 2; + for (let glyph of this.glyphs!) { + glyph.x = x; + x += glyph.width; + } + } + + public addChord(chord: ChordDiagramGlyph): void { + this.glyphs!.push(chord); + this._glyphWidth += chord.width; + if (chord.height > this.height) { + this.height = chord.height; + } + } +} diff --git a/src/rendering/glyphs/CircleGlyph.ts b/src/rendering/glyphs/CircleGlyph.ts new file mode 100644 index 000000000..5b5392655 --- /dev/null +++ b/src/rendering/glyphs/CircleGlyph.ts @@ -0,0 +1,19 @@ +import { ICanvas } from '@src/platform/ICanvas'; +import { Glyph } from '@src/rendering/glyphs/Glyph'; + +export class CircleGlyph extends Glyph { + private _size: number = 0; + + public constructor(x: number, y: number, size: number) { + super(x, y); + this._size = size; + } + + public doLayout(): void { + this.width = this._size + 3 * this.scale; + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + canvas.fillCircle(cx + this.x, cy + this.y, this._size); + } +} diff --git a/src/rendering/glyphs/ClefGlyph.ts b/src/rendering/glyphs/ClefGlyph.ts new file mode 100644 index 000000000..fdabf5055 --- /dev/null +++ b/src/rendering/glyphs/ClefGlyph.ts @@ -0,0 +1,102 @@ +import { Clef } from '@src/model/Clef'; +import { Ottavia } from '@src/model/Ottavia'; +import { ICanvas } from '@src/platform/ICanvas'; +import { Glyph } from '@src/rendering/glyphs/Glyph'; +import { MusicFontGlyph } from '@src/rendering/glyphs/MusicFontGlyph'; +import { MusicFontSymbol } from '@src/rendering/glyphs/MusicFontSymbol'; + +export class ClefGlyph extends MusicFontGlyph { + private _clef: Clef; + private _clefOttava: Ottavia; + + public constructor(x: number, y: number, clef: Clef, clefOttava: Ottavia) { + super(x, y, 1, ClefGlyph.getSymbol(clef)); + this._clef = clef; + this._clefOttava = clefOttava; + } + + public doLayout(): void { + switch (this._clef) { + case Clef.Neutral: + this.width = 15 * this.scale; + break; + case Clef.C3: + case Clef.C4: + case Clef.F4: + case Clef.G2: + this.width = 28 * this.scale; + break; + } + } + + private static getSymbol(clef: Clef): MusicFontSymbol { + switch (clef) { + case Clef.Neutral: + return MusicFontSymbol.ClefNeutral; + case Clef.C3: + return MusicFontSymbol.ClefC; + case Clef.C4: + return MusicFontSymbol.ClefC; + case Clef.F4: + return MusicFontSymbol.ClefF; + case Clef.G2: + return MusicFontSymbol.ClefG; + default: + return MusicFontSymbol.None; + } + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + super.paint(cx, cy, canvas); + let numberGlyph: Glyph; + let top: boolean = false; + switch (this._clefOttava) { + case Ottavia._15ma: + numberGlyph = new MusicFontGlyph(-4 * this.scale, 0, 0.5, MusicFontSymbol.Ottava15); + top = true; + break; + case Ottavia._8va: + numberGlyph = new MusicFontGlyph(-2 * this.scale, 0, 0.5, MusicFontSymbol.Ottava8); + top = true; + break; + case Ottavia._8vb: + numberGlyph = new MusicFontGlyph(-6 * this.scale, 0, 0.5, MusicFontSymbol.Ottava8); + break; + case Ottavia._15mb: + numberGlyph = new MusicFontGlyph(-8 * this.scale, 0, 0.5, MusicFontSymbol.Ottava15); + break; + default: + return; + } + let offsetY: number = 0; + let offsetX: number = 0; + switch (this._clef) { + case Clef.Neutral: + offsetY = top ? -12 : 15; + offsetX = 0; + break; + case Clef.C3: + offsetY = top ? -19 : 27; + offsetX = 0; + break; + case Clef.C4: + offsetY = top ? -19 : 27; + offsetX = 0; + break; + case Clef.F4: + offsetY = top ? -9 : 27; + offsetX = -4; + break; + case Clef.G2: + offsetY = top ? -37 : 30; + offsetX = 0; + break; + default: + return; + } + numberGlyph.renderer = this.renderer; + numberGlyph.doLayout(); + let x: number = this.width / 2; + numberGlyph.paint(cx + this.x + x + offsetX * this.scale, cy + this.y + offsetY * this.scale, canvas); + } +} diff --git a/src/rendering/glyphs/CrescendoGlyph.ts b/src/rendering/glyphs/CrescendoGlyph.ts new file mode 100644 index 000000000..a685c6064 --- /dev/null +++ b/src/rendering/glyphs/CrescendoGlyph.ts @@ -0,0 +1,41 @@ +import { CrescendoType } from '@src/model/CrescendoType'; +import { ICanvas } from '@src/platform/ICanvas'; +import { BeatXPosition } from '@src/rendering/BeatXPosition'; +import { GroupedEffectGlyph } from '@src/rendering/glyphs/GroupedEffectGlyph'; +import { NoteHeadGlyph } from '@src/rendering/glyphs/NoteHeadGlyph'; + +export class CrescendoGlyph extends GroupedEffectGlyph { + private static readonly Padding: number = (NoteHeadGlyph.QuarterNoteHeadWidth / 2) | 0; + private _crescendo: CrescendoType; + + public constructor(x: number, y: number, crescendo: CrescendoType) { + super(BeatXPosition.EndBeat); + this._crescendo = CrescendoType.None; + this._crescendo = crescendo; + this.x = x; + this.y = y; + } + + public doLayout(): void { + super.doLayout(); + this.height = 17 * this.scale; + } + + protected paintGrouped(cx: number, cy: number, endX: number, canvas: ICanvas): void { + let startX: number = cx + this.x; + let height: number = this.height * this.scale; + canvas.beginPath(); + if (this._crescendo === CrescendoType.Crescendo) { + endX -= CrescendoGlyph.Padding * this.scale; + canvas.moveTo(endX, cy + this.y); + canvas.lineTo(startX, cy + this.y + height / 2); + canvas.lineTo(endX, cy + this.y + height); + } else { + endX -= CrescendoGlyph.Padding * this.scale; + canvas.moveTo(startX, cy + this.y); + canvas.lineTo(endX, cy + this.y + height / 2); + canvas.lineTo(startX, cy + this.y + height); + } + canvas.stroke(); + } +} diff --git a/src/rendering/glyphs/DeadNoteHeadGlyph.ts b/src/rendering/glyphs/DeadNoteHeadGlyph.ts new file mode 100644 index 000000000..d511201f6 --- /dev/null +++ b/src/rendering/glyphs/DeadNoteHeadGlyph.ts @@ -0,0 +1,17 @@ +import { MusicFontGlyph } from '@src/rendering/glyphs/MusicFontGlyph'; +import { MusicFontSymbol } from '@src/rendering/glyphs/MusicFontSymbol'; +import { NoteHeadGlyph } from '@src/rendering/glyphs/NoteHeadGlyph'; + +export class DeadNoteHeadGlyph extends MusicFontGlyph { + private _isGrace: boolean; + + public constructor(x: number, y: number, isGrace: boolean) { + super(x, y, isGrace ? NoteHeadGlyph.GraceScale : 1, MusicFontSymbol.NoteDead); + this._isGrace = isGrace; + } + + public doLayout(): void { + this.width = 9 * (this._isGrace ? NoteHeadGlyph.GraceScale : 1) * this.scale; + this.height = NoteHeadGlyph.NoteHeadHeight * this.scale; + } +} diff --git a/src/rendering/glyphs/DiamondNoteHeadGlyph.ts b/src/rendering/glyphs/DiamondNoteHeadGlyph.ts new file mode 100644 index 000000000..b5a97e315 --- /dev/null +++ b/src/rendering/glyphs/DiamondNoteHeadGlyph.ts @@ -0,0 +1,30 @@ +import { Duration } from '@src/model/Duration'; +import { MusicFontGlyph } from '@src/rendering/glyphs/MusicFontGlyph'; +import { MusicFontSymbol } from '@src/rendering/glyphs/MusicFontSymbol'; +import { NoteHeadGlyph } from '@src/rendering/glyphs/NoteHeadGlyph'; + +export class DiamondNoteHeadGlyph extends MusicFontGlyph { + private _isGrace: boolean; + + public constructor(x: number, y: number, duration: Duration, isGrace: boolean) { + super(x, y, isGrace ? NoteHeadGlyph.GraceScale : 1, DiamondNoteHeadGlyph.getSymbol(duration)); + this._isGrace = isGrace; + } + + private static getSymbol(duration: Duration): MusicFontSymbol { + switch (duration) { + case Duration.QuadrupleWhole: + case Duration.DoubleWhole: + case Duration.Whole: + case Duration.Half: + return MusicFontSymbol.NoteHarmonicWhole; + default: + return MusicFontSymbol.NoteHarmonic; + } + } + + public doLayout(): void { + this.width = 9 * (this._isGrace ? NoteHeadGlyph.GraceScale : 1) * this.scale; + this.height = NoteHeadGlyph.NoteHeadHeight * this.scale; + } +} diff --git a/src/rendering/glyphs/DigitGlyph.ts b/src/rendering/glyphs/DigitGlyph.ts new file mode 100644 index 000000000..c63ab23a6 --- /dev/null +++ b/src/rendering/glyphs/DigitGlyph.ts @@ -0,0 +1,64 @@ +import { MusicFontGlyph } from '@src/rendering/glyphs/MusicFontGlyph'; +import { MusicFontSymbol } from '@src/rendering/glyphs/MusicFontSymbol'; + +export class DigitGlyph extends MusicFontGlyph { + private _digit: number = 0; + private _scale: number = 0; + + public constructor(x: number, y: number, digit: number, scale: number) { + super(x, y, scale, DigitGlyph.getSymbol(digit)); + this._digit = digit; + this._scale = scale; + } + + public doLayout(): void { + this.y += 7 * this.scale; + this.width = this.getDigitWidth(this._digit) * this.scale * this._scale; + } + + private getDigitWidth(digit: number): number { + switch (digit) { + case 0: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + return 14; + case 1: + return 10; + default: + return 0; + } + } + + private static getSymbol(digit: number): MusicFontSymbol { + switch (digit) { + case 0: + return MusicFontSymbol.Num0; + case 1: + return MusicFontSymbol.Num1; + case 2: + return MusicFontSymbol.Num2; + case 3: + return MusicFontSymbol.Num3; + case 4: + return MusicFontSymbol.Num4; + case 5: + return MusicFontSymbol.Num5; + case 6: + return MusicFontSymbol.Num6; + case 7: + return MusicFontSymbol.Num7; + case 8: + return MusicFontSymbol.Num8; + case 9: + return MusicFontSymbol.Num9; + default: + return MusicFontSymbol.None; + } + } +} diff --git a/src/rendering/glyphs/DrumSticksGlyph.ts b/src/rendering/glyphs/DrumSticksGlyph.ts new file mode 100644 index 000000000..cce030164 --- /dev/null +++ b/src/rendering/glyphs/DrumSticksGlyph.ts @@ -0,0 +1,17 @@ +import { MusicFontGlyph } from '@src/rendering/glyphs/MusicFontGlyph'; +import { MusicFontSymbol } from '@src/rendering/glyphs/MusicFontSymbol'; +import { NoteHeadGlyph } from '@src/rendering/glyphs/NoteHeadGlyph'; + +export class DrumSticksGlyph extends MusicFontGlyph { + private _isGrace: boolean; + + public constructor(x: number, y: number, isGrace: boolean) { + super(x, y, isGrace ? NoteHeadGlyph.GraceScale : 1, MusicFontSymbol.NoteSideStick); + this._isGrace = isGrace; + } + + public doLayout(): void { + this.width = 9 * (this._isGrace ? NoteHeadGlyph.GraceScale : 1) * this.scale; + this.height = NoteHeadGlyph.NoteHeadHeight * this.scale; + } +} diff --git a/src/rendering/glyphs/DynamicsGlyph.ts b/src/rendering/glyphs/DynamicsGlyph.ts new file mode 100644 index 000000000..0222f3714 --- /dev/null +++ b/src/rendering/glyphs/DynamicsGlyph.ts @@ -0,0 +1,38 @@ +import { DynamicValue } from '@src/model/DynamicValue'; +import { MusicFontGlyph } from '@src/rendering/glyphs/MusicFontGlyph'; +import { MusicFontSymbol } from '@src/rendering/glyphs/MusicFontSymbol'; + +export class DynamicsGlyph extends MusicFontGlyph { + public constructor(x: number, y: number, dynamics: DynamicValue) { + super(x, y, 0.6, DynamicsGlyph.getSymbol(dynamics)); + } + + public doLayout(): void { + super.doLayout(); + this.height = 17 * this.scale; + this.y += this.height / 2; + } + + private static getSymbol(dynamics: DynamicValue): MusicFontSymbol { + switch (dynamics) { + case DynamicValue.PPP: + return MusicFontSymbol.DynamicPPP; + case DynamicValue.PP: + return MusicFontSymbol.DynamicPP; + case DynamicValue.P: + return MusicFontSymbol.DynamicP; + case DynamicValue.MP: + return MusicFontSymbol.DynamicMP; + case DynamicValue.MF: + return MusicFontSymbol.DynamicMF; + case DynamicValue.F: + return MusicFontSymbol.DynamicF; + case DynamicValue.FF: + return MusicFontSymbol.DynamicFF; + case DynamicValue.FFF: + return MusicFontSymbol.DynamicFFF; + default: + return MusicFontSymbol.None; + } + } +} diff --git a/src/rendering/glyphs/EffectGlyph.ts b/src/rendering/glyphs/EffectGlyph.ts new file mode 100644 index 000000000..f37f6a0e7 --- /dev/null +++ b/src/rendering/glyphs/EffectGlyph.ts @@ -0,0 +1,31 @@ +import { Beat } from '@src/model/Beat'; +import { Glyph } from '@src/rendering/glyphs/Glyph'; + +/** + * Effect-Glyphs implementing this public interface get notified + * as they are expanded over multiple beats. + */ +export class EffectGlyph extends Glyph { + /** + * Gets or sets the beat where the glyph belongs to. + */ + public beat!: Beat; + + /** + * Gets or sets the next glyph of the same type in case + * the effect glyph is expanded when using {@link EffectBarGlyphSizing.groupedOnBeat}. + */ + public nextGlyph: EffectGlyph | null = null; + + /** + * Gets or sets the previous glyph of the same type in case + * the effect glyph is expanded when using {@link EffectBarGlyphSizing.groupedOnBeat}. + */ + public previousGlyph: EffectGlyph | null = null; + + public height: number = 0; + + public constructor(x: number = 0, y: number = 0) { + super(x, y); + } +} diff --git a/src/rendering/glyphs/FadeInGlyph.ts b/src/rendering/glyphs/FadeInGlyph.ts new file mode 100644 index 000000000..d9e9254d9 --- /dev/null +++ b/src/rendering/glyphs/FadeInGlyph.ts @@ -0,0 +1,31 @@ +import { ICanvas } from '@src/platform/ICanvas'; +import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph'; + +export class FadeInGlyph extends EffectGlyph { + public doLayout(): void { + super.doLayout(); + this.height = 17 * this.scale; + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + let size: number = 6 * this.scale; + let width: number = Math.max(this.width, 14 * this.scale); + let offset: number = this.height / 2; + canvas.beginPath(); + canvas.moveTo(cx + this.x, cy + this.y + offset); + canvas.quadraticCurveTo( + cx + this.x + width / 2, + cy + this.y + offset, + cx + this.x + width, + cy + this.y + offset - size + ); + canvas.moveTo(cx + this.x, cy + this.y + offset); + canvas.quadraticCurveTo( + cx + this.x + width / 2, + cy + this.y + offset, + cx + this.x + width, + cy + this.y + offset + size + ); + canvas.stroke(); + } +} diff --git a/src/rendering/glyphs/FermataGlyph.ts b/src/rendering/glyphs/FermataGlyph.ts new file mode 100644 index 000000000..3d8d5fcc1 --- /dev/null +++ b/src/rendering/glyphs/FermataGlyph.ts @@ -0,0 +1,32 @@ +import { FermataType } from '@src/model/Fermata'; +import { ICanvas } from '@src/platform/ICanvas'; +import { MusicFontGlyph } from '@src/rendering/glyphs/MusicFontGlyph'; +import { MusicFontSymbol } from '@src/rendering/glyphs/MusicFontSymbol'; + +export class FermataGlyph extends MusicFontGlyph { + public constructor(x: number, y: number, fermata: FermataType) { + super(x, y, 1, FermataGlyph.getSymbol(fermata)); + } + + private static getSymbol(accentuation: FermataType): MusicFontSymbol { + switch (accentuation) { + case FermataType.Short: + return MusicFontSymbol.FermataShort; + case FermataType.Medium: + return MusicFontSymbol.FermataMedium; + case FermataType.Long: + return MusicFontSymbol.FermataLong; + default: + return MusicFontSymbol.None; + } + } + + public doLayout(): void { + this.width = 23 * this.scale; + this.height = 12 * this.scale; + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + super.paint(cx - this.width / 2, cy + this.height, canvas); + } +} diff --git a/src/rendering/glyphs/GhostNoteContainerGlyph.ts b/src/rendering/glyphs/GhostNoteContainerGlyph.ts new file mode 100644 index 000000000..5891ccdb8 --- /dev/null +++ b/src/rendering/glyphs/GhostNoteContainerGlyph.ts @@ -0,0 +1,88 @@ +import { Note } from '@src/model/Note'; +import { ICanvas } from '@src/platform/ICanvas'; +import { GhostParenthesisGlyph } from '@src/rendering/glyphs/GhostParenthesisGlyph'; +import { Glyph } from '@src/rendering/glyphs/Glyph'; +import { ScoreBarRenderer } from '@src/rendering/ScoreBarRenderer'; +import { NotationElement } from '@src/NotationSettings'; + +export class GhostNoteInfo { + public line: number = 0; + public isGhost: boolean; + + public constructor(line: number, isGhost: boolean) { + this.line = line; + this.isGhost = isGhost; + } +} + +export class GhostNoteContainerGlyph extends Glyph { + private _isOpen: boolean; + private _infos: GhostNoteInfo[] = []; + private _glyphs: Glyph[] = []; + public isEmpty: boolean = true; + + public constructor(isOpen: boolean) { + super(0, 0); + this._isOpen = isOpen; + } + + public addParenthesis(n: Note): void { + let sr: ScoreBarRenderer = this.renderer as ScoreBarRenderer; + let line: number = sr.getNoteLine(n); + let hasParenthesis: boolean = + n.isGhost || (this.isTiedBend(n) && sr.settings.notation.isNotationElementVisible(NotationElement.ParenthesisOnTiedBends)); + this.addParenthesisOnLine(line, hasParenthesis); + } + + public addParenthesisOnLine(line: number, hasParenthesis: boolean): void { + let info: GhostNoteInfo = new GhostNoteInfo(line, hasParenthesis); + this._infos.push(info); + if (hasParenthesis) { + this.isEmpty = false; + } + } + + private isTiedBend(note: Note): boolean { + if (note.isTieDestination) { + if (note.tieOrigin!.hasBend) { + return true; + } + return this.isTiedBend(note.tieOrigin!); + } + return false; + } + + public doLayout(): void { + let sr: ScoreBarRenderer = this.renderer as ScoreBarRenderer; + this._infos.sort((a, b) => { + return a.line - b.line; + }); + let previousGlyph: GhostParenthesisGlyph | null = null; + let sizePerLine: number = sr.getScoreY(1, 0); + for (let i: number = 0, j: number = this._infos.length; i < j; i++) { + let g: GhostParenthesisGlyph; + if (!this._infos[i].isGhost) { + previousGlyph = null; + } else if (!previousGlyph) { + g = new GhostParenthesisGlyph(this._isOpen); + g.renderer = this.renderer; + g.y = sr.getScoreY(this._infos[i].line, 0) - sizePerLine; + g.height = sizePerLine * 2; + g.doLayout(); + this._glyphs.push(g); + previousGlyph = g; + } else { + let y: number = sr.getScoreY(this._infos[i].line, 0) + sizePerLine; + previousGlyph.height = y - previousGlyph.y; + } + } + this.width = this._glyphs.length > 0 ? this._glyphs[0].width : 0; + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + super.paint(cx, cy, canvas); + for (let g of this._glyphs) { + g.paint(cx + this.x, cy + this.y, canvas); + } + } +} diff --git a/src/rendering/glyphs/GhostParenthesisGlyph.ts b/src/rendering/glyphs/GhostParenthesisGlyph.ts new file mode 100644 index 000000000..d03d9414b --- /dev/null +++ b/src/rendering/glyphs/GhostParenthesisGlyph.ts @@ -0,0 +1,49 @@ +import { ICanvas } from '@src/platform/ICanvas'; +import { Glyph } from '@src/rendering/glyphs/Glyph'; +import { TieGlyph } from '@src/rendering/glyphs/TieGlyph'; + +export class GhostParenthesisGlyph extends Glyph { + private static readonly Size: number = 6; + private _isOpen: boolean; + + public constructor(isOpen: boolean) { + super(0, 0); + this._isOpen = isOpen; + } + + public height: number = 0; + + public doLayout(): void { + super.doLayout(); + this.width = GhostParenthesisGlyph.Size * this.scale; + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + if (this._isOpen) { + TieGlyph.paintTie( + canvas, + this.scale, + cx + this.x + this.width, + cy + this.y + this.height, + cx + this.x + this.width, + cy + this.y, + false, + 6, + 3 + ); + } else { + TieGlyph.paintTie( + canvas, + this.scale, + cx + this.x, + cy + this.y, + cx + this.x, + cy + this.y + this.height, + false, + 6, + 3 + ); + } + canvas.fill(); + } +} diff --git a/src/rendering/glyphs/Glyph.ts b/src/rendering/glyphs/Glyph.ts new file mode 100644 index 000000000..f4d63a779 --- /dev/null +++ b/src/rendering/glyphs/Glyph.ts @@ -0,0 +1,30 @@ +import { ICanvas } from '@src/platform/ICanvas'; +import { BarRendererBase } from '@src/rendering/BarRendererBase'; + +/** + * A glyph is a single symbol which can be added to a GlyphBarRenderer for automated + * layouting and drawing of stacked symbols. + */ +export class Glyph { + public x: number = 0; + public y: number = 0; + public width: number = 0; + public renderer!: BarRendererBase; + + public constructor(x: number = 0, y: number = 0) { + this.x = x; + this.y = y; + } + + public get scale(): number { + return this.renderer.scale; + } + + public doLayout(): void { + // to be implemented in subclass + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + // to be implemented in subclass + } +} diff --git a/src/rendering/glyphs/GlyphGroup.ts b/src/rendering/glyphs/GlyphGroup.ts new file mode 100644 index 000000000..d6f662b9f --- /dev/null +++ b/src/rendering/glyphs/GlyphGroup.ts @@ -0,0 +1,50 @@ +import { ICanvas } from '@src/platform/ICanvas'; +import { Glyph } from '@src/rendering/glyphs/Glyph'; + +/** + * This glyph allows to group several other glyphs to be + * drawn at the same x position + */ +export class GlyphGroup extends Glyph { + protected glyphs: Glyph[] | null = null; + + public get isEmpty(): boolean { + return !this.glyphs || this.glyphs.length === 0; + } + + public constructor(x: number, y: number) { + super(x, y); + } + + public doLayout(): void { + if (!this.glyphs || this.glyphs.length === 0) { + this.width = 0; + return; + } + let w: number = 0; + for (let i: number = 0, j: number = this.glyphs.length; i < j; i++) { + let g: Glyph = this.glyphs[i]; + g.renderer = this.renderer; + g.doLayout(); + w = Math.max(w, g.width); + } + this.width = w; + } + + public addGlyph(g: Glyph): void { + if (!this.glyphs) { + this.glyphs = []; + } + this.glyphs.push(g); + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + let glyphs = this.glyphs; + if (!glyphs || glyphs.length === 0) { + return; + } + for (let g of glyphs) { + g.paint(cx + this.x, cy + this.y, canvas); + } + } +} diff --git a/src/rendering/glyphs/GroupedEffectGlyph.ts b/src/rendering/glyphs/GroupedEffectGlyph.ts new file mode 100644 index 000000000..99c43dccf --- /dev/null +++ b/src/rendering/glyphs/GroupedEffectGlyph.ts @@ -0,0 +1,78 @@ +import { Beat } from '@src/model/Beat'; +import { ICanvas } from '@src/platform/ICanvas'; +import { BarRendererBase } from '@src/rendering/BarRendererBase'; +import { BeatXPosition } from '@src/rendering/BeatXPosition'; +import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph'; + +export abstract class GroupedEffectGlyph extends EffectGlyph { + protected endPosition: BeatXPosition; + protected forceGroupedRendering: boolean = false; + protected endOnBarLine: boolean = false; + + protected constructor(endPosition: BeatXPosition) { + super(); + this.endPosition = endPosition; + } + + public get isLinkedWithPrevious(): boolean { + return !!this.previousGlyph && this.previousGlyph.renderer.staff.staveGroup === this.renderer.staff.staveGroup; + } + + public get isLinkedWithNext(): boolean { + return ( + !!this.nextGlyph && + this.nextGlyph.renderer.isFinalized && + this.nextGlyph.renderer.staff.staveGroup === this.renderer.staff.staveGroup + ); + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + // if we are linked with the previous, the first glyph of the group will also render this one. + if (this.isLinkedWithPrevious) { + return; + } + // we are not linked with any glyph therefore no expansion is required, we render a simple glyph. + if (!this.isLinkedWithNext && !this.forceGroupedRendering) { + this.paintNonGrouped(cx, cy, canvas); + return; + } + // find last linked glyph that can be + let lastLinkedGlyph: GroupedEffectGlyph; + if (!this.isLinkedWithNext && this.forceGroupedRendering) { + lastLinkedGlyph = this; + } else { + lastLinkedGlyph = this.nextGlyph as GroupedEffectGlyph; + while (lastLinkedGlyph.isLinkedWithNext) { + lastLinkedGlyph = lastLinkedGlyph.nextGlyph as GroupedEffectGlyph; + } + } + // use start position of next beat when possible + let endBeatRenderer: BarRendererBase = lastLinkedGlyph.renderer; + let endBeat: Beat = lastLinkedGlyph.beat; + let position: BeatXPosition = this.endPosition; + // calculate end X-position + let cxRenderer: number = cx - this.renderer.x; + let endX: number = this.calculateEndX(endBeatRenderer, endBeat, cxRenderer, position); + this.paintGrouped(cx, cy, endX, canvas); + } + + protected calculateEndX( + endBeatRenderer: BarRendererBase, + endBeat: Beat, + cx: number, + endPosition: BeatXPosition + ): number { + if (!endBeat) { + return cx + endBeatRenderer.x + this.x + this.width; + } + return cx + endBeatRenderer.x + endBeatRenderer.getBeatX(endBeat, endPosition); + } + + protected paintNonGrouped(cx: number, cy: number, canvas: ICanvas): void { + let cxRenderer: number = cx - this.renderer.x; + let endX: number = this.calculateEndX(this.renderer, this.beat, cxRenderer, this.endPosition); + this.paintGrouped(cx, cy, endX, canvas); + } + + protected abstract paintGrouped(cx: number, cy: number, endX: number, canvas: ICanvas): void; +} diff --git a/src/rendering/glyphs/HiHatGlyph.ts b/src/rendering/glyphs/HiHatGlyph.ts new file mode 100644 index 000000000..5407f531c --- /dev/null +++ b/src/rendering/glyphs/HiHatGlyph.ts @@ -0,0 +1,17 @@ +import { MusicFontGlyph } from '@src/rendering/glyphs/MusicFontGlyph'; +import { MusicFontSymbol } from '@src/rendering/glyphs/MusicFontSymbol'; +import { NoteHeadGlyph } from '@src/rendering/glyphs/NoteHeadGlyph'; + +export class HiHatGlyph extends MusicFontGlyph { + private _isGrace: boolean; + + public constructor(x: number, y: number, isGrace: boolean) { + super(x, y, isGrace ? NoteHeadGlyph.GraceScale : 1, MusicFontSymbol.NoteHiHat); + this._isGrace = isGrace; + } + + public doLayout(): void { + this.width = 9 * (this._isGrace ? NoteHeadGlyph.GraceScale : 1) * this.scale; + this.height = NoteHeadGlyph.NoteHeadHeight * this.scale; + } +} diff --git a/src/rendering/glyphs/LeftHandTapGlyph.ts b/src/rendering/glyphs/LeftHandTapGlyph.ts new file mode 100644 index 000000000..e29650ed5 --- /dev/null +++ b/src/rendering/glyphs/LeftHandTapGlyph.ts @@ -0,0 +1,27 @@ +import { ICanvas, TextAlign } from '@src/platform/ICanvas'; +import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph'; +import { RenderingResources } from '@src/RenderingResources'; + +export class LeftHandTapGlyph extends EffectGlyph { + private static readonly Padding = 4; + + public constructor() { + super(0, 0); + } + + public doLayout(): void { + super.doLayout(); + const font = this.renderer.resources.effectFont; + this.height = font.size + LeftHandTapGlyph.Padding * this.scale; + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + let res: RenderingResources = this.renderer.resources; + canvas.font = res.effectFont; + let old: TextAlign = canvas.textAlign; + canvas.textAlign = TextAlign.Center; + canvas.fillText('T', cx + this.x, cy + this.y + canvas.font.size / 2); + canvas.textAlign = old; + canvas.strokeCircle(cx + this.x, cy + this.y + canvas.font.size / 2 + (LeftHandTapGlyph.Padding - 1) * this.scale, canvas.font.size / 1.6); + } +} diff --git a/src/rendering/glyphs/LeftToRightLayoutingGlyphGroup.ts b/src/rendering/glyphs/LeftToRightLayoutingGlyphGroup.ts new file mode 100644 index 000000000..62e4b200b --- /dev/null +++ b/src/rendering/glyphs/LeftToRightLayoutingGlyphGroup.ts @@ -0,0 +1,20 @@ +import { Glyph } from '@src/rendering/glyphs/Glyph'; +import { GlyphGroup } from '@src/rendering/glyphs/GlyphGroup'; + +export class LeftToRightLayoutingGlyphGroup extends GlyphGroup { + public constructor() { + super(0, 0); + this.glyphs = []; + } + + public addGlyph(g: Glyph): void { + g.x = + this.glyphs!.length === 0 + ? 0 + : this.glyphs![this.glyphs!.length - 1].x + this.glyphs![this.glyphs!.length - 1].width; + g.renderer = this.renderer; + g.doLayout(); + this.width = g.x + g.width; + super.addGlyph(g); + } +} diff --git a/src/rendering/glyphs/LineRangedGlyph.ts b/src/rendering/glyphs/LineRangedGlyph.ts new file mode 100644 index 000000000..572c84e43 --- /dev/null +++ b/src/rendering/glyphs/LineRangedGlyph.ts @@ -0,0 +1,58 @@ +import { ICanvas, TextAlign } from '@src/platform/ICanvas'; +import { BeatXPosition } from '@src/rendering/BeatXPosition'; +import { GroupedEffectGlyph } from '@src/rendering/glyphs/GroupedEffectGlyph'; +import { RenderingResources } from '@src/RenderingResources'; + +export class LineRangedGlyph extends GroupedEffectGlyph { + public static readonly LineSpacing: number = 3; + public static readonly LineTopPadding: number = 4; + public static readonly LineTopOffset: number = 5; + public static readonly LineSize: number = 8; + private _label: string; + + public constructor(label: string) { + super(BeatXPosition.OnNotes); + this._label = label; + } + + public doLayout(): void { + if (this.renderer.settings.notation.extendLineEffectsToBeatEnd) { + this.endPosition = BeatXPosition.EndBeat; + this.forceGroupedRendering = true; + } + super.doLayout(); + this.height = this.renderer.resources.effectFont.size; + } + + protected paintNonGrouped(cx: number, cy: number, canvas: ICanvas): void { + let res: RenderingResources = this.renderer.resources; + canvas.font = res.effectFont; + let x: TextAlign = canvas.textAlign; + canvas.textAlign = TextAlign.Center; + canvas.fillText(this._label, cx + this.x, cy + this.y); + canvas.textAlign = x; + } + + protected paintGrouped(cx: number, cy: number, endX: number, canvas: ICanvas): void { + this.paintNonGrouped(cx, cy, canvas); + let lineSpacing: number = 3 * this.scale; + let textWidth: number = canvas.measureText(this._label); + let startX: number = cx + this.x + textWidth / 2 + lineSpacing; + let lineY: number = cy + this.y + 4 * this.scale; + let lineSize: number = 8 * this.scale; + if (endX > startX) { + let lineX: number = startX; + while (lineX < endX) { + canvas.beginPath(); + canvas.moveTo(lineX, lineY | 0); + canvas.lineTo(Math.min(lineX + lineSize, endX), lineY | 0); + lineX += lineSize + lineSpacing; + canvas.stroke(); + } + canvas.beginPath(); + canvas.moveTo(endX, (lineY - 5 * this.scale) | 0); + canvas.lineTo(endX, (lineY + 5 * this.scale) | 0); + canvas.stroke(); + } + } +} diff --git a/src/rendering/glyphs/LyricsGlyph.ts b/src/rendering/glyphs/LyricsGlyph.ts new file mode 100644 index 000000000..0a03654ea --- /dev/null +++ b/src/rendering/glyphs/LyricsGlyph.ts @@ -0,0 +1,34 @@ +import { Font } from '@src/model/Font'; +import { ICanvas, TextAlign } from '@src/platform/ICanvas'; +import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph'; + +export class LyricsGlyph extends EffectGlyph { + private _lines: string[]; + + public font: Font; + public textAlign: TextAlign; + + public constructor(x: number, y: number, lines: string[], font: Font, textAlign: TextAlign = TextAlign.Center) { + super(x, y); + this._lines = lines; + this.font = font; + this.textAlign = textAlign; + } + + public doLayout(): void { + super.doLayout(); + this.height = this.font.size * this._lines.length; + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + canvas.font = this.font; + let old: TextAlign = canvas.textAlign; + canvas.textAlign = this.textAlign; + for (let i: number = 0; i < this._lines.length; i++) { + if (this._lines[i]) { + canvas.fillText(this._lines[i], cx + this.x, cy + this.y + i * this.font.size); + } + } + canvas.textAlign = old; + } +} diff --git a/src/rendering/glyphs/MusicFontGlyph.ts b/src/rendering/glyphs/MusicFontGlyph.ts new file mode 100644 index 000000000..67a508690 --- /dev/null +++ b/src/rendering/glyphs/MusicFontGlyph.ts @@ -0,0 +1,18 @@ +import { ICanvas } from '@src/platform/ICanvas'; +import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph'; +import { MusicFontSymbol } from '@src/rendering/glyphs/MusicFontSymbol'; + +export class MusicFontGlyph extends EffectGlyph { + protected glyphScale: number = 0; + protected symbol: MusicFontSymbol; + + public constructor(x: number, y: number, glyphScale: number, symbol: MusicFontSymbol) { + super(x, y); + this.glyphScale = glyphScale; + this.symbol = symbol; + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + canvas.fillMusicFontSymbol(cx + this.x, cy + this.y, this.glyphScale * this.scale, this.symbol, false); + } +} diff --git a/src/rendering/glyphs/MusicFontSymbol.ts b/src/rendering/glyphs/MusicFontSymbol.ts new file mode 100644 index 000000000..014d3271f --- /dev/null +++ b/src/rendering/glyphs/MusicFontSymbol.ts @@ -0,0 +1,120 @@ +export enum MusicFontSymbol { + None = -1, + + ClefG = 0xe050, + ClefC = 0xe05c, + ClefF = 0xe062, + ClefNeutral = 0xe069, + ClefTab = 0xe06d, + ClefTabSmall = 0xe06e, + + RestQuadrupleWhole = 0xe4e1, + RestDoubleWhole = 0xe4e2, + RestWhole = 0xe4e3, + RestHalf = 0xe4e4, + RestQuarter = 0xe4e5, + RestEighth = 0xe4e6, + RestSixteenth = 0xe4e7, + RestThirtySecond = 0xe4e8, + RestSixtyFourth = 0xe4e9, + RestOneHundredTwentyEighth = 0xe4ea, + RestTwoHundredFiftySixth = 0xe4eb, + + Trill = 0xe566, + + Num0 = 0xe080, + Num1 = 0xe081, + Num2 = 0xe082, + Num3 = 0xe083, + Num4 = 0xe084, + Num5 = 0xe085, + Num6 = 0xe086, + Num7 = 0xe087, + Num8 = 0xe088, + Num9 = 0xe089, + TimeSignatureCommon = 0xe08a, + TimeSignatureCutCommon = 0xe08b, + + NoteQuadrupleWhole = 0xe0a1, + NoteDoubleWhole = 0xe0a0, + NoteWhole = 0xe0a2, + NoteHalf = 0xe0a3, + NoteQuarter = 0xe0a4, + NoteDead = 0xe0aa, + NoteHarmonic = 0xe0dc, + NoteHarmonicWhole = 0xe0de, + NoteHiHat = 0xe0b3, + NoteSideStick = 0xe0a9, + NoteHiHatHalf = 0xe0f7, + NoteChineseCymbal = 0xe0f9, + + FooterUpEighth = 0xe240, + FooterDownEighth = 0xe241, + + FooterUpSixteenth = 0xe242, + FooterDownSixteenth = 0xe243, + + FooterUpThirtySecond = 0xe244, + FooterDownThirtySecond = 0xe245, + + FooterUpSixtyFourth = 0xe246, + FooterDownSixtyFourth = 0xe247, + + FooterUpOneHundredTwentyEighth = 0xe248, + FooterDownOneHundredTwentyEighth = 0xe249, + + FooterUpTwoHundredFiftySixth = 0xe24a, + FooterDownTwoHundredFiftySixth = 0xe24b, + + DynamicPPP = 0xe52a, + DynamicPP = 0xe52b, + DynamicP = 0xe520, + DynamicMP = 0xe52c, + DynamicMF = 0xe52d, + DynamicF = 0xe522, + DynamicFF = 0xe52f, + DynamicFFF = 0xe530, + + Accentuation = 0xe4a0, + HeavyAccentuation = 0xe4ac, + + WaveHorizontalSlight = 0xeaa4, + WaveHorizontalWide = 0xeade, + + PickStrokeDown = 0xe610, + PickStrokeUp = 0xe612, + TremoloPickingThirtySecond = 0xe222, + TremoloPickingSixteenth = 0xe221, + TremoloPickingEighth = 0xe220, + + Tempo = 0xe1d5, + NoteEighth = 0xe1d7, + + AccidentalFlat = 0xe260, + AccidentalNatural = 0xe261, + AccidentalSharp = 0xe262, + AccidentalQuarterToneFlatArrowUp = 0xe270, + AccidentalQuarterToneSharpArrowUp = 0xe274, + AccidentalQuarterToneNaturalArrowUp = 0xe272, + + Ottava8 = 0xe510, + + Ottava8va = 0xe511, + + Ottava8vb = 0xe51c, + Ottava15 = 0xe514, + + Ottava15ma = 0xe515, + OttavaMBaseline = 0xec95, + OttavaBBaseline = 0xec93, + + SimileMarkSimple = 0xe500, + SimileMarkDouble = 0xe501, + + FermataMedium = 0xe4c0, + FermataShort = 0xe4c4, + FermataLong = 0xe4c6, + + FretboardX = 0xe859, + FretboardO = 0xe85a +} diff --git a/src/rendering/glyphs/NoteHeadGlyph.ts b/src/rendering/glyphs/NoteHeadGlyph.ts new file mode 100644 index 000000000..6b15c88ec --- /dev/null +++ b/src/rendering/glyphs/NoteHeadGlyph.ts @@ -0,0 +1,57 @@ +import { Duration } from '@src/model/Duration'; +import { ICanvas } from '@src/platform/ICanvas'; +import { MusicFontGlyph } from '@src/rendering/glyphs/MusicFontGlyph'; +import { MusicFontSymbol } from '@src/rendering/glyphs/MusicFontSymbol'; + +export class NoteHeadGlyph extends MusicFontGlyph { + public static readonly GraceScale: number = 0.75; + public static readonly NoteHeadHeight: number = 9; + public static readonly QuarterNoteHeadWidth: number = 10; + private _isGrace: boolean; + private _duration: Duration; + + public constructor(x: number, y: number, duration: Duration, isGrace: boolean) { + super(x, y, isGrace ? NoteHeadGlyph.GraceScale : 1, NoteHeadGlyph.getSymbol(duration)); + this._isGrace = isGrace; + this._duration = duration; + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + let offset: number = this._isGrace ? this.scale : 0; + canvas.fillMusicFontSymbol(cx + this.x, cy + this.y + offset, this.glyphScale * this.scale, this.symbol, false); + } + + public doLayout(): void { + let scale: number = (this._isGrace ? NoteHeadGlyph.GraceScale : 1) * this.scale; + switch (this._duration) { + case Duration.QuadrupleWhole: + this.width = 14 * scale; + break; + case Duration.DoubleWhole: + this.width = 14 * (this._isGrace ? NoteHeadGlyph.GraceScale : 1) * this.scale; + break; + case Duration.Whole: + this.width = 14 * (this._isGrace ? NoteHeadGlyph.GraceScale : 1) * this.scale; + break; + default: + this.width = 10 * (this._isGrace ? NoteHeadGlyph.GraceScale : 1) * this.scale; + break; + } + this.height = NoteHeadGlyph.NoteHeadHeight * scale; + } + + private static getSymbol(duration: Duration): MusicFontSymbol { + switch (duration) { + case Duration.QuadrupleWhole: + return MusicFontSymbol.NoteQuadrupleWhole; + case Duration.DoubleWhole: + return MusicFontSymbol.NoteDoubleWhole; + case Duration.Whole: + return MusicFontSymbol.NoteWhole; + case Duration.Half: + return MusicFontSymbol.NoteHalf; + default: + return MusicFontSymbol.NoteQuarter; + } + } +} diff --git a/src/rendering/glyphs/NoteNumberGlyph.ts b/src/rendering/glyphs/NoteNumberGlyph.ts new file mode 100644 index 000000000..87ea332a6 --- /dev/null +++ b/src/rendering/glyphs/NoteNumberGlyph.ts @@ -0,0 +1,117 @@ +import { BendType } from '@src/model/BendType'; +import { Font } from '@src/model/Font'; +import { HarmonicType } from '@src/model/HarmonicType'; +import { Note } from '@src/model/Note'; +import { ICanvas } from '@src/platform/ICanvas'; +import { Glyph } from '@src/rendering/glyphs/Glyph'; +import { Bounds } from '@src/rendering/utils/Bounds'; +import { NoteBounds } from '@src/rendering/utils/NoteBounds'; +import { ModelUtils } from '@src/model/ModelUtils'; +import { NotationElement, NotationMode } from '@src/NotationSettings'; +import { BeatBounds } from '../utils/BeatBounds'; + +export class NoteNumberGlyph extends Glyph { + private _note: Note; + private _noteString: string | null = null; + private _trillNoteString: string | null = null; + private _trillNoteStringWidth: number = 0; + + public isEmpty: boolean = false; + public height: number = 0; + public noteStringWidth: number = 0; + + public constructor(x: number, y: number, note: Note) { + super(x, y); + this._note = note; + } + + public doLayout(): void { + let n: Note = this._note; + let fret: number = n.fret - n.beat.voice.bar.staff.transpositionPitch; + if (n.harmonicType === HarmonicType.Natural && n.harmonicValue !== 0) { + fret = n.harmonicValue - n.beat.voice.bar.staff.transpositionPitch; + } + if (!n.isTieDestination) { + this._noteString = n.isDead ? 'x' : fret.toString(); + if (n.isGhost) { + this._noteString = '(' + this._noteString + ')'; + } else if (n.harmonicType === HarmonicType.Natural) { + // only first decimal char + let i: number = this._noteString.indexOf(String.fromCharCode(46)); + if (i >= 0) { + this._noteString = this._noteString.substr(0, i + 2); + } + this._noteString = '<' + this._noteString + '>'; + } + } else if ( + (n.beat.index === 0 && this.renderer.settings.notation.notationMode == NotationMode.GuitarPro) || + ((n.bendType === BendType.Bend || n.bendType === BendType.BendRelease) && + this.renderer.settings.notation.isNotationElementVisible(NotationElement.TabNotesOnTiedBends)) + ) { + this._noteString = '(' + (n.tieOrigin!.fret - n.beat.voice.bar.staff.transpositionPitch) + ')'; + } else { + this._noteString = ''; + } + if (n.isTrill) { + this._trillNoteString = '(' + (n.trillFret - n.beat.voice.bar.staff.transpositionPitch) + ')'; + } else if (!ModelUtils.isAlmostEqualTo(n.harmonicValue, 0)) { + switch (n.harmonicType) { + case HarmonicType.Artificial: + case HarmonicType.Pinch: + case HarmonicType.Tap: + case HarmonicType.Semi: + case HarmonicType.Feedback: + let s: string = (fret + n.harmonicValue).toString(); + // only first decimal char + let i: number = s.indexOf(String.fromCharCode(46)); + if (i >= 0) { + s = s.substr(0, i + 2); + } + this._trillNoteString = '<' + s + '>'; + break; + default: + this._trillNoteString = ''; + break; + } + } else { + this._trillNoteString = ''; + } + this.isEmpty = !this._noteString; + if (!this.isEmpty) { + this.renderer.scoreRenderer.canvas!.font = this.renderer.resources.tablatureFont; + this.width = this.noteStringWidth = this.renderer.scoreRenderer.canvas!.measureText(this._noteString); + this.height = this.renderer.scoreRenderer.canvas!.font.size; + let hasTrill: boolean = !!this._trillNoteString; + if (hasTrill) { + this.renderer.scoreRenderer.canvas!.font = this.renderer.resources.graceFont; + this._trillNoteStringWidth = + 3 * this.scale + this.renderer.scoreRenderer.canvas!.measureText(this._trillNoteString); + this.width += this._trillNoteStringWidth; + } + } + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + if (this.isEmpty) { + return; + } + let textWidth: number = this.noteStringWidth + this._trillNoteStringWidth; + let x: number = cx + this.x + (this.width - textWidth) / 2; + let prevFont: Font = this.renderer.scoreRenderer.canvas!.font; + this.renderer.scoreRenderer.canvas!.font = this.renderer.resources.graceFont; + canvas.fillText(this._trillNoteString!, x + this.noteStringWidth + 3 * this.scale, cy + this.y); + this.renderer.scoreRenderer.canvas!.font = prevFont; + canvas.fillText(this._noteString!, x, cy + this.y); + } + + public buildBoundingsLookup(beatBounds: BeatBounds, cx: number, cy: number) { + let noteBounds: NoteBounds = new NoteBounds(); + noteBounds.note = this._note; + noteBounds.noteHeadBounds = new Bounds(); + noteBounds.noteHeadBounds.x = cx + this.x; + noteBounds.noteHeadBounds.y = cy + this.y - this.height/2; + noteBounds.noteHeadBounds.w = this.width; + noteBounds.noteHeadBounds.h = this.height; + this.renderer.scoreRenderer.boundsLookup!.addNote(noteBounds); + } +} diff --git a/src/rendering/glyphs/NoteVibratoGlyph.ts b/src/rendering/glyphs/NoteVibratoGlyph.ts new file mode 100644 index 000000000..770f7c1af --- /dev/null +++ b/src/rendering/glyphs/NoteVibratoGlyph.ts @@ -0,0 +1,70 @@ +import { VibratoType } from '@src/model/VibratoType'; +import { ICanvas } from '@src/platform/ICanvas'; +import { BeatXPosition } from '@src/rendering/BeatXPosition'; +import { GroupedEffectGlyph } from '@src/rendering/glyphs/GroupedEffectGlyph'; +import { MusicFontSymbol } from '@src/rendering/glyphs/MusicFontSymbol'; + +export class NoteVibratoGlyph extends GroupedEffectGlyph { + private _type: VibratoType; + private _scale: number = 0; + private _symbol: MusicFontSymbol = MusicFontSymbol.None; + private _symbolSize: number = 0; + private _symbolOffset: number = 0; + private _partialWaves: boolean; + + public constructor(x: number, y: number, type: VibratoType, scale: number = 1.2, partialWaves: boolean = false) { + super(BeatXPosition.EndBeat); + this._type = type; + this._scale = scale; + this.x = x; + this.y = y; + this._partialWaves = partialWaves; + } + + public doLayout(): void { + super.doLayout(); + let symbolHeight: number = 0; + switch (this._type) { + case VibratoType.Slight: + this._symbol = MusicFontSymbol.WaveHorizontalSlight; + this._symbolSize = 9 * this._scale; + this._symbolOffset = 8.5 * this._scale; + symbolHeight = 6 * this._scale; + break; + case VibratoType.Wide: + this._symbol = MusicFontSymbol.WaveHorizontalWide; + this._symbolSize = 10 * this._scale; + this._symbolOffset = 7 * this._scale; + symbolHeight = 10 * this._scale; + break; + } + this.height = symbolHeight * this.scale; + } + + protected paintGrouped(cx: number, cy: number, endX: number, canvas: ICanvas): void { + let startX: number = cx + this.x; + let width: number = endX - startX; + let step: number = this._symbolSize * this.scale; + + let loops: number = width / step; + if (!this._partialWaves) { + loops = Math.floor(loops); + } + if (loops < 1) { + loops = 1; + } + + let loopX: number = 0; + + for (let i: number = 0; i < loops; i++) { + canvas.fillMusicFontSymbol( + cx + this.x + loopX, + cy + this.y + this._symbolOffset, + this._scale, + this._symbol, + false + ); + loopX += step; + } + } +} diff --git a/src/rendering/glyphs/NumberGlyph.ts b/src/rendering/glyphs/NumberGlyph.ts new file mode 100644 index 000000000..1498f4fa4 --- /dev/null +++ b/src/rendering/glyphs/NumberGlyph.ts @@ -0,0 +1,38 @@ +import { DigitGlyph } from '@src/rendering/glyphs/DigitGlyph'; +import { Glyph } from '@src/rendering/glyphs/Glyph'; +import { GlyphGroup } from '@src/rendering/glyphs/GlyphGroup'; + +export class NumberGlyph extends GlyphGroup { + private _number: number = 0; + private _scale: number = 0; + + public constructor(x: number, y: number, num: number, scale: number = 1.0) { + super(x, y); + this._number = num; + this._scale = scale; + } + + public doLayout(): void { + let i: number = this._number; + while (i > 0) { + let num: number = i % 10; + let gl: DigitGlyph = new DigitGlyph(0, 0, num, this._scale); + this.addGlyph(gl); + i = (i / 10) | 0; + } + + if (this.glyphs) { + this.glyphs.reverse(); + let cx: number = 0; + for (let j: number = 0, k: number = this.glyphs.length; j < k; j++) { + let g: Glyph = this.glyphs[j]; + g.x = cx; + g.y = 0; + g.renderer = this.renderer; + g.doLayout(); + cx += g.width; + } + this.width = cx; + } + } +} diff --git a/src/rendering/glyphs/OttavaGlyph.ts b/src/rendering/glyphs/OttavaGlyph.ts new file mode 100644 index 000000000..2d9ca9577 --- /dev/null +++ b/src/rendering/glyphs/OttavaGlyph.ts @@ -0,0 +1,101 @@ +import { Ottavia } from '@src/model/Ottavia'; +import { ICanvas } from '@src/platform/ICanvas'; +import { BeatXPosition } from '@src/rendering/BeatXPosition'; +import { GroupedEffectGlyph } from '@src/rendering/glyphs/GroupedEffectGlyph'; +import { MusicFontSymbol } from '@src/rendering/glyphs/MusicFontSymbol'; + +export class OttavaGlyph extends GroupedEffectGlyph { + private _ottava: Ottavia; + private _aboveStaff: boolean; + + public constructor(ottava: Ottavia, aboveStaff: boolean) { + super(BeatXPosition.PostNotes); + this._ottava = ottava; + this._aboveStaff = aboveStaff; + } + + public doLayout(): void { + super.doLayout(); + this.height = 13 * this.scale; + } + + protected paintNonGrouped(cx: number, cy: number, canvas: ICanvas): void { + this.paintOttava(cx, cy, canvas); + } + + private paintOttava(cx: number, cy: number, canvas: ICanvas): number { + let size: number = 0; + switch (this._ottava) { + case Ottavia._15ma: + size = 37 * this.scale; + canvas.fillMusicFontSymbol( + cx + this.x - size / 2, + cy + this.y + this.height, + 0.8, + MusicFontSymbol.Ottava15ma, + false + ); + break; + case Ottavia._8va: + size = 26 * this.scale; + canvas.fillMusicFontSymbol( + cx + this.x - size / 2, + cy + this.y + this.height, + 0.8, + MusicFontSymbol.Ottava8va, + false + ); + break; + case Ottavia._8vb: + size = 23 * this.scale; + canvas.fillMusicFontSymbol( + cx + this.x - size / 2, + cy + this.y + this.height, + 0.8, + MusicFontSymbol.Ottava8vb, + false + ); + break; + case Ottavia._15mb: + size = 36 * this.scale; + // NOTE: SMUFL does not have a glyph for 15mb so we build it + canvas.fillMusicFontSymbols( + cx + this.x - size / 2, + cy + this.y + this.height, + 0.8, + [MusicFontSymbol.Ottava15, MusicFontSymbol.OttavaMBaseline, MusicFontSymbol.OttavaBBaseline], + false + ); + break; + } + return size / 2; + } + + protected paintGrouped(cx: number, cy: number, endX: number, canvas: ICanvas): void { + let size: number = this.paintOttava(cx, cy, canvas); + let lineSpacing: number = 3 * this.scale; + let startX: number = cx + this.x + size + lineSpacing; + let lineY: number = cy + this.y; + lineY += this._aboveStaff ? 2 * this.scale : this.height - 2 * this.scale; + let lineSize: number = 8 * this.scale; + if (endX > startX) { + let lineX: number = startX; + while (lineX < endX) { + canvas.beginPath(); + canvas.moveTo(lineX, lineY | 0); + canvas.lineTo(Math.min(lineX + lineSize, endX), lineY | 0); + lineX += lineSize + lineSpacing; + canvas.stroke(); + } + canvas.beginPath(); + if (this._aboveStaff) { + canvas.moveTo(endX, lineY); + canvas.lineTo(endX, cy + this.y + this.height); + } else { + canvas.moveTo(endX, lineY); + canvas.lineTo(endX, cy + this.y); + } + canvas.stroke(); + } + } +} diff --git a/src/rendering/glyphs/PickStrokeGlyph.ts b/src/rendering/glyphs/PickStrokeGlyph.ts new file mode 100644 index 000000000..fc9f03d69 --- /dev/null +++ b/src/rendering/glyphs/PickStrokeGlyph.ts @@ -0,0 +1,31 @@ +import { PickStroke } from '@src/model/PickStroke'; +import { ICanvas } from '@src/platform/ICanvas'; +import { MusicFontGlyph } from '@src/rendering/glyphs/MusicFontGlyph'; +import { MusicFontSymbol } from '@src/rendering/glyphs/MusicFontSymbol'; +import { NoteHeadGlyph } from '@src/rendering/glyphs/NoteHeadGlyph'; + +export class PickStrokeGlyph extends MusicFontGlyph { + public constructor(x: number, y: number, pickStroke: PickStroke) { + super(x, y, NoteHeadGlyph.GraceScale, PickStrokeGlyph.getSymbol(pickStroke)); + } + + public doLayout(): void { + this.width = 9 * this.scale; + this.height = 10 * this.scale; + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + super.paint(cx, cy + this.height, canvas); + } + + private static getSymbol(pickStroke: PickStroke): MusicFontSymbol { + switch (pickStroke) { + case PickStroke.Up: + return MusicFontSymbol.PickStrokeUp; + case PickStroke.Down: + return MusicFontSymbol.PickStrokeDown; + default: + return MusicFontSymbol.None; + } + } +} diff --git a/src/rendering/glyphs/RepeatCloseGlyph.ts b/src/rendering/glyphs/RepeatCloseGlyph.ts new file mode 100644 index 000000000..93fb4479e --- /dev/null +++ b/src/rendering/glyphs/RepeatCloseGlyph.ts @@ -0,0 +1,35 @@ +import { ICanvas } from '@src/platform/ICanvas'; +import { Glyph } from '@src/rendering/glyphs/Glyph'; + +export class RepeatCloseGlyph extends Glyph { + public constructor(x: number, y: number) { + super(x, y); + } + + public doLayout(): void { + this.width = 11 * this.scale; + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + let blockWidth: number = 4 * this.scale; + let top: number = cy + this.y + this.renderer.topPadding; + let bottom: number = cy + this.y + this.renderer.height - this.renderer.bottomPadding; + let left: number = cx + this.x; + let h: number = bottom - top; + // circles + let circleSize: number = 1.5 * this.scale; + let middle: number = (top + bottom) / 2; + let dotOffset: number = 3; + canvas.fillCircle(left, middle - circleSize * dotOffset, circleSize); + canvas.fillCircle(left, middle + circleSize * dotOffset, circleSize); + // line + left += 4 * this.scale; + canvas.beginPath(); + canvas.moveTo(left, top); + canvas.lineTo(left, bottom); + canvas.stroke(); + // big bar + left += 3 * this.scale + 0.5; + canvas.fillRect(left, top, blockWidth, h); + } +} diff --git a/src/rendering/glyphs/RepeatCountGlyph.ts b/src/rendering/glyphs/RepeatCountGlyph.ts new file mode 100644 index 000000000..d3858cfbb --- /dev/null +++ b/src/rendering/glyphs/RepeatCountGlyph.ts @@ -0,0 +1,28 @@ +import { ICanvas, TextAlign } from '@src/platform/ICanvas'; +import { Glyph } from '@src/rendering/glyphs/Glyph'; +import { RenderingResources } from '@src/RenderingResources'; + +export class RepeatCountGlyph extends Glyph { + private _count: number = 0; + + public constructor(x: number, y: number, count: number) { + super(x, y); + this._count = 0; + this._count = count; + } + + public doLayout(): void { + this.width = 0; + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + let res: RenderingResources = this.renderer.resources; + let oldAlign: TextAlign = canvas.textAlign; + canvas.font = res.barNumberFont; + canvas.textAlign = TextAlign.Right; + let s: string = 'x' + this._count; + let w: number = canvas.measureText(s) / 1.5; + canvas.fillText(s, cx + this.x - w, cy + this.y); + canvas.textAlign = oldAlign; + } +} diff --git a/src/rendering/glyphs/RepeatOpenGlyph.ts b/src/rendering/glyphs/RepeatOpenGlyph.ts new file mode 100644 index 000000000..819e69adc --- /dev/null +++ b/src/rendering/glyphs/RepeatOpenGlyph.ts @@ -0,0 +1,41 @@ +import { ICanvas } from '@src/platform/ICanvas'; +import { Glyph } from '@src/rendering/glyphs/Glyph'; + +export class RepeatOpenGlyph extends Glyph { + private _dotOffset: number = 0; + private _circleSize: number = 0; + + public constructor(x: number, y: number, circleSize: number, dotOffset: number) { + super(x, y); + this._dotOffset = 0.0; + this._circleSize = 0.0; + this._dotOffset = dotOffset; + this._circleSize = circleSize; + } + + public doLayout(): void { + this.width = 13 * this.scale; + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + let blockWidth: number = 4 * this.scale; + let top: number = cy + this.y + this.renderer.topPadding; + let bottom: number = cy + this.y + this.renderer.height - this.renderer.bottomPadding; + let left: number = cx + this.x + 0.5; + // big bar + let h: number = bottom - top; + canvas.fillRect(left, top, blockWidth, h); + // line + left += blockWidth * 2 - 0.5; + canvas.beginPath(); + canvas.moveTo(left, top); + canvas.lineTo(left, bottom); + canvas.stroke(); + // circles + left += 3 * this.scale; + let circleSize: number = this._circleSize * this.scale; + let middle: number = (top + bottom) / 2; + canvas.fillCircle(left, middle - circleSize * this._dotOffset, circleSize); + canvas.fillCircle(left, middle + circleSize * this._dotOffset, circleSize); + } +} diff --git a/src/rendering/glyphs/RideCymbalGlyph.ts b/src/rendering/glyphs/RideCymbalGlyph.ts new file mode 100644 index 000000000..9a4c7a833 --- /dev/null +++ b/src/rendering/glyphs/RideCymbalGlyph.ts @@ -0,0 +1,17 @@ +import { MusicFontGlyph } from '@src/rendering/glyphs/MusicFontGlyph'; +import { MusicFontSymbol } from '@src/rendering/glyphs/MusicFontSymbol'; +import { NoteHeadGlyph } from '@src/rendering/glyphs/NoteHeadGlyph'; + +export class RideCymbalGlyph extends MusicFontGlyph { + private _isGrace: boolean; + + public constructor(x: number, y: number, isGrace: boolean) { + super(x, y, isGrace ? NoteHeadGlyph.GraceScale : 1, MusicFontSymbol.NoteHarmonicWhole); + this._isGrace = isGrace; + } + + public doLayout(): void { + this.width = 9 * (this._isGrace ? NoteHeadGlyph.GraceScale : 1) * this.scale; + this.height = NoteHeadGlyph.NoteHeadHeight * this.scale; + } +} diff --git a/src/rendering/glyphs/ScoreBeatGlyph.ts b/src/rendering/glyphs/ScoreBeatGlyph.ts new file mode 100644 index 000000000..0744cff6a --- /dev/null +++ b/src/rendering/glyphs/ScoreBeatGlyph.ts @@ -0,0 +1,291 @@ +import { AccentuationType } from '@src/model/AccentuationType'; +import { Duration } from '@src/model/Duration'; +import { GraceType } from '@src/model/GraceType'; +import { HarmonicType } from '@src/model/HarmonicType'; +import { Note } from '@src/model/Note'; +import { AccentuationGlyph } from '@src/rendering/glyphs/AccentuationGlyph'; +import { BeatOnNoteGlyphBase } from '@src/rendering/glyphs/BeatOnNoteGlyphBase'; +import { ChineseCymbalGlyph } from '@src/rendering/glyphs/ChineseCymbalGlyph'; +import { CircleGlyph } from '@src/rendering/glyphs/CircleGlyph'; +import { DeadNoteHeadGlyph } from '@src/rendering/glyphs/DeadNoteHeadGlyph'; +import { DiamondNoteHeadGlyph } from '@src/rendering/glyphs/DiamondNoteHeadGlyph'; +import { DrumSticksGlyph } from '@src/rendering/glyphs/DrumSticksGlyph'; +import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph'; +import { GhostNoteContainerGlyph } from '@src/rendering/glyphs/GhostNoteContainerGlyph'; +import { GlyphGroup } from '@src/rendering/glyphs/GlyphGroup'; +import { HiHatGlyph } from '@src/rendering/glyphs/HiHatGlyph'; +import { NoteHeadGlyph } from '@src/rendering/glyphs/NoteHeadGlyph'; +import { RideCymbalGlyph } from '@src/rendering/glyphs/RideCymbalGlyph'; +import { ScoreNoteChordGlyph } from '@src/rendering/glyphs/ScoreNoteChordGlyph'; +import { ScoreRestGlyph } from '@src/rendering/glyphs/ScoreRestGlyph'; +import { ScoreWhammyBarGlyph } from '@src/rendering/glyphs/ScoreWhammyBarGlyph'; +import { SpacingGlyph } from '@src/rendering/glyphs/SpacingGlyph'; +import { ScoreBarRenderer } from '@src/rendering/ScoreBarRenderer'; +import { NoteXPosition, NoteYPosition } from '../BarRendererBase'; +import { BeatBounds } from '../utils/BeatBounds'; + +export class ScoreBeatGlyph extends BeatOnNoteGlyphBase { + public noteHeads: ScoreNoteChordGlyph | null = null; + public restGlyph: ScoreRestGlyph | null = null; + + public getNoteX(note: Note, requestedPosition: NoteXPosition): number { + return this.noteHeads ? this.noteHeads.getNoteX(note, requestedPosition) : 0; + } + + public buildBoundingsLookup(beatBounds:BeatBounds, cx:number, cy:number) { + if(this.noteHeads) { + this.noteHeads.buildBoundingsLookup(beatBounds, cx + this.x, cy + this.y); + } + } + + public getNoteY(note: Note, requestedPosition: NoteYPosition): number { + return this.noteHeads ? this.noteHeads.getNoteY(note, requestedPosition) : 0; + } + + public updateBeamingHelper(): void { + if (this.noteHeads) { + this.noteHeads.updateBeamingHelper(this.container.x + this.x); + } else if (this.restGlyph) { + this.restGlyph.updateBeamingHelper(this.container.x + this.x); + } + } + + public doLayout(): void { + // create glyphs + let sr: ScoreBarRenderer = this.renderer as ScoreBarRenderer; + if (!this.container.beat.isEmpty) { + if (!this.container.beat.isRest) { + // + // Note heads + // + this.noteHeads = new ScoreNoteChordGlyph(); + this.noteHeads.beat = this.container.beat; + this.noteHeads.beamingHelper = this.beamingHelper; + let ghost: GhostNoteContainerGlyph = new GhostNoteContainerGlyph(false); + ghost.renderer = this.renderer; + for (let note of this.container.beat.notes) { + if (note.isVisible) { + this.createNoteGlyph(note); + ghost.addParenthesis(note); + } + } + this.addGlyph(this.noteHeads); + if (!ghost.isEmpty) { + this.addGlyph( + new SpacingGlyph( + 0, + 0, + 4 * (this.container.beat.graceType !== GraceType.None ? NoteHeadGlyph.GraceScale : 1) * this.scale + ) + ); + this.addGlyph(ghost); + } + // + // Whammy Bar + if (this.container.beat.hasWhammyBar) { + let whammy: ScoreWhammyBarGlyph = new ScoreWhammyBarGlyph(this.container.beat); + whammy.renderer = this.renderer; + whammy.doLayout(); + this.container.ties.push(whammy); + } + // + // Note dots + // + if (this.container.beat.dots > 0) { + this.addGlyph(new SpacingGlyph(0, 0, 5 * this.scale)); + for (let i: number = 0; i < this.container.beat.dots; i++) { + let group: GlyphGroup = new GlyphGroup(0, 0); + for (let note of this.container.beat.notes) { + this.createBeatDot(sr.getNoteLine(note), group); + } + this.addGlyph(group); + } + } + } else { + let dotLine: number = 0; + let line: number = 0; + let offset: number = 0; + switch (this.container.beat.duration) { + case Duration.QuadrupleWhole: + line = 6; + dotLine = 5; + break; + case Duration.DoubleWhole: + line = 6; + dotLine = 5; + break; + case Duration.Whole: + line = 4; + dotLine = 5; + break; + case Duration.Half: + line = 6; + dotLine = 5; + break; + case Duration.Quarter: + line = 6; + offset = -2; + dotLine = 5; + break; + case Duration.Eighth: + line = 6; + dotLine = 5; + break; + case Duration.Sixteenth: + line = 6; + dotLine = 5; + break; + case Duration.ThirtySecond: + line = 6; + dotLine = 3; + break; + case Duration.SixtyFourth: + line = 6; + dotLine = 3; + break; + case Duration.OneHundredTwentyEighth: + line = 6; + dotLine = 3; + break; + case Duration.TwoHundredFiftySixth: + line = 6; + dotLine = 3; + break; + } + let y: number = sr.getScoreY(line, offset); + this.restGlyph = new ScoreRestGlyph(0, y, this.container.beat.duration); + this.restGlyph.beat = this.container.beat; + this.restGlyph.beamingHelper = this.beamingHelper; + this.addGlyph(this.restGlyph); + // + // Note dots + // + if (this.container.beat.dots > 0) { + this.addGlyph(new SpacingGlyph(0, 0, 5 * this.scale)); + for (let i: number = 0; i < this.container.beat.dots; i++) { + let group: GlyphGroup = new GlyphGroup(0, 0); + this.createBeatDot(dotLine, group); + this.addGlyph(group); + } + } + } + } + super.doLayout(); + if (this.container.beat.isEmpty) { + this.centerX = this.width / 2; + } else if (this.container.beat.isRest) { + this.centerX = this.restGlyph!.x + this.restGlyph!.width / 2; + } else { + this.centerX = this.noteHeads!.x + this.noteHeads!.width / 2; + } + } + + private createBeatDot(line: number, group: GlyphGroup): void { + let sr: ScoreBarRenderer = this.renderer as ScoreBarRenderer; + group.addGlyph(new CircleGlyph(0, sr.getScoreY(line, 0), 1.5 * this.scale)); + } + + private static NormalKeys: Map = new Map([ + [32, true], + [34, true], + [35, true], + [36, true], + [38, true], + [39, true], + [40, true], + [41, true], + [43, true], + [45, true], + [47, true], + [48, true], + [50, true], + [55, true], + [56, true], + [58, true], + [60, true], + [61, true] + ]); + private static XKeys: Map = new Map([ + [31, true], + [33, true], + [37, true], + [42, true], + [44, true], + [54, true], + [62, true], + [63, true], + [64, true], + [65, true], + [66, true] + ]); + + private createNoteHeadGlyph(n: Note): EffectGlyph { + let isGrace: boolean = this.container.beat.graceType !== GraceType.None; + if (n.beat.voice.bar.staff.isPercussion) { + let value: number = n.realValue; + if (value <= 30 || value >= 67 || ScoreBeatGlyph.NormalKeys.has(value)) { + return new NoteHeadGlyph(0, 0, Duration.Quarter, isGrace); + } + if (ScoreBeatGlyph.XKeys.has(value)) { + return new DrumSticksGlyph(0, 0, isGrace); + } + if (value === 46) { + return new HiHatGlyph(0, 0, isGrace); + } + if (value === 49 || value === 57) { + return new DiamondNoteHeadGlyph(0, 0, n.beat.duration, isGrace); + } + if (value === 52) { + return new ChineseCymbalGlyph(0, 0, isGrace); + } + if (value === 51 || value === 53 || value === 59) { + return new RideCymbalGlyph(0, 0, isGrace); + } + return new NoteHeadGlyph(0, 0, Duration.Quarter, isGrace); + } + if (n.isDead) { + return new DeadNoteHeadGlyph(0, 0, isGrace); + } + if (n.beat.graceType === GraceType.BendGrace) { + return new NoteHeadGlyph(0, 0, Duration.Quarter, true); + } + if (n.harmonicType === HarmonicType.Natural) { + return new DiamondNoteHeadGlyph(0, 0, n.beat.duration, isGrace); + } + return new NoteHeadGlyph(0, 0, n.beat.duration, isGrace); + } + + private createNoteGlyph(n: Note): void { + if (n.beat.graceType === GraceType.BendGrace && !n.hasBend) { + return; + } + let sr: ScoreBarRenderer = this.renderer as ScoreBarRenderer; + let noteHeadGlyph: EffectGlyph = this.createNoteHeadGlyph(n); + // calculate y position + let line: number = sr.getNoteLine(n); + noteHeadGlyph.y = sr.getScoreY(line, 0); + this.noteHeads!.addNoteGlyph(noteHeadGlyph, n, line); + if (n.harmonicType !== HarmonicType.None && n.harmonicType !== HarmonicType.Natural) { + // create harmonic note head. + let harmonicFret: number = n.displayValue + n.harmonicPitch; + noteHeadGlyph = new DiamondNoteHeadGlyph( + 0, + 0, + n.beat.duration, + this.container.beat.graceType !== GraceType.None + ); + line = sr.accidentalHelper.getNoteLineForValue(harmonicFret, false); + noteHeadGlyph.y = sr.getScoreY(line, 0); + this.noteHeads!.addNoteGlyph(noteHeadGlyph, n, line); + } + if (n.isStaccato && !this.noteHeads!.beatEffects.has('Staccato')) { + this.noteHeads!.beatEffects.set('Staccato', new CircleGlyph(0, 0, 1.5)); + } + if (n.accentuated === AccentuationType.Normal && !this.noteHeads!.beatEffects.has('Accent')) { + this.noteHeads!.beatEffects.set('Accent', new AccentuationGlyph(0, 0, AccentuationType.Normal)); + } + if (n.accentuated === AccentuationType.Heavy && !this.noteHeads!.beatEffects.has('HAccent')) { + this.noteHeads!.beatEffects.set('HAccent', new AccentuationGlyph(0, 0, AccentuationType.Heavy)); + } + } +} diff --git a/src/rendering/glyphs/ScoreBeatPreNotesGlyph.ts b/src/rendering/glyphs/ScoreBeatPreNotesGlyph.ts new file mode 100644 index 000000000..aad1750d5 --- /dev/null +++ b/src/rendering/glyphs/ScoreBeatPreNotesGlyph.ts @@ -0,0 +1,123 @@ +import { AccidentalType } from '@src/model/AccidentalType'; +import { BendType } from '@src/model/BendType'; +import { BrushType } from '@src/model/BrushType'; +import { GraceType } from '@src/model/GraceType'; +import { HarmonicType } from '@src/model/HarmonicType'; +import { Note } from '@src/model/Note'; +import { WhammyType } from '@src/model/WhammyType'; +import { AccidentalGlyph } from '@src/rendering/glyphs/AccidentalGlyph'; +import { AccidentalGroupGlyph } from '@src/rendering/glyphs/AccidentalGroupGlyph'; +import { BeatGlyphBase } from '@src/rendering/glyphs/BeatGlyphBase'; +import { BendNoteHeadGroupGlyph } from '@src/rendering/glyphs/BendNoteHeadGroupGlyph'; +import { GhostNoteContainerGlyph } from '@src/rendering/glyphs/GhostNoteContainerGlyph'; +import { ScoreBrushGlyph } from '@src/rendering/glyphs/ScoreBrushGlyph'; +import { SpacingGlyph } from '@src/rendering/glyphs/SpacingGlyph'; +import { ScoreBarRenderer } from '@src/rendering/ScoreBarRenderer'; +import { NoteHeadGlyph } from '@src/rendering/glyphs/NoteHeadGlyph'; + +export class ScoreBeatPreNotesGlyph extends BeatGlyphBase { + private _prebends: BendNoteHeadGroupGlyph | null = null; + public get prebendNoteHeadOffset(): number { + return this._prebends ? this._prebends.x + this._prebends.noteHeadOffset : 0; + } + + public accidentals: AccidentalGroupGlyph | null = null; + + public doLayout(): void { + if (!this.container.beat.isRest) { + let accidentals: AccidentalGroupGlyph = new AccidentalGroupGlyph(); + let ghost: GhostNoteContainerGlyph = new GhostNoteContainerGlyph(true); + ghost.renderer = this.renderer; + this._prebends = new BendNoteHeadGroupGlyph(this.container.beat, true); + this._prebends.renderer = this.renderer; + for (let note of this.container.beat.notes) { + if (note.isVisible) { + if (note.hasBend) { + switch (note.bendType) { + case BendType.PrebendBend: + case BendType.Prebend: + case BendType.PrebendRelease: + this._prebends.addGlyph( + note.displayValue - ((note.bendPoints[0].value / 2) | 0), + false + ); + break; + } + } else if (note.beat.hasWhammyBar) { + switch (note.beat.whammyBarType) { + case WhammyType.PrediveDive: + case WhammyType.Predive: + this._prebends.addGlyph( + note.displayValue - ((note.beat.whammyBarPoints[0].value / 2) | 0), + false + ); + break; + } + } + this.createAccidentalGlyph(note, accidentals); + ghost.addParenthesis(note); + } + } + if (!this._prebends.isEmpty) { + this.addGlyph(this._prebends); + this.addGlyph( + new SpacingGlyph( + 0, + 0, + 4 * (this.container.beat.graceType !== GraceType.None ? NoteHeadGlyph.GraceScale : 1) * this.scale + ) + ); + } + if (this.container.beat.brushType !== BrushType.None) { + this.addGlyph(new ScoreBrushGlyph(this.container.beat)); + this.addGlyph(new SpacingGlyph(0, 0, 4 * this.scale)); + } + if (!ghost.isEmpty) { + this.addGlyph(ghost); + this.addGlyph( + new SpacingGlyph( + 0, + 0, + 4 * (this.container.beat.graceType !== GraceType.None ? NoteHeadGlyph.GraceScale : 1) * this.scale + ) + ); + } + if (!accidentals.isEmpty) { + this.accidentals = accidentals; + this.addGlyph(accidentals); + this.addGlyph( + new SpacingGlyph( + 0, + 0, + 4 * (this.container.beat.graceType !== GraceType.None ? NoteHeadGlyph.GraceScale : 1) * this.scale + ) + ); + } + } + super.doLayout(); + } + + private createAccidentalGlyph(n: Note, accidentals: AccidentalGroupGlyph): void { + let sr: ScoreBarRenderer = this.renderer as ScoreBarRenderer; + let accidental: AccidentalType = sr.accidentalHelper.applyAccidental(n); + let noteLine: number = sr.getNoteLine(n); + let isGrace: boolean = this.container.beat.graceType !== GraceType.None; + if (accidental !== AccidentalType.None) { + let g = new AccidentalGlyph(0, sr.getScoreY(noteLine, 0), accidental, isGrace); + g.renderer = this.renderer; + accidentals.addGlyph(g); + } + if (n.harmonicType !== HarmonicType.None && n.harmonicType !== HarmonicType.Natural) { + let harmonicFret: number = n.displayValue + n.harmonicPitch; + accidental = sr.accidentalHelper.applyAccidentalForValue(n.beat, harmonicFret, isGrace); + noteLine = sr.accidentalHelper.getNoteLineForValue(harmonicFret, false); + let g = new AccidentalGlyph(0, sr.getScoreY(noteLine, 0), accidental, isGrace); + g.renderer = this.renderer; + accidentals.addGlyph(g); + } + } + + public constructor() { + super(); + } +} diff --git a/src/rendering/glyphs/ScoreBendGlyph.ts b/src/rendering/glyphs/ScoreBendGlyph.ts new file mode 100644 index 000000000..2de5405dd --- /dev/null +++ b/src/rendering/glyphs/ScoreBendGlyph.ts @@ -0,0 +1,355 @@ +import { Beat } from '@src/model/Beat'; +import { BendPoint } from '@src/model/BendPoint'; +import { BendStyle } from '@src/model/BendStyle'; +import { BendType } from '@src/model/BendType'; +import { GraceType } from '@src/model/GraceType'; +import { Note } from '@src/model/Note'; +import { ICanvas } from '@src/platform/ICanvas'; +import { BeatXPosition } from '@src/rendering/BeatXPosition'; +import { BendNoteHeadGroupGlyph } from '@src/rendering/glyphs/BendNoteHeadGroupGlyph'; +import { ScoreBeatPreNotesGlyph } from '@src/rendering/glyphs/ScoreBeatPreNotesGlyph'; +import { ScoreHelperNotesBaseGlyph } from '@src/rendering/glyphs/ScoreHelperNotesBaseGlyph'; +import { TieGlyph } from '@src/rendering/glyphs/TieGlyph'; +import { ScoreBarRenderer } from '@src/rendering/ScoreBarRenderer'; +import { BeamDirection } from '@src/rendering/utils/BeamDirection'; +import { NoteHeadGlyph } from '@src/rendering/glyphs/NoteHeadGlyph'; +import { NoteYPosition } from '../BarRendererBase'; + +export class ScoreBendGlyph extends ScoreHelperNotesBaseGlyph { + private _beat: Beat; + private _notes: Note[] = []; + private _endNoteGlyph: BendNoteHeadGroupGlyph | null = null; + private _middleNoteGlyph: BendNoteHeadGroupGlyph | null = null; + + public constructor(beat: Beat) { + super(); + this._beat = beat; + } + + public addBends(note: Note): void { + this._notes.push(note); + if (note.isTieOrigin) { + return; + } + switch (note.bendType) { + case BendType.Bend: + case BendType.PrebendRelease: + case BendType.PrebendBend: + { + let endGlyphs = this._endNoteGlyph; + if (!endGlyphs) { + endGlyphs = this._endNoteGlyph = new BendNoteHeadGroupGlyph(note.beat, false); + endGlyphs.renderer = this.renderer; + this.BendNoteHeads.push(endGlyphs); + } + let lastBendPoint: BendPoint = note.bendPoints[note.bendPoints.length - 1]; + endGlyphs.addGlyph(this.getBendNoteValue(note, lastBendPoint), lastBendPoint.value % 2 !== 0); + } + break; + case BendType.Release: + { + if (!note.isTieOrigin) { + let endGlyphs = this._endNoteGlyph; + if (!endGlyphs) { + endGlyphs = this._endNoteGlyph = new BendNoteHeadGroupGlyph(note.beat, false); + endGlyphs.renderer = this.renderer; + this.BendNoteHeads.push(endGlyphs); + } + let lastBendPoint: BendPoint = note.bendPoints[note.bendPoints.length - 1]; + endGlyphs.addGlyph(this.getBendNoteValue(note, lastBendPoint), lastBendPoint.value % 2 !== 0); + } + } + break; + case BendType.BendRelease: + { + let middleGlyphs = this._middleNoteGlyph; + if (!middleGlyphs) { + middleGlyphs = this._middleNoteGlyph = new BendNoteHeadGroupGlyph(note.beat, false); + middleGlyphs.renderer = this.renderer; + this.BendNoteHeads.push(middleGlyphs); + } + let middleBendPoint: BendPoint = note.bendPoints[1]; + middleGlyphs.addGlyph( + this.getBendNoteValue(note, note.bendPoints[1]), + middleBendPoint.value % 2 !== 0 + ); + let endGlyphs = this._endNoteGlyph; + if (!endGlyphs) { + endGlyphs = this._endNoteGlyph = new BendNoteHeadGroupGlyph(note.beat, false); + endGlyphs.renderer = this.renderer; + this.BendNoteHeads.push(endGlyphs); + } + let lastBendPoint: BendPoint = note.bendPoints[note.bendPoints.length - 1]; + endGlyphs.addGlyph(this.getBendNoteValue(note, lastBendPoint), lastBendPoint.value % 2 !== 0); + } + break; + } + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + // Draw note heads + let startNoteRenderer: ScoreBarRenderer = this.renderer.scoreRenderer.layout!.getRendererForBar( + this.renderer.staff.staveId, + this._beat.voice.bar + )! as ScoreBarRenderer; + let startX: number = + cx + startNoteRenderer.x + startNoteRenderer.getBeatX(this._beat, BeatXPosition.MiddleNotes); + let endBeatX: number = cx + startNoteRenderer.x; + if (this._beat.isLastOfVoice) { + endBeatX += startNoteRenderer.postBeatGlyphsStart; + } else { + endBeatX += startNoteRenderer.getBeatX(this._beat.nextBeat!, BeatXPosition.PreNotes); + } + endBeatX -= 8 * this.scale; + let middleX: number = (startX + endBeatX) / 2; + if (this._middleNoteGlyph) { + this._middleNoteGlyph.x = middleX - this._middleNoteGlyph.noteHeadOffset; + this._middleNoteGlyph.y = cy + startNoteRenderer.y; + this._middleNoteGlyph.paint(0, 0, canvas); + } + if (this._endNoteGlyph) { + this._endNoteGlyph.x = endBeatX - this._endNoteGlyph.noteHeadOffset; + this._endNoteGlyph.y = cy + startNoteRenderer.y; + this._endNoteGlyph.paint(0, 0, canvas); + } + this._notes.sort((a, b) => { + return b.displayValue - a.displayValue; + }); + let directionBeat: Beat = this._beat.graceType === GraceType.BendGrace ? this._beat.nextBeat! : this._beat; + let direction: BeamDirection = + this._notes.length === 1 ? this.getBeamDirection(directionBeat, startNoteRenderer) : BeamDirection.Up; + // draw slurs + for (let i: number = 0; i < this._notes.length; i++) { + let note: Note = this._notes[i]; + if (i > 0 && i >= ((this._notes.length / 2) | 0)) { + direction = BeamDirection.Down; + } + let startY: number = cy + startNoteRenderer.y + startNoteRenderer.getNoteY(note, NoteYPosition.Top); + let heightOffset: number = NoteHeadGlyph.NoteHeadHeight * this.scale * NoteHeadGlyph.GraceScale * 0.5; + if (direction === BeamDirection.Down) { + startY += NoteHeadGlyph.NoteHeadHeight * this.scale; + } + let slurText: string = note.bendStyle === BendStyle.Gradual ? 'grad.' : ''; + if (note.isTieOrigin) { + let endNote: Note = note.tieDestination!; + let endNoteRenderer: ScoreBarRenderer | null = !endNote + ? null + : this.renderer.scoreRenderer.layout!.getRendererForBar( + this.renderer.staff.staveId, + endNote.beat.voice.bar + ) as ScoreBarRenderer; + // if we have a line break we draw only a line until the end + if (!endNoteRenderer || endNoteRenderer.staff !== startNoteRenderer.staff) { + let endX: number = cx + startNoteRenderer.x + startNoteRenderer.width; + let noteValueToDraw: number = note.tieDestination!.realValue; + startNoteRenderer.accidentalHelper.applyAccidentalForValue(note.beat, noteValueToDraw, false); + let endY: number = + cy + + startNoteRenderer.y + + startNoteRenderer.getScoreY( + startNoteRenderer.accidentalHelper.getNoteLineForValue(noteValueToDraw, false), + 0 + ); + if (note.bendType === BendType.Hold || note.bendType === BendType.Prebend) { + TieGlyph.paintTie( + canvas, + this.scale, + startX, + startY, + endX, + endY, + direction === BeamDirection.Down, + 22, + 4 + ); + canvas.fill(); + } else { + this.drawBendSlur( + canvas, + startX, + startY, + endX, + endY, + direction === BeamDirection.Down, + this.scale, + slurText + ); + } + } else { + let endX: number = + cx + endNoteRenderer.x + endNoteRenderer.getBeatX(endNote.beat, BeatXPosition.MiddleNotes); + let endY: number = cy + endNoteRenderer.y + endNoteRenderer.getNoteY(endNote, NoteYPosition.Top); + if (direction === BeamDirection.Down) { + endY += NoteHeadGlyph.NoteHeadHeight * this.scale; + } + if (note.bendType === BendType.Hold || note.bendType === BendType.Prebend) { + TieGlyph.paintTie( + canvas, + this.scale, + startX, + startY, + endX, + endY, + direction === BeamDirection.Down, + 22, + 4 + ); + canvas.fill(); + } else { + this.drawBendSlur( + canvas, + startX, + startY, + endX, + endY, + direction === BeamDirection.Down, + this.scale, + slurText + ); + } + } + switch (note.bendType) { + case BendType.Prebend: + case BendType.PrebendBend: + case BendType.PrebendRelease: + let preX: number = + cx + startNoteRenderer.x + startNoteRenderer.getBeatX(note.beat, BeatXPosition.PreNotes); + preX += (startNoteRenderer.getBeatContainer(note.beat).preNotes as ScoreBeatPreNotesGlyph) + .prebendNoteHeadOffset; + let preY: number = + cy + + startNoteRenderer.y + + startNoteRenderer.getScoreY( + startNoteRenderer.accidentalHelper.getNoteLineForValue( + note.displayValue - ((note.bendPoints[0].value / 2) | 0), + false + ), + 0 + ) + + heightOffset; + this.drawBendSlur( + canvas, + preX, + preY, + startX, + startY, + direction === BeamDirection.Down, + this.scale + ); + break; + } + } else { + if (direction === BeamDirection.Up) { + heightOffset = -heightOffset; + } + let endValue: number = 0; + let endY: number = 0; + switch (note.bendType) { + case BendType.Bend: + endValue = this.getBendNoteValue(note, note.bendPoints[note.bendPoints.length - 1]); + endY = this._endNoteGlyph!.getNoteValueY(endValue) + heightOffset; + this.drawBendSlur( + canvas, + startX, + startY, + endBeatX, + endY, + direction === BeamDirection.Down, + this.scale, + slurText + ); + break; + case BendType.BendRelease: + let middleValue: number = this.getBendNoteValue(note, note.bendPoints[1]); + let middleY: number = this._middleNoteGlyph!.getNoteValueY(middleValue) + heightOffset; + this.drawBendSlur( + canvas, + startX, + startY, + middleX, + middleY, + direction === BeamDirection.Down, + this.scale, + slurText + ); + endValue = this.getBendNoteValue(note, note.bendPoints[note.bendPoints.length - 1]); + endY = this._endNoteGlyph!.getNoteValueY(endValue) + heightOffset; + this.drawBendSlur( + canvas, + middleX, + middleY, + endBeatX, + endY, + direction === BeamDirection.Down, + this.scale, + slurText + ); + break; + case BendType.Release: + if (this.BendNoteHeads.length > 0) { + endValue = this.getBendNoteValue(note, note.bendPoints[note.bendPoints.length - 1]); + endY = this.BendNoteHeads[0].getNoteValueY(endValue) + heightOffset; + this.drawBendSlur( + canvas, + startX, + startY, + endBeatX, + endY, + direction === BeamDirection.Down, + this.scale, + slurText + ); + } + break; + case BendType.Prebend: + case BendType.PrebendBend: + case BendType.PrebendRelease: + let preX: number = + cx + startNoteRenderer.x + startNoteRenderer.getBeatX(note.beat, BeatXPosition.PreNotes); + preX += (startNoteRenderer.getBeatContainer(note.beat).preNotes as ScoreBeatPreNotesGlyph) + .prebendNoteHeadOffset; + let preY: number = + cy + + startNoteRenderer.y + + startNoteRenderer.getScoreY( + startNoteRenderer.accidentalHelper.getNoteLineForValue( + note.displayValue - ((note.bendPoints[0].value / 2) | 0), + false + ), + 0 + ) + + heightOffset; + this.drawBendSlur( + canvas, + preX, + preY, + startX, + startY, + direction === BeamDirection.Down, + this.scale + ); + if (this.BendNoteHeads.length > 0) { + endValue = this.getBendNoteValue(note, note.bendPoints[note.bendPoints.length - 1]); + endY = this.BendNoteHeads[0].getNoteValueY(endValue) + heightOffset; + this.drawBendSlur( + canvas, + startX, + startY, + endBeatX, + endY, + direction === BeamDirection.Down, + this.scale, + slurText + ); + } + break; + } + } + } + } + + private getBendNoteValue(note: Note, bendPoint: BendPoint): number { + // NOTE: bendpoints are in 1/4 tones, but the note values are in 1/2 notes. + return note.displayValueWithoutBend + ((bendPoint.value / 2) | 0); + } +} diff --git a/src/rendering/glyphs/ScoreBrushGlyph.ts b/src/rendering/glyphs/ScoreBrushGlyph.ts new file mode 100644 index 000000000..a4c41ec2b --- /dev/null +++ b/src/rendering/glyphs/ScoreBrushGlyph.ts @@ -0,0 +1,65 @@ +import { Beat } from '@src/model/Beat'; +import { BrushType } from '@src/model/BrushType'; +import { VibratoType } from '@src/model/VibratoType'; +import { ICanvas } from '@src/platform/ICanvas'; +import { Glyph } from '@src/rendering/glyphs/Glyph'; +import { NoteVibratoGlyph } from '@src/rendering/glyphs/NoteVibratoGlyph'; +import { ScoreBarRenderer } from '@src/rendering/ScoreBarRenderer'; +import { NoteYPosition } from '../BarRendererBase'; + +export class ScoreBrushGlyph extends Glyph { + private _beat: Beat; + + public constructor(beat: Beat) { + super(0, 0); + this._beat = beat; + } + + public doLayout(): void { + this.width = 10 * this.scale; + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + let scoreBarRenderer: ScoreBarRenderer = this.renderer as ScoreBarRenderer; + let lineSize: number = scoreBarRenderer.lineOffset; + let startY: number = cy + this.y + (scoreBarRenderer.getNoteY(this._beat.maxNote!, NoteYPosition.Bottom) - lineSize); + let endY: number = cy + this.y + scoreBarRenderer.getNoteY(this._beat.minNote!, NoteYPosition.Top) + lineSize; + let arrowX: number = cx + this.x + this.width / 2; + let arrowSize: number = 8 * this.scale; + if (this._beat.brushType !== BrushType.None) { + if (this._beat.brushType === BrushType.ArpeggioUp) { + let lineStartY: number = startY + arrowSize; + let lineEndY: number = endY - arrowSize; + canvas.beginRotate(cx + this.x + 5 * this.scale, lineEndY, -90); + let glyph: NoteVibratoGlyph = new NoteVibratoGlyph(0, 0, VibratoType.Slight, 1.2, true); + glyph.renderer = this.renderer; + glyph.doLayout(); + glyph.width = Math.abs(lineEndY - lineStartY); + glyph.paint(0, 0, canvas); + canvas.endRotate(); + canvas.beginPath(); + canvas.moveTo(arrowX, endY); + canvas.lineTo(arrowX + arrowSize / 2, endY - arrowSize); + canvas.lineTo(arrowX - arrowSize / 2, endY - arrowSize); + canvas.closePath(); + canvas.fill(); + } else if (this._beat.brushType === BrushType.ArpeggioDown) { + let lineStartY: number = startY + arrowSize; + let lineEndY: number = endY; + canvas.beginRotate(cx + this.x + 5 * this.scale, lineStartY, 90); + let glyph: NoteVibratoGlyph = new NoteVibratoGlyph(0, 0, VibratoType.Slight, 1.2, true); + glyph.renderer = this.renderer; + glyph.doLayout(); + glyph.width = Math.abs(lineEndY - lineStartY); + glyph.paint(0, 0, canvas); + canvas.endRotate(); + canvas.beginPath(); + canvas.moveTo(arrowX, startY); + canvas.lineTo(arrowX + arrowSize / 2, startY + arrowSize); + canvas.lineTo(arrowX - arrowSize / 2, startY + arrowSize); + canvas.closePath(); + canvas.fill(); + } + } + } +} diff --git a/src/rendering/glyphs/ScoreHelperNotesBaseGlyph.ts b/src/rendering/glyphs/ScoreHelperNotesBaseGlyph.ts new file mode 100644 index 000000000..7f885647e --- /dev/null +++ b/src/rendering/glyphs/ScoreHelperNotesBaseGlyph.ts @@ -0,0 +1,44 @@ +import { Beat } from '@src/model/Beat'; +import { ICanvas } from '@src/platform/ICanvas'; +import { BendNoteHeadGroupGlyph } from '@src/rendering/glyphs/BendNoteHeadGroupGlyph'; +import { Glyph } from '@src/rendering/glyphs/Glyph'; +import { ScoreBarRenderer } from '@src/rendering/ScoreBarRenderer'; +import { BeamDirection } from '@src/rendering/utils/BeamDirection'; +import { TieGlyph } from './TieGlyph'; + +export class ScoreHelperNotesBaseGlyph extends Glyph { + public static readonly EndPadding: number = ((10 / 2) | 0) + 3; + protected BendNoteHeads: BendNoteHeadGroupGlyph[] = []; + + protected drawBendSlur( + canvas: ICanvas, + x1: number, + y1: number, + x2: number, + y2: number, + down: boolean, + scale: number, + slurText?: string + ): void { + TieGlyph.drawBendSlur(canvas, x1, y1, x2, y2, down, scale, slurText); + } + + public doLayout(): void { + super.doLayout(); + this.width = 0; + for (let noteHeads of this.BendNoteHeads) { + noteHeads.doLayout(); + this.width += noteHeads.width + 10 * this.scale; + } + } + + protected getBeamDirection(beat: Beat, noteRenderer: ScoreBarRenderer): BeamDirection { + // invert direction (if stems go up, ties go down to not cross them) + switch (noteRenderer.getBeatDirection(beat)) { + case BeamDirection.Up: + return BeamDirection.Down; + default: + return BeamDirection.Up; + } + } +} diff --git a/src/rendering/glyphs/ScoreLegatoGlyph.ts b/src/rendering/glyphs/ScoreLegatoGlyph.ts new file mode 100644 index 000000000..726aa334e --- /dev/null +++ b/src/rendering/glyphs/ScoreLegatoGlyph.ts @@ -0,0 +1,104 @@ +import { Beat } from '@src/model/Beat'; +import { BarRendererBase, NoteYPosition } from '@src/rendering/BarRendererBase'; +import { BeatXPosition } from '@src/rendering/BeatXPosition'; +import { TieGlyph } from '@src/rendering/glyphs/TieGlyph'; +import { ScoreBarRenderer } from '@src/rendering/ScoreBarRenderer'; +import { BeamDirection } from '@src/rendering/utils/BeamDirection'; +import { Duration } from '@src/model/Duration'; +import { GraceType } from '@src/model/GraceType'; + +export class ScoreLegatoGlyph extends TieGlyph { + public constructor(startBeat: Beat, endBeat: Beat, forEnd: boolean = false) { + super(startBeat, endBeat, forEnd); + } + + public doLayout(): void { + super.doLayout(); + } + + protected getBeamDirection(beat: Beat, noteRenderer: BarRendererBase): BeamDirection { + if (beat.isRest) { + return BeamDirection.Up; + } + // invert direction (if stems go up, ties go down to not cross them) + switch ((noteRenderer as ScoreBarRenderer).getBeatDirection(beat)) { + case BeamDirection.Up: + return BeamDirection.Down; + default: + return BeamDirection.Up; + } + } + + protected getStartY(): number { + if (this.startBeat!.isRest) { + // below all lines + return (this.startNoteRenderer as ScoreBarRenderer).getScoreY(9, 0); + } + switch (this.tieDirection) { + case BeamDirection.Up: + // below lowest note + return this.startNoteRenderer!.getNoteY(this.startBeat!.maxNote!, NoteYPosition.Top); + default: + return this.startNoteRenderer!.getNoteY(this.startBeat!.minNote!, NoteYPosition.Bottom); + } + } + + protected getEndY(): number { + const endNoteScoreRenderer = this.endNoteRenderer as ScoreBarRenderer; + if (this.endBeat!.isRest) { + switch (this.tieDirection) { + case BeamDirection.Up: + return endNoteScoreRenderer.getScoreY(9, 0); + default: + return endNoteScoreRenderer.getScoreY(0, 0); + } + } + + const startBeamDirection = (this.startNoteRenderer as ScoreBarRenderer).getBeatDirection(this.startBeat!); + const endBeamDirection = endNoteScoreRenderer.getBeatDirection(this.endBeat!); + + if (startBeamDirection !== endBeamDirection && this.startBeat!.graceType === GraceType.None) { + if (endBeamDirection === this.tieDirection) { + switch (this.tieDirection) { + case BeamDirection.Up: + // stem upper end + return endNoteScoreRenderer.getNoteY(this.endBeat!.maxNote!, NoteYPosition.TopWithStem); + default: + // stem lower end + return endNoteScoreRenderer.getNoteY(this.endBeat!.minNote!, NoteYPosition.BottomWithStem); + } + } else { + switch (this.tieDirection) { + case BeamDirection.Up: + // stem upper end + return endNoteScoreRenderer.getNoteY(this.endBeat!.maxNote!, NoteYPosition.BottomWithStem); + default: + // stem lower end + return endNoteScoreRenderer.getNoteY(this.endBeat!.minNote!, NoteYPosition.TopWithStem); + } + } + } + + switch (this.tieDirection) { + case BeamDirection.Up: + // below lowest note + return endNoteScoreRenderer.getNoteY(this.endBeat!.maxNote!, NoteYPosition.Top); + default: + // above highest note + return endNoteScoreRenderer.getNoteY(this.endBeat!.minNote!, NoteYPosition.Bottom); + } + } + + protected getStartX(): number { + return this.startNoteRenderer!.getBeatX(this.startBeat!, BeatXPosition.MiddleNotes); + } + + protected getEndX(): number { + const endBeamDirection = (this.endNoteRenderer as ScoreBarRenderer).getBeatDirection(this.endBeat!); + return this.endNoteRenderer!.getBeatX( + this.endBeat!, + this.endBeat!.duration > Duration.Whole && + endBeamDirection === this.tieDirection ? BeatXPosition.Stem : BeatXPosition.MiddleNotes + ); + } +} diff --git a/src/rendering/glyphs/ScoreNoteChordGlyph.ts b/src/rendering/glyphs/ScoreNoteChordGlyph.ts new file mode 100644 index 000000000..7bedd8120 --- /dev/null +++ b/src/rendering/glyphs/ScoreNoteChordGlyph.ts @@ -0,0 +1,174 @@ +import { Beat } from '@src/model/Beat'; +import { Duration } from '@src/model/Duration'; +import { Note } from '@src/model/Note'; +import { ICanvas } from '@src/platform/ICanvas'; +import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph'; +import { Glyph } from '@src/rendering/glyphs/Glyph'; +import { ScoreNoteChordGlyphBase } from '@src/rendering/glyphs/ScoreNoteChordGlyphBase'; +import { ScoreNoteGlyphInfo } from '@src/rendering/glyphs/ScoreNoteGlyphInfo'; +import { TremoloPickingGlyph } from '@src/rendering/glyphs/TremoloPickingGlyph'; +import { ScoreBarRenderer } from '@src/rendering/ScoreBarRenderer'; +import { BeamDirection } from '@src/rendering/utils/BeamDirection'; +import { BeamingHelper } from '@src/rendering/utils/BeamingHelper'; +import { Bounds } from '@src/rendering/utils/Bounds'; +import { NoteBounds } from '@src/rendering/utils/NoteBounds'; +import { NoteHeadGlyph } from '@src/rendering/glyphs/NoteHeadGlyph'; +import { NoteXPosition, NoteYPosition } from '../BarRendererBase'; +import { BeatBounds } from '../utils/BeatBounds'; + +export class ScoreNoteChordGlyph extends ScoreNoteChordGlyphBase { + private _noteGlyphLookup: Map = new Map(); + private _notes: Note[] = []; + private _tremoloPicking: Glyph | null = null; + + public beatEffects: Map = new Map(); + public beat!: Beat; + public beamingHelper!: BeamingHelper; + + public constructor() { + super(); + } + + public get direction(): BeamDirection { + return this.beamingHelper.direction; + } + + public getNoteX(note: Note, requestedPosition: NoteXPosition): number { + if (this._noteGlyphLookup.has(note.id)) { + let n = this._noteGlyphLookup.get(note.id)!; + + let pos = this.x + n.x + this._noteHeadPadding; + switch (requestedPosition) { + case NoteXPosition.Left: + break; + case NoteXPosition.Center: + pos += n.width / 2; + break; + case NoteXPosition.Right: + pos += n.width; + break; + } + return pos; + } + return 0; + } + + public getNoteY(note: Note, requestedPosition: NoteYPosition): number { + if (this._noteGlyphLookup.has(note.id)) { + const n = this._noteGlyphLookup.get(note.id)!; + let pos = this.y + n.y; + + switch (requestedPosition) { + case NoteYPosition.TopWithStem: + pos -= (this.renderer as ScoreBarRenderer).getStemSize(this.beamingHelper); + break; + case NoteYPosition.Top: + pos -= n.height / 2; + break; + case NoteYPosition.Center: + break; + case NoteYPosition.Bottom: + pos += n.height / 2; + break; + case NoteYPosition.BottomWithStem: + pos += (this.renderer as ScoreBarRenderer).getStemSize(this.beamingHelper); + break; + } + + return pos; + } + return 0; + } + + public addNoteGlyph(noteGlyph: EffectGlyph, note: Note, noteLine: number): void { + super.add(noteGlyph, noteLine); + this._noteGlyphLookup.set(note.id, noteGlyph); + this._notes.push(note); + } + + public updateBeamingHelper(cx: number): void { + if (this.beamingHelper) { + this.beamingHelper.registerBeatLineX( + 'score', + this.beat, + cx + this.x + this.upLineX, + cx + this.x + this.downLineX + ); + } + } + + public doLayout(): void { + super.doLayout(); + let direction: BeamDirection = this.direction; + this.beatEffects.forEach(effect => { + effect.renderer = this.renderer; + effect.doLayout(); + }); + if (this.beat.isTremolo) { + let offset: number = 0; + let baseNote: ScoreNoteGlyphInfo = direction === BeamDirection.Up ? this.minNote! : this.maxNote!; + let tremoloX: number = direction === BeamDirection.Up ? this.displacedX : 0; + let speed: Duration = this.beat.tremoloSpeed!; + switch (speed) { + case Duration.ThirtySecond: + offset = direction === BeamDirection.Up ? -15 : 15; + break; + case Duration.Sixteenth: + offset = direction === BeamDirection.Up ? -12 : 15; + break; + case Duration.Eighth: + offset = direction === BeamDirection.Up ? -10 : 10; + break; + default: + offset = direction === BeamDirection.Up ? -10 : 15; + break; + } + this._tremoloPicking = new TremoloPickingGlyph(tremoloX, baseNote.glyph.y + offset * this.scale, speed); + this._tremoloPicking.renderer = this.renderer; + this._tremoloPicking.doLayout(); + } + } + + + public buildBoundingsLookup(beatBounds:BeatBounds, cx:number, cy:number) { + for (let note of this._notes) { + if (this._noteGlyphLookup.has(note.id)) { + let glyph: EffectGlyph = this._noteGlyphLookup.get(note.id)!; + let noteBounds: NoteBounds = new NoteBounds(); + noteBounds.note = note; + noteBounds.noteHeadBounds = new Bounds(); + noteBounds.noteHeadBounds.x = cx + this.x + this._noteHeadPadding + glyph.x; + noteBounds.noteHeadBounds.y = cy + this.y + glyph.y - glyph.height / 2; + noteBounds.noteHeadBounds.w = glyph.width; + noteBounds.noteHeadBounds.h = glyph.height; + beatBounds.addNote(noteBounds); + } + } + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + // TODO: this method seems to be quite heavy according to the profiler, why? + let scoreRenderer: ScoreBarRenderer = this.renderer as ScoreBarRenderer; + // + // Note Effects only painted once + // + let effectY: number = + this.beamingHelper.direction === BeamDirection.Up + ? scoreRenderer.getScoreY(this.maxNote!.line, 1.5 * NoteHeadGlyph.NoteHeadHeight) + : scoreRenderer.getScoreY(this.minNote!.line, -1.0 * NoteHeadGlyph.NoteHeadHeight); + // TODO: take care of actual glyph height + let effectSpacing: number = + this.beamingHelper.direction === BeamDirection.Up ? 7 * this.scale : -7 * this.scale; + + this.beatEffects.forEach(g => { + g.y = effectY; + g.x = this.width / 2; + g.paint(cx + this.x, cy + this.y, canvas); + effectY += effectSpacing; + }); + super.paint(cx, cy, canvas); + if (this._tremoloPicking) { + this._tremoloPicking.paint(cx, cy, canvas); + } + } +} diff --git a/src/rendering/glyphs/ScoreNoteChordGlyphBase.ts b/src/rendering/glyphs/ScoreNoteChordGlyphBase.ts new file mode 100644 index 000000000..0f88ec83f --- /dev/null +++ b/src/rendering/glyphs/ScoreNoteChordGlyphBase.ts @@ -0,0 +1,143 @@ +import { EventEmitter, IEventEmitter } from '@src/EventEmitter'; +import { Color } from '@src/model/Color'; +import { ICanvas } from '@src/platform/ICanvas'; +import { Glyph } from '@src/rendering/glyphs/Glyph'; +import { ScoreNoteGlyphInfo } from '@src/rendering/glyphs/ScoreNoteGlyphInfo'; +import { ScoreBarRenderer } from '@src/rendering/ScoreBarRenderer'; +import { BeamDirection } from '@src/rendering/utils/BeamDirection'; + +export abstract class ScoreNoteChordGlyphBase extends Glyph { + private _infos: ScoreNoteGlyphInfo[] = []; + protected _noteHeadPadding: number = 0; + + public minNote: ScoreNoteGlyphInfo | null = null; + public maxNote: ScoreNoteGlyphInfo | null = null; + public spacingChanged: IEventEmitter = new EventEmitter(); + public upLineX: number = 0; + public downLineX: number = 0; + public displacedX: number = 0; + public noteStartX: number = 0; + + public constructor() { + super(0, 0); + } + + public abstract get direction(): BeamDirection; + + protected add(noteGlyph: Glyph, noteLine: number): void { + let info: ScoreNoteGlyphInfo = new ScoreNoteGlyphInfo(noteGlyph, noteLine); + this._infos.push(info); + if (!this.minNote || this.minNote.line > info.line) { + this.minNote = info; + } + if (!this.maxNote || this.maxNote.line < info.line) { + this.maxNote = info; + } + } + + public get hasTopOverflow(): boolean { + return !!this.minNote && this.minNote.line <= 0; + } + + public get hasBottomOverflow(): boolean { + return !!this.maxNote && this.maxNote.line > 8; + } + + public doLayout(): void { + this._infos.sort((a, b) => { + return b.line - a.line; + }); + let displacedX: number = 0; + let lastDisplaced: boolean = false; + let lastLine: number = 0; + let anyDisplaced: boolean = false; + let direction: BeamDirection = this.direction; + let lineOffset: number = this.scale * 1.5; + let w: number = 0; + for (let i: number = 0, j: number = this._infos.length; i < j; i++) { + let g: Glyph = this._infos[i].glyph; + g.renderer = this.renderer; + g.doLayout(); + let displace: boolean = false; + if (i === 0) { + displacedX = g.width - lineOffset; + } else { + // check if note needs to be repositioned + if (Math.abs(lastLine - this._infos[i].line) <= 1) { + // reposition if needed + if (!lastDisplaced) { + displace = true; + g.x = displacedX; + anyDisplaced = true; + lastDisplaced = true; // let next iteration know we are displace now + } else { + lastDisplaced = false; // let next iteration know that we weren't displaced now + } + } else { + lastDisplaced = false; + } + } + // for beat direction down we invert the displacement. + // this means: displaced is on the left side of the stem and not displaced is right + if (direction === BeamDirection.Down) { + g.x = displace ? 0 : displacedX; + } else { + g.x = displace ? displacedX : 0; + } + g.x += this.noteStartX; + lastLine = this._infos[i].line; + w = Math.max(w, g.x + g.width - lineOffset); + } + if (anyDisplaced) { + this._noteHeadPadding = 0; + this.upLineX = displacedX; + this.downLineX = displacedX; + } else { + this._noteHeadPadding = direction === BeamDirection.Down ? -displacedX : 0; + w += this._noteHeadPadding; + this.upLineX = w; + this.downLineX = 0; + } + this.displacedX = displacedX; + this.width = w; + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + cx += this.x; + cy += this.y; + // TODO: this method seems to be quite heavy according to the profiler, why? + let scoreRenderer: ScoreBarRenderer = this.renderer as ScoreBarRenderer; + // TODO: Take care of beateffects in overflow + let linePadding: number = 3 * this.scale; + let lineWidth: number = this.width - this.noteStartX + linePadding * 2; + if (this.hasTopOverflow) { + let color: Color = canvas.color; + canvas.color = scoreRenderer.resources.staffLineColor; + let l: number = 0; + while (l >= this.minNote!.line) { + // + 1 Because we want to place the line in the center of the note, not at the top + let lY: number = cy + scoreRenderer.getScoreY(l, 0); + canvas.fillRect(cx - linePadding + this.noteStartX, lY, lineWidth, this.scale); + l -= 2; + } + canvas.color = color; + } + if (this.hasBottomOverflow) { + let color: Color = canvas.color; + canvas.color = scoreRenderer.resources.staffLineColor; + let l: number = 12; + while (l <= this.maxNote!.line) { + let lY: number = cy + scoreRenderer.getScoreY(l, 0); + canvas.fillRect(cx - linePadding + this.noteStartX, lY, lineWidth, this.scale); + l += 2; + } + canvas.color = color; + } + let infos: ScoreNoteGlyphInfo[] = this._infos; + let x: number = cx + this._noteHeadPadding; + for (let g of infos) { + g.glyph.renderer = this.renderer; + g.glyph.paint(x, cy, canvas); + } + } +} diff --git a/src/rendering/glyphs/ScoreNoteGlyphInfo.ts b/src/rendering/glyphs/ScoreNoteGlyphInfo.ts new file mode 100644 index 000000000..8eb559999 --- /dev/null +++ b/src/rendering/glyphs/ScoreNoteGlyphInfo.ts @@ -0,0 +1,11 @@ +import { Glyph } from '@src/rendering/glyphs/Glyph'; + +export class ScoreNoteGlyphInfo { + public glyph: Glyph; + public line: number = 0; + + public constructor(glyph: Glyph, line: number) { + this.glyph = glyph; + this.line = line; + } +} diff --git a/src/rendering/glyphs/ScoreRestGlyph.ts b/src/rendering/glyphs/ScoreRestGlyph.ts new file mode 100644 index 000000000..5f293301b --- /dev/null +++ b/src/rendering/glyphs/ScoreRestGlyph.ts @@ -0,0 +1,79 @@ +import { Duration } from '@src/model/Duration'; +import { MusicFontGlyph } from '@src/rendering/glyphs/MusicFontGlyph'; +import { MusicFontSymbol } from '@src/rendering/glyphs/MusicFontSymbol'; +import { BeamingHelper } from '@src/rendering/utils/BeamingHelper'; + +export class ScoreRestGlyph extends MusicFontGlyph { + private _duration: Duration; + public beamingHelper!: BeamingHelper; + + public constructor(x: number, y: number, duration: Duration) { + super(x, y, 1, ScoreRestGlyph.getSymbol(duration)); + this._duration = duration; + } + + public static getSymbol(duration: Duration): MusicFontSymbol { + switch (duration) { + case Duration.QuadrupleWhole: + return MusicFontSymbol.RestQuadrupleWhole; + case Duration.DoubleWhole: + return MusicFontSymbol.RestDoubleWhole; + case Duration.Whole: + return MusicFontSymbol.RestWhole; + case Duration.Half: + return MusicFontSymbol.RestHalf; + case Duration.Quarter: + return MusicFontSymbol.RestQuarter; + case Duration.Eighth: + return MusicFontSymbol.RestEighth; + case Duration.Sixteenth: + return MusicFontSymbol.RestSixteenth; + case Duration.ThirtySecond: + return MusicFontSymbol.RestThirtySecond; + case Duration.SixtyFourth: + return MusicFontSymbol.RestSixtyFourth; + case Duration.OneHundredTwentyEighth: + return MusicFontSymbol.RestOneHundredTwentyEighth; + case Duration.TwoHundredFiftySixth: + return MusicFontSymbol.RestTwoHundredFiftySixth; + default: + return MusicFontSymbol.None; + } + } + + public static getSize(duration: Duration): number { + switch (duration) { + case Duration.QuadrupleWhole: + case Duration.DoubleWhole: + case Duration.Whole: + case Duration.Half: + case Duration.Quarter: + case Duration.Eighth: + case Duration.Sixteenth: + return 9; + case Duration.ThirtySecond: + return 12; + case Duration.SixtyFourth: + return 14; + case Duration.OneHundredTwentyEighth: + case Duration.TwoHundredFiftySixth: + return 20; + } + return 10; + } + + public doLayout(): void { + this.width = ScoreRestGlyph.getSize(this._duration) * this.scale; + } + + public updateBeamingHelper(cx: number): void { + if (this.beamingHelper) { + this.beamingHelper.registerBeatLineX( + 'score', + this.beat, + cx + this.x + this.width / 2, + cx + this.x + this.width / 2 + ); + } + } +} diff --git a/src/rendering/glyphs/ScoreSlideLineGlyph.ts b/src/rendering/glyphs/ScoreSlideLineGlyph.ts new file mode 100644 index 000000000..a906c6cad --- /dev/null +++ b/src/rendering/glyphs/ScoreSlideLineGlyph.ts @@ -0,0 +1,197 @@ +import { Beat } from '@src/model/Beat'; +import { Note } from '@src/model/Note'; +import { SlideInType } from '@src/model/SlideInType'; +import { SlideOutType } from '@src/model/SlideOutType'; +import { VibratoType } from '@src/model/VibratoType'; +import { ICanvas } from '@src/platform/ICanvas'; +import { BarRendererBase, NoteXPosition, NoteYPosition } from '@src/rendering/BarRendererBase'; +import { BeatXPosition } from '@src/rendering/BeatXPosition'; +import { BeatContainerGlyph } from '@src/rendering/glyphs/BeatContainerGlyph'; +import { Glyph } from '@src/rendering/glyphs/Glyph'; +import { NoteVibratoGlyph } from '@src/rendering/glyphs/NoteVibratoGlyph'; +import { ScoreBeatPreNotesGlyph } from '@src/rendering/glyphs/ScoreBeatPreNotesGlyph'; +import { ScoreBarRenderer } from '@src/rendering/ScoreBarRenderer'; + +export class ScoreSlideLineGlyph extends Glyph { + private _outType: SlideOutType; + private _inType: SlideInType; + private _startNote: Note; + private _parent: BeatContainerGlyph; + + public constructor(inType: SlideInType, outType: SlideOutType, startNote: Note, parent: BeatContainerGlyph) { + super(0, 0); + this._outType = outType; + this._inType = inType; + this._startNote = startNote; + this._parent = parent; + } + + public doLayout(): void { + this.width = 0; + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + this.paintSlideIn(cx, cy, canvas); + this.drawSlideOut(cx, cy, canvas); + } + + private paintSlideIn(cx: number, cy: number, canvas: ICanvas): void { + let startNoteRenderer: ScoreBarRenderer = this.renderer as ScoreBarRenderer; + let sizeX: number = 12 * this.scale; + let endX = cx + startNoteRenderer.x + startNoteRenderer.getNoteX(this._startNote, NoteXPosition.Left) - 2 * this.scale; + let endY = cy + startNoteRenderer.y + startNoteRenderer.getNoteY(this._startNote, NoteYPosition.Center); + let startX = endX - sizeX; + let startY: number = cy + startNoteRenderer.y; + + switch (this._inType) { + case SlideInType.IntoFromBelow: + startY += startNoteRenderer.getNoteY(this._startNote, NoteYPosition.Bottom); + break; + case SlideInType.IntoFromAbove: + startY += startNoteRenderer.getNoteY(this._startNote, NoteYPosition.Top); + break; + default: + return; + } + + let accidentalsWidth: number = this.getAccidentalsWidth(startNoteRenderer, this._startNote.beat); + startX -= accidentalsWidth; + endX -= accidentalsWidth; + this.paintSlideLine(canvas, false, startX, endX, startY, endY); + } + + private getAccidentalsWidth(renderer: ScoreBarRenderer, beat: Beat): number { + let preNotes: ScoreBeatPreNotesGlyph = renderer.getPreNotesGlyphForBeat(beat) as ScoreBeatPreNotesGlyph; + if (preNotes && preNotes.accidentals) { + return preNotes.accidentals.width; + } + return 0; + } + + private drawSlideOut(cx: number, cy: number, canvas: ICanvas): void { + let startNoteRenderer: ScoreBarRenderer = this.renderer as ScoreBarRenderer; + let sizeX: number = 12 * this.scale; + let startOffsetX: number = 3 * this.scale; + let endOffsetX: number = 1 * this.scale; + let offsetY: number = 2 * this.scale; + let startX: number = 0; + let startY: number = 0; + let endX: number = 0; + let endY: number = 0; + let waves: boolean = false; + switch (this._outType) { + case SlideOutType.Shift: + case SlideOutType.Legato: + startX = + cx + + startNoteRenderer.x + + startNoteRenderer.getBeatX(this._startNote.beat, BeatXPosition.PostNotes) + + startOffsetX; + startY = cy + startNoteRenderer.y + startNoteRenderer.getNoteY(this._startNote, NoteYPosition.Center); + if (this._startNote.slideTarget) { + let endNoteRenderer: BarRendererBase | null = this.renderer.scoreRenderer.layout!.getRendererForBar( + this.renderer.staff.staveId, + this._startNote.slideTarget.beat.voice.bar + ) as BarRendererBase; + if (!endNoteRenderer || endNoteRenderer.staff !== startNoteRenderer.staff) { + endX = cx + startNoteRenderer.x + this._parent.x; + endY = startY; + } else { + endX = + cx + + endNoteRenderer.x + + endNoteRenderer.getBeatX(this._startNote.slideTarget.beat, BeatXPosition.PreNotes) - + endOffsetX; + endY = cy + endNoteRenderer.y + endNoteRenderer.getNoteY(this._startNote.slideTarget, NoteYPosition.Center); + } + + if(this._startNote.slideTarget.realValue > this._startNote.realValue) { + startY += offsetY; + endY -= offsetY; + } else { + startY -= offsetY; + endY += offsetY; + } + } else { + endX = cx + startNoteRenderer.x + this._parent.x; + endY = startY; + } + break; + case SlideOutType.OutUp: + startX = cx + startNoteRenderer.x + startNoteRenderer.getNoteX(this._startNote, NoteXPosition.Right); + startY = cy + startNoteRenderer.y + startNoteRenderer.getNoteY(this._startNote, NoteYPosition.Center); + endX = startX + sizeX; + endY = cy + startNoteRenderer.y + startNoteRenderer.getNoteY(this._startNote, NoteYPosition.Top); + break; + case SlideOutType.OutDown: + startX = cx + startNoteRenderer.x + startNoteRenderer.getNoteX(this._startNote, NoteXPosition.Right); + startY = cy + startNoteRenderer.y + startNoteRenderer.getNoteY(this._startNote, NoteYPosition.Center); + endX = startX + sizeX; + endY = cy + startNoteRenderer.y + startNoteRenderer.getNoteY(this._startNote, NoteYPosition.Bottom); + break; + case SlideOutType.PickSlideUp: + startX = cx + startNoteRenderer.x + startNoteRenderer.getNoteX(this._startNote, NoteXPosition.Right) + startOffsetX; + startY = cy + startNoteRenderer.y + startNoteRenderer.getNoteY(this._startNote, NoteYPosition.Center); + endY = cy + startNoteRenderer.y + startNoteRenderer.getNoteY(this._startNote, NoteYPosition.Top); + endX = cx + startNoteRenderer.x + startNoteRenderer.width; + if ( + this._startNote.beat.nextBeat && + this._startNote.beat.nextBeat.voice === this._startNote.beat.voice + ) { + endX = + cx + + startNoteRenderer.x + + startNoteRenderer.getBeatX(this._startNote.beat.nextBeat, BeatXPosition.PreNotes); + } + waves = true; + break; + case SlideOutType.PickSlideDown: + startX = cx + startNoteRenderer.x + startNoteRenderer.getNoteX(this._startNote, NoteXPosition.Right) + startOffsetX; + startY = cy + startNoteRenderer.y + startNoteRenderer.getNoteY(this._startNote, NoteYPosition.Center); + endY = cy + startNoteRenderer.y + startNoteRenderer.getNoteY(this._startNote, NoteYPosition.Bottom); + endX = cx + startNoteRenderer.x + startNoteRenderer.width; + if ( + this._startNote.beat.nextBeat && + this._startNote.beat.nextBeat.voice === this._startNote.beat.voice + ) { + endX = + cx + + startNoteRenderer.x + + startNoteRenderer.getBeatX(this._startNote.beat.nextBeat, BeatXPosition.PreNotes); + } + waves = true; + break; + default: + return; + } + this.paintSlideLine(canvas, waves, startX, endX, startY, endY); + } + + private paintSlideLine( + canvas: ICanvas, + waves: boolean, + startX: number, + endX: number, + startY: number, + endY: number + ): void { + if (waves) { + let b: number = endX - startX; + let a: number = endY - startY; + let c: number = Math.sqrt(Math.pow(a, 2) + Math.pow(b, 2)); + let angle: number = Math.asin(a / c) * (180 / Math.PI); + canvas.beginRotate(startX, startY, angle); + let glyph: NoteVibratoGlyph = new NoteVibratoGlyph(0, 0, VibratoType.Slight, 1.2); + glyph.renderer = this.renderer; + glyph.doLayout(); + glyph.width = b; + glyph.paint(0, 0, canvas); + canvas.endRotate(); + } else { + canvas.beginPath(); + canvas.moveTo(startX, startY); + canvas.lineTo(endX, endY); + canvas.stroke(); + } + } +} diff --git a/src/rendering/glyphs/ScoreSlurGlyph.ts b/src/rendering/glyphs/ScoreSlurGlyph.ts new file mode 100644 index 000000000..3f5624353 --- /dev/null +++ b/src/rendering/glyphs/ScoreSlurGlyph.ts @@ -0,0 +1,98 @@ +import { Note } from '@src/model/Note'; +import { ScoreLegatoGlyph } from './ScoreLegatoGlyph'; +import { ScoreBarRenderer } from '../ScoreBarRenderer'; +import { NoteYPosition, NoteXPosition } from '../BarRendererBase'; +import { BeamDirection } from '../utils/BeamDirection'; +import { GraceType } from '@src/model/GraceType'; +import { BeatXPosition } from '../BeatXPosition'; + +export class ScoreSlurGlyph extends ScoreLegatoGlyph { + private _startNote: Note; + private _endNote: Note; + + public constructor(startNote: Note, endNote: Note, forEnd: boolean = false) { + super(startNote.beat, endNote.beat, forEnd); + this._startNote = startNote; + this._endNote = endNote; + } + + protected getTieHeight(startX: number, startY: number, endX: number, endY: number): number { + return Math.log2(endX - startX + 1) * this.renderer.settings.notation.slurHeight; + } + + protected getStartY(): number { + if (this.isStartCentered()) { + switch (this.tieDirection) { + case BeamDirection.Up: + // below lowest note + return this.startNoteRenderer!.getNoteY(this._startNote, NoteYPosition.Top); + default: + return this.startNoteRenderer!.getNoteY(this._startNote, NoteYPosition.Bottom); + } + } + + return this.startNoteRenderer!.getNoteY(this._startNote, NoteYPosition.Center); + } + + protected getEndY(): number { + if (this.isEndCentered()) { + if (this.isEndOnStem()) { + switch (this.tieDirection) { + case BeamDirection.Up: + return this.endNoteRenderer!.getNoteY(this._endNote, NoteYPosition.TopWithStem); + default: + return this.endNoteRenderer!.getNoteY(this._endNote, NoteYPosition.BottomWithStem); + } + } else { + switch (this.tieDirection) { + case BeamDirection.Up: + return this.endNoteRenderer!.getNoteY(this._endNote, NoteYPosition.Top); + default: + return this.endNoteRenderer!.getNoteY(this._endNote, NoteYPosition.Bottom); + } + } + } else { + return this.endNoteRenderer!.getNoteY(this._endNote, NoteYPosition.Center); + } + } + + private isStartCentered() { + return ( + (this._startNote === this._startNote.beat.maxNote && this.tieDirection === BeamDirection.Up) || + (this._startNote === this._startNote.beat.minNote && this.tieDirection === BeamDirection.Down) + ); + } + private isEndCentered() { + return this._startNote.beat.graceType === GraceType.None && ( + (this._endNote === this._endNote.beat.maxNote && this.tieDirection === BeamDirection.Up) || + (this._endNote === this._endNote.beat.minNote && this.tieDirection === BeamDirection.Down) + ); + } + + private isEndOnStem() { + const endNoteScoreRenderer = this.endNoteRenderer as ScoreBarRenderer; + + const startBeamDirection = (this.startNoteRenderer as ScoreBarRenderer).getBeatDirection(this.startBeat!); + const endBeamDirection = endNoteScoreRenderer.getBeatDirection(this.endBeat!); + + return startBeamDirection !== endBeamDirection && this.startBeat!.graceType === GraceType.None; + } + + protected getStartX(): number { + return this.isStartCentered() + ? this.startNoteRenderer!.getBeatX(this._startNote.beat, BeatXPosition.MiddleNotes) + : this.startNoteRenderer!.getNoteX(this._startNote, NoteXPosition.Right); + } + + protected getEndX(): number { + if (this.isEndCentered()) { + if (this.isEndOnStem()) { + return this.endNoteRenderer!.getBeatX(this._endNote.beat, BeatXPosition.Stem); + } else { + return this.endNoteRenderer!.getNoteX(this._endNote, NoteXPosition.Center); + } + } else { + return this.endNoteRenderer!.getBeatX(this._endNote.beat, BeatXPosition.PreNotes); + } + } +} diff --git a/src/rendering/glyphs/ScoreTieGlyph.ts b/src/rendering/glyphs/ScoreTieGlyph.ts new file mode 100644 index 000000000..8e39cffc8 --- /dev/null +++ b/src/rendering/glyphs/ScoreTieGlyph.ts @@ -0,0 +1,77 @@ +import { Beat } from '@src/model/Beat'; +import { Note } from '@src/model/Note'; +import { BarRendererBase, NoteYPosition } from '@src/rendering/BarRendererBase'; +import { TieGlyph } from '@src/rendering/glyphs/TieGlyph'; +import { ScoreBarRenderer } from '@src/rendering/ScoreBarRenderer'; +import { BeamDirection } from '@src/rendering/utils/BeamDirection'; +import { BeatXPosition } from '../BeatXPosition'; + +export class ScoreTieGlyph extends TieGlyph { + protected startNote: Note; + protected endNote: Note; + + public constructor(startNote: Note, endNote: Note, forEnd: boolean = false) { + super(!startNote ? null : startNote.beat, !endNote ? null : endNote.beat, forEnd); + this.startNote = startNote; + this.endNote = endNote; + } + + protected shouldDrawBendSlur() { + return this.renderer.settings.notation.extendBendArrowsOnTiedNotes && !!this.startNote.bendOrigin && this.startNote.isTieOrigin; + } + + public doLayout(): void { + super.doLayout(); + } + + protected getBeamDirection(beat: Beat, noteRenderer: BarRendererBase): BeamDirection { + // invert direction (if stems go up, ties go down to not cross them) + switch ((noteRenderer as ScoreBarRenderer).getBeatDirection(beat)) { + case BeamDirection.Up: + return BeamDirection.Down; + default: + return BeamDirection.Up; + } + } + + protected getStartY(): number { + if (this.startBeat!.isRest) { + // below all lines + return (this.startNoteRenderer as ScoreBarRenderer).getScoreY(9, 0); + } + switch (this.tieDirection) { + case BeamDirection.Up: + // below lowest note + return this.startNoteRenderer!.getNoteY(this.startNote, NoteYPosition.Top); + default: + return this.startNoteRenderer!.getNoteY(this.startNote, NoteYPosition.Bottom); + } + } + + protected getEndY(): number { + const endNoteScoreRenderer = this.endNoteRenderer as ScoreBarRenderer; + if (this.endBeat!.isRest) { + switch (this.tieDirection) { + case BeamDirection.Up: + return endNoteScoreRenderer.getScoreY(9, 0); + default: + return endNoteScoreRenderer.getScoreY(0, 0); + } + } + + switch (this.tieDirection) { + case BeamDirection.Up: + return endNoteScoreRenderer.getNoteY(this.endNote, NoteYPosition.Top); + default: + return endNoteScoreRenderer.getNoteY(this.endNote, NoteYPosition.Bottom); + } + } + + protected getStartX(): number { + return this.startNoteRenderer!.getBeatX(this.startNote.beat, BeatXPosition.PostNotes); + } + + protected getEndX(): number { + return this.endNoteRenderer!.getBeatX(this.endNote.beat, BeatXPosition.PreNotes); + } +} diff --git a/src/rendering/glyphs/ScoreTimeSignatureGlyph.ts b/src/rendering/glyphs/ScoreTimeSignatureGlyph.ts new file mode 100644 index 000000000..ab184dd5f --- /dev/null +++ b/src/rendering/glyphs/ScoreTimeSignatureGlyph.ts @@ -0,0 +1,29 @@ +import { TimeSignatureGlyph } from '@src/rendering/glyphs/TimeSignatureGlyph'; +import { ScoreBarRenderer } from '@src/rendering/ScoreBarRenderer'; + +export class ScoreTimeSignatureGlyph extends TimeSignatureGlyph { + protected get commonY(): number { + let renderer: ScoreBarRenderer = this.renderer as ScoreBarRenderer; + return renderer.getScoreY(4, 0); + } + + protected get numeratorY(): number { + return 2 * this.scale; + } + + protected get denominatorY(): number { + return 20 * this.scale; + } + + protected get commonScale(): number { + return 1; + } + + protected get numberScale(): number { + return 1; + } + + public constructor(x: number, y: number, numerator: number, denominator: number, isCommon: boolean) { + super(x, y, numerator, denominator, isCommon); + } +} diff --git a/src/rendering/glyphs/ScoreWhammyBarGlyph.ts b/src/rendering/glyphs/ScoreWhammyBarGlyph.ts new file mode 100644 index 000000000..c0b71e164 --- /dev/null +++ b/src/rendering/glyphs/ScoreWhammyBarGlyph.ts @@ -0,0 +1,370 @@ +import { Beat } from '@src/model/Beat'; +import { BendPoint } from '@src/model/BendPoint'; +import { BendStyle } from '@src/model/BendStyle'; +import { Note } from '@src/model/Note'; +import { WhammyType } from '@src/model/WhammyType'; +import { NotationMode } from '@src/NotationSettings'; +import { ICanvas, TextAlign } from '@src/platform/ICanvas'; +import { BeatXPosition } from '@src/rendering/BeatXPosition'; +import { BendNoteHeadGroupGlyph } from '@src/rendering/glyphs/BendNoteHeadGroupGlyph'; +import { ScoreBeatPreNotesGlyph } from '@src/rendering/glyphs/ScoreBeatPreNotesGlyph'; +import { ScoreHelperNotesBaseGlyph } from '@src/rendering/glyphs/ScoreHelperNotesBaseGlyph'; +import { TieGlyph } from '@src/rendering/glyphs/TieGlyph'; +import { ScoreBarRenderer } from '@src/rendering/ScoreBarRenderer'; +import { BeamDirection } from '@src/rendering/utils/BeamDirection'; +import { RenderingResources } from '@src/RenderingResources'; +import { TabWhammyBarGlyph } from '@src/rendering/glyphs/TabWhammyBarGlyph'; +import { NoteHeadGlyph } from '@src/rendering/glyphs/NoteHeadGlyph'; +import { NoteYPosition } from '../BarRendererBase'; + +export class ScoreWhammyBarGlyph extends ScoreHelperNotesBaseGlyph { + public static readonly SimpleDipHeight: number = TabWhammyBarGlyph.PerHalfSize * 2; + public static readonly SimpleDipPadding: number = 2; + private _beat: Beat; + + public constructor(beat: Beat) { + super(); + this._beat = beat; + } + + public doLayout(): void { + let whammyMode: NotationMode = this.renderer.settings.notation.notationMode; + switch (this._beat.whammyBarType) { + case WhammyType.None: + case WhammyType.Custom: + case WhammyType.Hold: + return; + case WhammyType.Dive: + case WhammyType.PrediveDive: + { + let endGlyphs: BendNoteHeadGroupGlyph = new BendNoteHeadGroupGlyph(this._beat, false); + endGlyphs.renderer = this.renderer; + let lastWhammyPoint: BendPoint = this._beat.whammyBarPoints[this._beat.whammyBarPoints.length - 1]; + for (let note of this._beat.notes) { + if (!note.isTieOrigin) { + endGlyphs.addGlyph( + this.getBendNoteValue(note, lastWhammyPoint), + lastWhammyPoint.value % 2 !== 0 + ); + } + } + endGlyphs.doLayout(); + this.BendNoteHeads.push(endGlyphs); + } + break; + case WhammyType.Dip: + { + if (whammyMode === NotationMode.SongBook) { + let res: RenderingResources = this.renderer.resources; + (this.renderer as ScoreBarRenderer).simpleWhammyOverflow = + res.tablatureFont.size * 1.5 + + ScoreWhammyBarGlyph.SimpleDipHeight * this.scale + + ScoreWhammyBarGlyph.SimpleDipPadding * this.scale; + } else { + let middleGlyphs: BendNoteHeadGroupGlyph = new BendNoteHeadGroupGlyph(this._beat, false); + middleGlyphs.renderer = this.renderer; + if (this.renderer.settings.notation.notationMode === NotationMode.GuitarPro) { + let middleBendPoint: BendPoint = this._beat.whammyBarPoints[1]; + for (let note of this._beat.notes) { + middleGlyphs.addGlyph( + this.getBendNoteValue(note, this._beat.whammyBarPoints[1]), + middleBendPoint.value % 2 !== 0 + ); + } + } + middleGlyphs.doLayout(); + this.BendNoteHeads.push(middleGlyphs); + let endGlyphs: BendNoteHeadGroupGlyph = new BendNoteHeadGroupGlyph(this._beat, false); + endGlyphs.renderer = this.renderer; + if (this.renderer.settings.notation.notationMode === NotationMode.GuitarPro) { + let lastBendPoint: BendPoint = this._beat.whammyBarPoints[ + this._beat.whammyBarPoints.length - 1 + ]; + for (let note of this._beat.notes) { + endGlyphs.addGlyph( + this.getBendNoteValue(note, lastBendPoint), + lastBendPoint.value % 2 !== 0 + ); + } + } + endGlyphs.doLayout(); + this.BendNoteHeads.push(endGlyphs); + } + } + break; + case WhammyType.Predive: + break; + } + super.doLayout(); + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + let beat: Beat = this._beat; + switch (beat.whammyBarType) { + case WhammyType.None: + case WhammyType.Custom: + return; + } + let whammyMode: NotationMode = this.renderer.settings.notation.notationMode; + let startNoteRenderer: ScoreBarRenderer = this.renderer.scoreRenderer.layout!.getRendererForBar( + this.renderer.staff.staveId, + beat.voice.bar + )! as ScoreBarRenderer; + let startX: number = cx + startNoteRenderer.x + startNoteRenderer.getBeatX(beat, BeatXPosition.MiddleNotes); + let beatDirection: BeamDirection = this.getBeamDirection(beat, startNoteRenderer); + let direction: BeamDirection = this._beat.notes.length === 1 ? beatDirection : BeamDirection.Up; + let textalign: TextAlign = canvas.textAlign; + for (let i: number = 0; i < beat.notes.length; i++) { + let note: Note = beat.notes[i]; + let startY: number = cy + startNoteRenderer.y; + if (i > 0 && i >= ((this._beat.notes.length / 2) | 0)) { + direction = BeamDirection.Down; + } + + if (direction === BeamDirection.Down) { + startY += startNoteRenderer.getNoteY(note, NoteYPosition.Bottom); + } else { + startY += startNoteRenderer.getNoteY(note, NoteYPosition.Top); + } + + let endX: number = cx + startNoteRenderer.x; + if (beat.isLastOfVoice) { + endX += startNoteRenderer.width; + } else { + endX += startNoteRenderer.getBeatX(beat, BeatXPosition.EndBeat); + } + endX -= 8 * this.scale; + let slurText: string = beat.whammyStyle === BendStyle.Gradual && i === 0 ? 'grad.' : ''; + let endNoteRenderer: ScoreBarRenderer | null = null; + if (note.isTieOrigin) { + endNoteRenderer = this.renderer.scoreRenderer.layout!.getRendererForBar( + this.renderer.staff.staveId, + note.tieDestination!.beat.voice.bar + ) as ScoreBarRenderer; + if (endNoteRenderer && endNoteRenderer.staff === startNoteRenderer.staff) { + endX = + cx + + endNoteRenderer.x + + endNoteRenderer.getBeatX(note.tieDestination!.beat, BeatXPosition.MiddleNotes); + } else { + endNoteRenderer = null; + } + } + let heightOffset: number = NoteHeadGlyph.NoteHeadHeight * this.scale * NoteHeadGlyph.GraceScale * 0.5; + if (direction === BeamDirection.Up) { + heightOffset = -heightOffset; + } + let endValue: number = beat.whammyBarPoints.length > 0 + ? this.getBendNoteValue(note, beat.whammyBarPoints[beat.whammyBarPoints.length - 1]) + : 0; + let endY: number = 0; + let bendTie: boolean = false; + + if (this.BendNoteHeads.length > 0 && this.BendNoteHeads[0].containsNoteValue(endValue)) { + endY = this.BendNoteHeads[0].getNoteValueY(endValue) + heightOffset; + bendTie = true; + } else if ( + endNoteRenderer && + ((note.isTieOrigin && note.tieDestination!.beat.hasWhammyBar) || note.beat.isContinuedWhammy) + ) { + endY = cy + endNoteRenderer.y + endNoteRenderer.getNoteY(note.tieDestination!, NoteYPosition.Top); + bendTie = true; + if (direction === BeamDirection.Down) { + endY += NoteHeadGlyph.NoteHeadHeight * this.scale; + } + } else if (note.isTieOrigin) { + if (!endNoteRenderer) { + endY = startY; + } else { + endY = cy + endNoteRenderer.y + endNoteRenderer.getNoteY(note.tieDestination!, NoteYPosition.Top); + } + if (direction === BeamDirection.Down) { + endY += NoteHeadGlyph.NoteHeadHeight * this.scale; + } + } + + switch (beat.whammyBarType) { + case WhammyType.Hold: + if (note.isTieOrigin) { + TieGlyph.paintTie( + canvas, + this.scale, + startX, + startY, + endX, + endY, + beatDirection === BeamDirection.Down, + 22, + 4 + ); + canvas.fill(); + } + break; + case WhammyType.Dive: + if (i === 0) { + this.BendNoteHeads[0].x = endX - this.BendNoteHeads[0].noteHeadOffset; + this.BendNoteHeads[0].y = cy + startNoteRenderer.y; + this.BendNoteHeads[0].paint(0, 0, canvas); + if(this.BendNoteHeads[0].containsNoteValue(endValue)) { + endY += this.BendNoteHeads[0].y; + } + } + if (bendTie) { + this.drawBendSlur( + canvas, + startX, + startY, + endX, + endY, + direction === BeamDirection.Down, + this.scale, + slurText + ); + } else if (note.isTieOrigin) { + TieGlyph.paintTie( + canvas, + this.scale, + startX, + startY, + endX, + endY, + beatDirection === BeamDirection.Down, + 22, + 4 + ); + canvas.fill(); + } + break; + case WhammyType.Dip: + if (whammyMode === NotationMode.SongBook) { + if (i === 0) { + let simpleStartX: number = + cx + + startNoteRenderer.x + + startNoteRenderer.getBeatX(this._beat, BeatXPosition.OnNotes) - + 2 * this.scale; + let simpleEndX: number = + cx + + startNoteRenderer.x + + startNoteRenderer.getBeatX(this._beat, BeatXPosition.PostNotes) + + 2 * this.scale; + let middleX: number = (simpleStartX + simpleEndX) / 2; + let text: string = ( + ((this._beat.whammyBarPoints[1].value - this._beat.whammyBarPoints[0].value) / 4) | + 0 + ).toString(); + canvas.font = this.renderer.resources.tablatureFont; + canvas.fillText(text, middleX, cy + this.y); + let simpleStartY: number = cy + this.y + canvas.font.size + 2 * this.scale; + let simpleEndY: number = simpleStartY + ScoreWhammyBarGlyph.SimpleDipHeight * this.scale; + if (this._beat.whammyBarPoints[1].value > this._beat.whammyBarPoints[0].value) { + canvas.moveTo(simpleStartX, simpleEndY); + canvas.lineTo(middleX, simpleStartY); + canvas.lineTo(simpleEndX, simpleEndY); + } else { + canvas.moveTo(simpleStartX, simpleStartY); + canvas.lineTo(middleX, simpleEndY); + canvas.lineTo(simpleEndX, simpleStartY); + } + canvas.stroke(); + } + if (note.isTieOrigin) { + TieGlyph.paintTie( + canvas, + this.scale, + startX, + startY, + endX, + endY, + beatDirection === BeamDirection.Down, + 22, + 4 + ); + canvas.fill(); + } + } else { + let middleX: number = (startX + endX) / 2; + this.BendNoteHeads[0].x = middleX - this.BendNoteHeads[0].noteHeadOffset; + this.BendNoteHeads[0].y = cy + startNoteRenderer.y; + this.BendNoteHeads[0].paint(0, 0, canvas); + let middleValue: number = this.getBendNoteValue(note, beat.whammyBarPoints[1]); + let middleY: number = this.BendNoteHeads[0].getNoteValueY(middleValue) + heightOffset; + this.drawBendSlur( + canvas, + startX, + startY, + middleX, + middleY, + direction === BeamDirection.Down, + this.scale, + slurText + ); + this.BendNoteHeads[1].x = endX - this.BendNoteHeads[1].noteHeadOffset; + this.BendNoteHeads[1].y = cy + startNoteRenderer.y; + this.BendNoteHeads[1].paint(0, 0, canvas); + endY = this.BendNoteHeads[1].getNoteValueY(endValue) + heightOffset; + this.drawBendSlur( + canvas, + middleX, + middleY, + endX, + endY, + direction === BeamDirection.Down, + this.scale, + slurText + ); + } + break; + case WhammyType.PrediveDive: + case WhammyType.Predive: + let preX: number = + cx + startNoteRenderer.x + startNoteRenderer.getBeatX(note.beat, BeatXPosition.PreNotes); + preX += (startNoteRenderer.getBeatContainer(note.beat).preNotes as ScoreBeatPreNotesGlyph) + .prebendNoteHeadOffset; + let preY: number = + cy + + startNoteRenderer.y + + startNoteRenderer.getScoreY( + startNoteRenderer.accidentalHelper.getNoteLineForValue( + note.displayValue - ((note.beat.whammyBarPoints[0].value / 2) | 0), + false + ), + 0 + ) + + heightOffset; + this.drawBendSlur( + canvas, + preX, + preY, + startX, + startY, + direction === BeamDirection.Down, + this.scale, + slurText + ); + if (this.BendNoteHeads.length > 0) { + this.BendNoteHeads[0].x = endX - this.BendNoteHeads[0].noteHeadOffset; + this.BendNoteHeads[0].y = cy + startNoteRenderer.y; + this.BendNoteHeads[0].paint(0, 0, canvas); + this.drawBendSlur( + canvas, + startX, + startY, + endX, + endY, + direction === BeamDirection.Down, + this.scale, + slurText + ); + } + break; + } + } + canvas.textAlign = textalign; + } + + private getBendNoteValue(note: Note, bendPoint: BendPoint): number { + // NOTE: bendpoints are in 1/4 tones, but the note values are in 1/2 notes. + return note.displayValueWithoutBend + ((bendPoint.value / 2) | 0); + } +} diff --git a/src/rendering/glyphs/SpacingGlyph.ts b/src/rendering/glyphs/SpacingGlyph.ts new file mode 100644 index 000000000..619de33ee --- /dev/null +++ b/src/rendering/glyphs/SpacingGlyph.ts @@ -0,0 +1,11 @@ +import { Glyph } from '@src/rendering/glyphs/Glyph'; + +/** + * This simple glyph allows to put an empty region in to a BarRenderer. + */ +export class SpacingGlyph extends Glyph { + public constructor(x: number, y: number, width: number) { + super(x, y); + this.width = width; + } +} diff --git a/src/rendering/glyphs/TabBeatContainerGlyph.ts b/src/rendering/glyphs/TabBeatContainerGlyph.ts new file mode 100644 index 000000000..abd94fcca --- /dev/null +++ b/src/rendering/glyphs/TabBeatContainerGlyph.ts @@ -0,0 +1,91 @@ +import { Beat } from '@src/model/Beat'; +import { Note } from '@src/model/Note'; +import { SlideInType } from '@src/model/SlideInType'; +import { SlideOutType } from '@src/model/SlideOutType'; +import { BeatContainerGlyph } from '@src/rendering/glyphs/BeatContainerGlyph'; +import { TabBendGlyph } from '@src/rendering/glyphs/TabBendGlyph'; +import { TabSlideLineGlyph } from '@src/rendering/glyphs/TabSlideLineGlyph'; +import { TabSlurGlyph } from '@src/rendering/glyphs/TabSlurGlyph'; +import { TabTieGlyph } from '@src/rendering/glyphs/TabTieGlyph'; +import { VoiceContainerGlyph } from '@src/rendering/glyphs/VoiceContainerGlyph'; +import { TabBarRenderer } from '@src/rendering/TabBarRenderer'; + +export class TabBeatContainerGlyph extends BeatContainerGlyph { + private _bend: TabBendGlyph | null = null; + private _effectSlurs: TabSlurGlyph[] = []; + + public constructor(beat: Beat, voiceContainer: VoiceContainerGlyph) { + super(beat, voiceContainer); + } + + public doLayout(): void { + this._effectSlurs = []; + super.doLayout(); + if (this._bend) { + this._bend.renderer = this.renderer; + this._bend.doLayout(); + this.updateWidth(); + } + } + + protected createTies(n: Note): void { + if (!n.isVisible) { + return; + } + let renderer: TabBarRenderer = this.renderer as TabBarRenderer; + if (n.isTieOrigin && renderer.showTiedNotes && n.tieDestination!.isVisible) { + let tie: TabTieGlyph = new TabTieGlyph(n, n.tieDestination!, false); + this.ties.push(tie); + } + if (n.isTieDestination && renderer.showTiedNotes) { + let tie: TabTieGlyph = new TabTieGlyph(n.tieOrigin!, n, true); + this.ties.push(tie); + } + if (n.isLeftHandTapped && !n.isHammerPullDestination) { + let tapSlur: TabTieGlyph = new TabTieGlyph(n, n, false); + this.ties.push(tapSlur); + } + // start effect slur on first beat + if (n.isEffectSlurOrigin && n.effectSlurDestination) { + let expanded: boolean = false; + for (let slur of this._effectSlurs) { + if (slur.tryExpand(n, n.effectSlurDestination, false, false)) { + expanded = true; + break; + } + } + if (!expanded) { + let effectSlur: TabSlurGlyph = new TabSlurGlyph(n, n.effectSlurDestination, false, false); + this._effectSlurs.push(effectSlur); + this.ties.push(effectSlur); + } + } + // end effect slur on last beat + if (n.isEffectSlurDestination && n.effectSlurOrigin) { + let expanded: boolean = false; + for (let slur of this._effectSlurs) { + if (slur.tryExpand(n.effectSlurOrigin, n, false, true)) { + expanded = true; + break; + } + } + if (!expanded) { + let effectSlur: TabSlurGlyph = new TabSlurGlyph(n.effectSlurOrigin, n, false, true); + this._effectSlurs.push(effectSlur); + this.ties.push(effectSlur); + } + } + if (n.slideInType !== SlideInType.None || n.slideOutType !== SlideOutType.None) { + let l: TabSlideLineGlyph = new TabSlideLineGlyph(n.slideInType, n.slideOutType, n, this); + this.ties.push(l); + } + if (n.hasBend) { + if (!this._bend) { + this._bend = new TabBendGlyph(); + this._bend.renderer = this.renderer; + this.ties.push(this._bend); + } + this._bend.addBends(n); + } + } +} diff --git a/src/rendering/glyphs/TabBeatGlyph.ts b/src/rendering/glyphs/TabBeatGlyph.ts new file mode 100644 index 000000000..c01d3e37c --- /dev/null +++ b/src/rendering/glyphs/TabBeatGlyph.ts @@ -0,0 +1,191 @@ +import { Duration } from '@src/model/Duration'; +import { GraceType } from '@src/model/GraceType'; +import { Note } from '@src/model/Note'; +import { TabRhythmMode } from '@src/NotationSettings'; +import { BeatOnNoteGlyphBase } from '@src/rendering/glyphs/BeatOnNoteGlyphBase'; +import { CircleGlyph } from '@src/rendering/glyphs/CircleGlyph'; +import { Glyph } from '@src/rendering/glyphs/Glyph'; +import { NoteNumberGlyph } from '@src/rendering/glyphs/NoteNumberGlyph'; +import { SpacingGlyph } from '@src/rendering/glyphs/SpacingGlyph'; +import { TabNoteChordGlyph } from '@src/rendering/glyphs/TabNoteChordGlyph'; +import { TabRestGlyph } from '@src/rendering/glyphs/TabRestGlyph'; +import { TabWhammyBarGlyph } from '@src/rendering/glyphs/TabWhammyBarGlyph'; +import { TremoloPickingGlyph } from '@src/rendering/glyphs/TremoloPickingGlyph'; +import { TabBarRenderer } from '@src/rendering/TabBarRenderer'; +import { NoteXPosition, NoteYPosition } from '../BarRendererBase'; +import { BeatBounds } from '../utils/BeatBounds'; + +export class TabBeatGlyph extends BeatOnNoteGlyphBase { + public noteNumbers: TabNoteChordGlyph | null = null; + public restGlyph: TabRestGlyph | null = null; + + public getNoteX(note: Note, requestedPosition: NoteXPosition): number { + return this.noteNumbers ? this.noteNumbers.getNoteX(note, requestedPosition) : 0; + } + + public getNoteY(note: Note, requestedPosition: NoteYPosition): number { + return this.noteNumbers ? this.noteNumbers.getNoteY(note, requestedPosition) : 0; + } + + public buildBoundingsLookup(beatBounds:BeatBounds, cx:number, cy:number) { + if(this.noteNumbers) { + this.noteNumbers.buildBoundingsLookup(beatBounds, cx + this.x, cy + this.y); + } + } + + public doLayout(): void { + let tabRenderer: TabBarRenderer = this.renderer as TabBarRenderer; + if (!this.container.beat.isRest) { + // + // Note numbers + let isGrace: boolean = + this.renderer.settings.notation.smallGraceTabNotes && this.container.beat.graceType !== GraceType.None; + this.noteNumbers = new TabNoteChordGlyph(0, 0, isGrace); + this.noteNumbers.beat = this.container.beat; + this.noteNumbers.beamingHelper = this.beamingHelper; + for (let note of this.container.beat.notes) { + if (note.isVisible) { + this.createNoteGlyph(note); + } + } + this.addGlyph(this.noteNumbers); + // + // Whammy Bar + if (this.container.beat.hasWhammyBar) { + let whammy: TabWhammyBarGlyph = new TabWhammyBarGlyph(this.container.beat); + whammy.renderer = this.renderer; + whammy.doLayout(); + this.container.ties.push(whammy); + } + // + // Tremolo Picking + if (this.container.beat.isTremolo && !this.noteNumbers.beatEffects.has('tremolo')) { + let offset: number = 0; + let speed = this.container.beat.tremoloSpeed!; + switch (speed) { + case Duration.ThirtySecond: + offset = 10; + break; + case Duration.Sixteenth: + offset = 5; + break; + case Duration.Eighth: + offset = 0; + break; + } + this.noteNumbers.beatEffects.set( + 'tremolo', + new TremoloPickingGlyph(5 * this.scale, offset * this.scale, speed) + ); + } + // + // Note dots + // + if (this.container.beat.dots > 0 && tabRenderer.settings.notation.rhythmMode !== TabRhythmMode.Hidden) { + this.addGlyph(new SpacingGlyph(0, 0, 5 * this.scale)); + for (let i: number = 0; i < this.container.beat.dots; i++) { + this.addGlyph( + new CircleGlyph( + 0, + tabRenderer.lineOffset * tabRenderer.bar.staff.tuning.length + + tabRenderer.settings.notation.rhythmHeight * tabRenderer.scale, + 1.5 * this.scale + ) + ); + } + } + } else { + let line: number = 0; + let offset: number = 0; + switch (this.container.beat.duration) { + case Duration.QuadrupleWhole: + line = 3; + break; + case Duration.DoubleWhole: + line = 3; + break; + case Duration.Whole: + line = 2; + break; + case Duration.Half: + line = 3; + break; + case Duration.Quarter: + line = 3; + break; + case Duration.Eighth: + line = 2; + offset = 5; + break; + case Duration.Sixteenth: + line = 2; + offset = 5; + break; + case Duration.ThirtySecond: + line = 3; + break; + case Duration.SixtyFourth: + line = 3; + break; + case Duration.OneHundredTwentyEighth: + line = 3; + break; + case Duration.TwoHundredFiftySixth: + line = 3; + break; + } + let y: number = tabRenderer.getTabY(line, offset); + this.restGlyph = new TabRestGlyph(0, y, tabRenderer.showRests, this.container.beat.duration); + this.restGlyph.beat = this.container.beat; + this.restGlyph.beamingHelper = this.beamingHelper; + this.addGlyph(this.restGlyph); + // + // Note dots + // + if (this.container.beat.dots > 0 && tabRenderer.showRests) { + this.addGlyph(new SpacingGlyph(0, 0, 5 * this.scale)); + for (let i: number = 0; i < this.container.beat.dots; i++) { + this.addGlyph(new CircleGlyph(0, y, 1.5 * this.scale)); + } + } + } + // left to right layout + if (!this.glyphs) { + return; + } + let w: number = 0; + for (let i: number = 0, j: number = this.glyphs.length; i < j; i++) { + let g: Glyph = this.glyphs[i]; + g.x = w; + g.renderer = this.renderer; + g.doLayout(); + w += g.width; + } + this.width = w; + if (this.container.beat.isEmpty) { + this.centerX = this.width / 2; + } else if (this.container.beat.isRest) { + this.centerX = this.restGlyph!.x + this.restGlyph!.width / 2; + } else { + this.centerX = this.noteNumbers!.x + this.noteNumbers!.noteStringWidth / 2; + } + } + + public updateBeamingHelper(): void { + if (!this.container.beat.isRest) { + this.noteNumbers!.updateBeamingHelper(this.container.x + this.x); + } else { + this.restGlyph!.updateBeamingHelper(this.container.x + this.x); + } + } + + private createNoteGlyph(n: Note): void { + let tr: TabBarRenderer = this.renderer as TabBarRenderer; + let noteNumberGlyph: NoteNumberGlyph = new NoteNumberGlyph(0, 0, n); + let l: number = n.beat.voice.bar.staff.tuning.length - n.string + 1; + noteNumberGlyph.y = tr.getTabY(l, -2); + noteNumberGlyph.renderer = this.renderer; + noteNumberGlyph.doLayout(); + this.noteNumbers!.addNoteGlyph(noteNumberGlyph, n); + } +} diff --git a/src/rendering/glyphs/TabBeatPreNotesGlyph.ts b/src/rendering/glyphs/TabBeatPreNotesGlyph.ts new file mode 100644 index 000000000..c5bee21f0 --- /dev/null +++ b/src/rendering/glyphs/TabBeatPreNotesGlyph.ts @@ -0,0 +1,18 @@ +import { BrushType } from '@src/model/BrushType'; +import { BeatGlyphBase } from '@src/rendering/glyphs/BeatGlyphBase'; +import { SpacingGlyph } from '@src/rendering/glyphs/SpacingGlyph'; +import { TabBrushGlyph } from '@src/rendering/glyphs/TabBrushGlyph'; + +export class TabBeatPreNotesGlyph extends BeatGlyphBase { + public doLayout(): void { + if (this.container.beat.brushType !== BrushType.None && !this.container.beat.isRest) { + this.addGlyph(new TabBrushGlyph(this.container.beat)); + this.addGlyph(new SpacingGlyph(0, 0, 4 * this.scale)); + } + super.doLayout(); + } + + public constructor() { + super(); + } +} diff --git a/src/rendering/glyphs/TabBendGlyph.ts b/src/rendering/glyphs/TabBendGlyph.ts new file mode 100644 index 000000000..0e4d377d7 --- /dev/null +++ b/src/rendering/glyphs/TabBendGlyph.ts @@ -0,0 +1,466 @@ +import { Beat } from '@src/model/Beat'; +import { BendStyle } from '@src/model/BendStyle'; +import { BendType } from '@src/model/BendType'; +import { Color } from '@src/model/Color'; +import { Note } from '@src/model/Note'; +import { ICanvas } from '@src/platform/ICanvas'; +import { BarRendererBase, NoteYPosition, NoteXPosition } from '@src/rendering/BarRendererBase'; +import { BeatXPosition } from '@src/rendering/BeatXPosition'; +import { Glyph } from '@src/rendering/glyphs/Glyph'; +import { TabBendRenderPoint } from '@src/rendering/glyphs/TabBendRenderPoint'; +import { TabBarRenderer } from '@src/rendering/TabBarRenderer'; +import { RenderingResources } from '@src/RenderingResources'; +import { BendPoint } from '@src/model/BendPoint'; + +export class TabBendGlyph extends Glyph { + private static readonly ArrowSize: number = 6; + private static readonly DashSize: number = 3; + private static readonly BendValueHeight: number = 6; + + private _notes: Note[] = []; + private _renderPoints: Map = new Map(); + private _preBendMinValue: number = -1; + private _bendMiddleMinValue: number = -1; + private _bendEndMinValue: number = -1; + private _bendEndContinuedMinValue: number = -1; + private _releaseMinValue: number = -1; + private _releaseContinuedMinValue: number = -1; + private _maxBendValue: number = -1; + + public addBends(note: Note): void { + this._notes.push(note); + let renderPoints: TabBendRenderPoint[] = this.createRenderingPoints(note); + this._renderPoints.set(note.id, renderPoints); + if (this._maxBendValue === -1 || this._maxBendValue < note.maxBendPoint!.value) { + this._maxBendValue = note.maxBendPoint!.value; + } + // compute arrow end values for common bend types + let value: number = 0; + switch (note.bendType) { + case BendType.Bend: + value = renderPoints[1].value; + if (note.isTieOrigin) { + if (this._bendEndContinuedMinValue === -1 || value < this._bendEndContinuedMinValue) { + this._bendEndContinuedMinValue = value; + } + } else { + if (this._bendEndMinValue === -1 || value < this._bendEndMinValue) { + this._bendEndMinValue = value; + } + } + break; + case BendType.Release: + value = renderPoints[1].value; + if (note.isTieOrigin) { + if (this._releaseContinuedMinValue === -1 || value < this._releaseContinuedMinValue) { + this._releaseContinuedMinValue = value; + } + } else { + if (value > 0 && (this._releaseMinValue === -1 || value < this._releaseMinValue)) { + this._releaseMinValue = value; + } + } + break; + case BendType.BendRelease: + value = renderPoints[1].value; + if (this._bendMiddleMinValue === -1 || value < this._bendMiddleMinValue) { + this._bendMiddleMinValue = value; + } + value = renderPoints[2].value; + if (note.isTieOrigin) { + if (this._releaseContinuedMinValue === -1 || value < this._releaseContinuedMinValue) { + this._releaseContinuedMinValue = value; + } + } else { + if (value > 0 && (this._releaseMinValue === -1 || value < this._releaseMinValue)) { + this._releaseMinValue = value; + } + } + break; + case BendType.Prebend: + value = renderPoints[0].value; + if (this._preBendMinValue === -1 || value < this._preBendMinValue) { + this._preBendMinValue = value; + } + break; + case BendType.PrebendBend: + value = renderPoints[0].value; + if (this._preBendMinValue === -1 || value < this._preBendMinValue) { + this._preBendMinValue = value; + } + value = renderPoints[1].value; + if (note.isTieOrigin) { + if (this._bendEndContinuedMinValue === -1 || value < this._bendEndContinuedMinValue) { + this._bendEndContinuedMinValue = value; + } + } else { + if (this._bendEndMinValue === -1 || value < this._bendEndMinValue) { + this._bendEndMinValue = value; + } + } + break; + case BendType.PrebendRelease: + value = renderPoints[0].value; + if (this._preBendMinValue === -1 || value < this._preBendMinValue) { + this._preBendMinValue = value; + } + value = renderPoints[1].value; + if (note.isTieOrigin) { + if (this._releaseContinuedMinValue === -1 || value < this._releaseContinuedMinValue) { + this._releaseContinuedMinValue = value; + } + } else { + if (value > 0 && (this._releaseMinValue === -1 || value < this._releaseMinValue)) { + this._releaseMinValue = value; + } + } + break; + } + } + + public doLayout(): void { + super.doLayout(); + let bendHeight: number = this._maxBendValue * TabBendGlyph.BendValueHeight * this.scale; + this.renderer.registerOverflowTop(bendHeight); + let value: number = 0; + for (let note of this._notes) { + let renderPoints: TabBendRenderPoint[] = this._renderPoints.get(note.id)!; + switch (note.bendType) { + case BendType.Bend: + renderPoints[1].lineValue = note.isTieOrigin + ? this._bendEndContinuedMinValue + : this._bendEndMinValue; + break; + case BendType.Release: + value = note.isTieOrigin ? this._releaseContinuedMinValue : this._releaseMinValue; + if (value >= 0) { + renderPoints[1].lineValue = value; + } + break; + case BendType.BendRelease: + renderPoints[1].lineValue = this._bendMiddleMinValue; + value = note.isTieOrigin ? this._releaseContinuedMinValue : this._releaseMinValue; + if (value >= 0) { + renderPoints[2].lineValue = value; + } + break; + case BendType.Prebend: + renderPoints[0].lineValue = this._preBendMinValue; + break; + case BendType.PrebendBend: + renderPoints[0].lineValue = this._preBendMinValue; + renderPoints[1].lineValue = note.isTieOrigin + ? this._bendEndContinuedMinValue + : this._bendEndMinValue; + break; + case BendType.PrebendRelease: + renderPoints[0].lineValue = this._preBendMinValue; + value = note.isTieOrigin ? this._releaseContinuedMinValue : this._releaseMinValue; + if (value >= 0) { + renderPoints[1].lineValue = value; + } + break; + } + } + this.width = 0; + this._notes.sort((a, b) => { + if (a.isStringed) { + return a.string - b.string; + } + return a.realValue - b.realValue; + }); + } + + private createRenderingPoints(note: Note): TabBendRenderPoint[] { + let renderingPoints: TabBendRenderPoint[] = []; + // Guitar Pro Rendering Note: + // Last point of bend is always at end of the note even + // though it might not be 100% correct from timing perspective. + switch (note.bendType) { + case BendType.Custom: + for (let bendPoint of note.bendPoints) { + renderingPoints.push(new TabBendRenderPoint(bendPoint.offset, bendPoint.value)); + } + break; + case BendType.BendRelease: + renderingPoints.push(new TabBendRenderPoint(0, note.bendPoints[0].value)); + renderingPoints.push(new TabBendRenderPoint((BendPoint.MaxPosition / 2) | 0, note.bendPoints[1].value)); + renderingPoints.push(new TabBendRenderPoint(BendPoint.MaxPosition, note.bendPoints[3].value)); + break; + case BendType.Bend: + case BendType.Hold: + case BendType.Prebend: + case BendType.PrebendBend: + case BendType.PrebendRelease: + case BendType.Release: + renderingPoints.push(new TabBendRenderPoint(0, note.bendPoints[0].value)); + renderingPoints.push(new TabBendRenderPoint(BendPoint.MaxPosition, note.bendPoints[1].value)); + break; + } + return renderingPoints; + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + let color: Color = canvas.color; + if (this._notes.length > 1) { + canvas.color = this.renderer.resources.secondaryGlyphColor; + } + for (let note of this._notes) { + let renderPoints: TabBendRenderPoint[] = this._renderPoints.get(note.id)!; + let startNoteRenderer: BarRendererBase = this.renderer; + let endNote: Note = note; + let isMultiBeatBend: boolean = false; + let endNoteRenderer: TabBarRenderer | null = null; + let endNoteHasBend: boolean = false; + let slurText: string = note.bendStyle === BendStyle.Gradual ? 'grad.' : ''; + let endBeat: Beat | null = null; + while (endNote.isTieOrigin) { + let nextNote: Note = endNote.tieDestination!; + endNoteRenderer = this.renderer.scoreRenderer.layout!.getRendererForBar( + this.renderer.staff.staveId, + nextNote.beat.voice.bar + ) as TabBarRenderer; + if (!endNoteRenderer || startNoteRenderer.staff !== endNoteRenderer.staff) { + break; + } + endNote = nextNote; + isMultiBeatBend = true; + if (endNote.hasBend || !this.renderer.settings.notation.extendBendArrowsOnTiedNotes) { + endNoteHasBend = true; + break; + } + } + endBeat = endNote.beat; + endNoteRenderer = this.renderer.scoreRenderer.layout!.getRendererForBar( + this.renderer.staff.staveId, + endBeat.voice.bar + ) as TabBarRenderer; + if ( + endBeat.isLastOfVoice && + !endNote.hasBend && + this.renderer.settings.notation.extendBendArrowsOnTiedNotes + ) { + endBeat = null; + } + let startX: number = 0; + let endX: number = 0; + let topY: number = cy + startNoteRenderer.y; + // float bottomY = cy + startNoteRenderer.Y + startNoteRenderer.GetNoteY(note); + startX = cx + startNoteRenderer.x; + if (renderPoints[0].value > 0 || note.isContinuedBend) { + startX += startNoteRenderer.getBeatX(note.beat, BeatXPosition.MiddleNotes); + } else { + startX += startNoteRenderer.getNoteX(note, NoteXPosition.Right); + } + // canvas.Color = Color.Random(); + // canvas.FillRect( + // cx + startNoteRenderer.X + startNoteRenderer.GetBeatX(_note.Beat, BeatXPosition.MiddleNotes), + // cy + startNoteRenderer.Y, 10, 10); + // canvas.FillRect( + // cx + startNoteRenderer.X + startNoteRenderer.GetBeatX(_note.Beat, BeatXPosition.EndBeat), + // cy + startNoteRenderer.Y + 10, 10, 10); + if (!endBeat || (endBeat.isLastOfVoice && !endNoteHasBend)) { + endX = cx + endNoteRenderer!.x + endNoteRenderer!.postBeatGlyphsStart; + } else if (endNoteHasBend || !endBeat.nextBeat) { + endX = cx + endNoteRenderer!.x + endNoteRenderer!.getBeatX(endBeat, BeatXPosition.MiddleNotes); + } else if (note.bendType === BendType.Hold) { + endX = cx + endNoteRenderer!.x + endNoteRenderer!.getBeatX(endBeat.nextBeat, BeatXPosition.OnNotes); + } else { + endX = cx + endNoteRenderer!.x + endNoteRenderer!.getBeatX(endBeat.nextBeat, BeatXPosition.PreNotes); + } + if (!isMultiBeatBend) { + endX -= TabBendGlyph.ArrowSize * this.scale; + } + // we need some pixels for the arrow. otherwise we might draw into the next + // note + let width: number = endX - startX; + // calculate offsets per step + let dX: number = width / BendPoint.MaxPosition; + canvas.beginPath(); + for (let i: number = 0, j: number = renderPoints.length - 1; i < j; i++) { + let firstPt: TabBendRenderPoint = renderPoints[i]; + let secondPt: TabBendRenderPoint = renderPoints[i + 1]; + // draw pre-bend if previous + if (i === 0 && firstPt.value !== 0 && !note.isTieDestination) { + this.paintBend(note, new TabBendRenderPoint(0, 0), firstPt, startX, topY, dX, slurText, canvas); + } + if (note.bendType !== BendType.Prebend) { + if(i === 0) { + startX += 2 * this.scale; + } + this.paintBend(note, firstPt, secondPt, startX , topY, dX, slurText, canvas); + } else if (note.isTieOrigin && note.tieDestination!.hasBend) { + secondPt = new TabBendRenderPoint(BendPoint.MaxPosition, firstPt.value); + secondPt.lineValue = firstPt.lineValue; + + this.paintBend( + note, + firstPt, + secondPt, + startX, + topY, + dX, + slurText, + canvas + ); + } + } + canvas.color = color; + } + } + + private paintBend( + note: Note, + firstPt: TabBendRenderPoint, + secondPt: TabBendRenderPoint, + cx: number, + cy: number, + dX: number, + slurText: string, + canvas: ICanvas + ): void { + let r: TabBarRenderer = this.renderer as TabBarRenderer; + let res: RenderingResources = this.renderer.resources; + let overflowOffset: number = r.lineOffset / 2; + let x1: number = cx + dX * firstPt.offset; + let bendValueHeight: number = TabBendGlyph.BendValueHeight * this.scale; + let y1: number = cy - bendValueHeight * firstPt.lineValue; + if (firstPt.value === 0) { + if (secondPt.offset === firstPt.offset) { + y1 += r.getNoteY(note.beat.maxStringNote!, NoteYPosition.Top); + } else { + y1 += r.getNoteY(note, NoteYPosition.Center); + } + } else { + y1 += overflowOffset; + } + let x2: number = cx + dX * secondPt.offset; + let y2: number = cy - bendValueHeight * secondPt.lineValue; + if (secondPt.lineValue === 0) { + y2 += r.getNoteY(note, NoteYPosition.Center); + } else { + y2 += overflowOffset; + } + // what type of arrow? (up/down) + let arrowOffset: number = 0; + let arrowSize: number = TabBendGlyph.ArrowSize * this.scale; + if (secondPt.value > firstPt.value) { + if (y2 + arrowSize > y1) { + y2 = y1 - arrowSize; + } + canvas.beginPath(); + canvas.moveTo(x2, y2); + canvas.lineTo(x2 - arrowSize * 0.5, y2 + arrowSize); + canvas.lineTo(x2 + arrowSize * 0.5, y2 + arrowSize); + canvas.closePath(); + canvas.fill(); + arrowOffset = arrowSize; + } else if (secondPt.value !== firstPt.value) { + if (y2 < y1) { + y2 = y1 + arrowSize; + } + canvas.beginPath(); + canvas.moveTo(x2, y2); + canvas.lineTo(x2 - arrowSize * 0.5, y2 - arrowSize); + canvas.lineTo(x2 + arrowSize * 0.5, y2 - arrowSize); + canvas.closePath(); + canvas.fill(); + arrowOffset = -arrowSize; + } + canvas.stroke(); + if (firstPt.value === secondPt.value) { + // draw horizontal dashed line + // to really have the line ending at the right position + // we draw from right to left. it's okay if the space is at the beginning + if (firstPt.lineValue > 0) { + let dashX: number = x2; + let dashSize: number = TabBendGlyph.DashSize * this.scale; + let end: number = x1 + dashSize; + let dashes: number = (dashX - x1) / (dashSize * 2); + if (dashes < 1) { + canvas.moveTo(dashX, y1); + canvas.lineTo(x1, y1); + } else { + while (dashX > end) { + canvas.moveTo(dashX, y1); + canvas.lineTo(dashX - dashSize, y1); + dashX -= dashSize * 2; + } + } + canvas.stroke(); + } + } else { + if (x2 > x1) { + // draw bezier lien from first to second point + canvas.moveTo(x1, y1); + canvas.bezierCurveTo((x1 + x2) / 2, y1, x2, y1, x2, y2 + arrowOffset); + canvas.stroke(); + } else { + canvas.moveTo(x1, y1); + canvas.lineTo(x2, y2); + canvas.stroke(); + } + } + if (slurText && firstPt.offset < secondPt.offset) { + canvas.font = res.graceFont; + let size: number = canvas.measureText(slurText); + let y: number = 0; + let x: number = 0; + if (y1 > y2) { + let h: number = Math.abs(y1 - y2); + y = h > canvas.font.size * 1.3 ? y1 - h / 2 : y1; + x = (x1 + x2 - size) / 2; + } else { + y = y1; + x = x2 - size; + } + canvas.fillText(slurText, x, y); + } + if (secondPt.value !== 0 && firstPt.value !== secondPt.value) { + let dV: number = secondPt.value; + let up: boolean = secondPt.value > firstPt.value; + dV = Math.abs(dV); + // calculate label + let s: string = ''; + // Full Steps + if (dV === 4) { + s = 'full'; + dV -= 4; + } else if (dV >= 4 || dV <= -4) { + let steps: number = (dV / 4) | 0; + s += steps; + // Quaters + dV -= steps * 4; + } + if (dV > 0) { + s += TabBendGlyph.getFractionSign(dV); + } + if (s !== '') { + y2 = cy - bendValueHeight * secondPt.value; + let startY: number = y2; + if (!up) { + startY = y1 + (Math.abs(y2 - y1) * 1) / 3; + } + // draw label + canvas.font = res.tablatureFont; + let size: number = canvas.measureText(s); + let y: number = startY - res.tablatureFont.size * 0.5 - 2 * this.scale; + let x: number = x2 - size / 2; + canvas.fillText(s, x, y); + } + } + } + + public static getFractionSign(steps: number): string { + switch (steps) { + case 1: + return '¼'; + case 2: + return '½'; + case 3: + return '¾'; + default: + return steps + '/ 4'; + } + } +} diff --git a/src/rendering/glyphs/TabBendRenderPoint.ts b/src/rendering/glyphs/TabBendRenderPoint.ts new file mode 100644 index 000000000..bd229e972 --- /dev/null +++ b/src/rendering/glyphs/TabBendRenderPoint.ts @@ -0,0 +1,10 @@ +import { BendPoint } from '@src/model/BendPoint'; + +export class TabBendRenderPoint extends BendPoint { + public lineValue: number = 0; + + public constructor(offset: number = 0, value: number = 0) { + super(offset, value); + this.lineValue = value; + } +} diff --git a/src/rendering/glyphs/TabBrushGlyph.ts b/src/rendering/glyphs/TabBrushGlyph.ts new file mode 100644 index 000000000..600a85052 --- /dev/null +++ b/src/rendering/glyphs/TabBrushGlyph.ts @@ -0,0 +1,74 @@ +import { Beat } from '@src/model/Beat'; +import { BrushType } from '@src/model/BrushType'; +import { VibratoType } from '@src/model/VibratoType'; +import { ICanvas } from '@src/platform/ICanvas'; +import { Glyph } from '@src/rendering/glyphs/Glyph'; +import { NoteVibratoGlyph } from '@src/rendering/glyphs/NoteVibratoGlyph'; +import { TabBarRenderer } from '@src/rendering/TabBarRenderer'; +import { NoteYPosition } from '../BarRendererBase'; + +export class TabBrushGlyph extends Glyph { + private _beat: Beat; + + public constructor(beat: Beat) { + super(0, 0); + this._beat = beat; + } + + public doLayout(): void { + this.width = 10 * this.scale; + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + let tabBarRenderer: TabBarRenderer = this.renderer as TabBarRenderer; + let startY: number = + cy + this.x + (tabBarRenderer.getNoteY(this._beat.maxNote!, NoteYPosition.Top)); + let endY: number = + cy + this.y + tabBarRenderer.getNoteY(this._beat.minNote!, NoteYPosition.Bottom); + let arrowX: number = (cx + this.x + this.width / 2) | 0; + let arrowSize: number = 8 * this.scale; + if (this._beat.brushType !== BrushType.None) { + if (this._beat.brushType === BrushType.BrushUp || this._beat.brushType === BrushType.BrushDown) { + canvas.beginPath(); + canvas.moveTo(arrowX, startY); + canvas.lineTo(arrowX, endY); + canvas.stroke(); + } else if (this._beat.brushType === BrushType.ArpeggioUp) { + let lineStartY: number = startY; + let lineEndY: number = endY - arrowSize; + canvas.beginRotate(cx + this.x + 4 * this.scale, lineEndY, -90); + let glyph: NoteVibratoGlyph = new NoteVibratoGlyph(0, 0, VibratoType.Slight, 1.2, true); + glyph.renderer = this.renderer; + glyph.doLayout(); + glyph.width = Math.abs(lineEndY - lineStartY); + glyph.paint(0, 0, canvas); + canvas.endRotate(); + } else if (this._beat.brushType === BrushType.ArpeggioDown) { + let lineStartY: number = startY + arrowSize; + let lineEndY: number = endY; + canvas.beginRotate(cx + this.x + 4 * this.scale, lineStartY, 90); + let glyph: NoteVibratoGlyph = new NoteVibratoGlyph(0, 0, VibratoType.Slight, 1.2, true); + glyph.renderer = this.renderer; + glyph.doLayout(); + glyph.width = Math.abs(lineEndY - lineStartY); + glyph.paint(0, 0, canvas); + canvas.endRotate(); + } + if (this._beat.brushType === BrushType.BrushUp || this._beat.brushType === BrushType.ArpeggioUp) { + canvas.beginPath(); + canvas.moveTo(arrowX, endY); + canvas.lineTo(arrowX + arrowSize / 2, endY - arrowSize); + canvas.lineTo(arrowX - arrowSize / 2, endY - arrowSize); + canvas.closePath(); + canvas.fill(); + } else { + canvas.beginPath(); + canvas.moveTo(arrowX, startY); + canvas.lineTo(arrowX + arrowSize / 2, startY + arrowSize); + canvas.lineTo(arrowX - arrowSize / 2, startY + arrowSize); + canvas.closePath(); + canvas.fill(); + } + } + } +} diff --git a/src/rendering/glyphs/TabClefGlyph.ts b/src/rendering/glyphs/TabClefGlyph.ts new file mode 100644 index 000000000..5f11a3e18 --- /dev/null +++ b/src/rendering/glyphs/TabClefGlyph.ts @@ -0,0 +1,27 @@ +import { ICanvas } from '@src/platform/ICanvas'; +import { Glyph } from '@src/rendering/glyphs/Glyph'; +import { MusicFontSymbol } from '@src/rendering/glyphs/MusicFontSymbol'; + +export class TabClefGlyph extends Glyph { + public constructor(x: number, y: number) { + super(x, y); + } + + public doLayout(): void { + this.width = 28 * this.scale; + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + let strings: number = this.renderer.bar.staff.tuning.length; + let correction: number = strings * this.scale * 0.5; + let symbol: MusicFontSymbol = strings <= 4 ? MusicFontSymbol.ClefTabSmall : MusicFontSymbol.ClefTab; + let scale: number = strings <= 4 ? strings / 4.5 : strings / 6.5; + canvas.fillMusicFontSymbol( + cx + this.x + 5 * this.scale, + cy + this.y - correction, + scale * this.scale, + symbol, + false + ); + } +} diff --git a/src/rendering/glyphs/TabNoteChordGlyph.ts b/src/rendering/glyphs/TabNoteChordGlyph.ts new file mode 100644 index 000000000..4e631fa41 --- /dev/null +++ b/src/rendering/glyphs/TabNoteChordGlyph.ts @@ -0,0 +1,138 @@ +import { Beat } from '@src/model/Beat'; +import { Note } from '@src/model/Note'; +import { ICanvas, TextBaseline } from '@src/platform/ICanvas'; +import { Glyph } from '@src/rendering/glyphs/Glyph'; +import { NoteNumberGlyph } from '@src/rendering/glyphs/NoteNumberGlyph'; +import { BeamingHelper } from '@src/rendering/utils/BeamingHelper'; +import { RenderingResources } from '@src/RenderingResources'; +import { NoteXPosition, NoteYPosition } from '../BarRendererBase'; +import { BeatBounds } from '../utils/BeatBounds'; + +export class TabNoteChordGlyph extends Glyph { + private _notes: NoteNumberGlyph[] = []; + private _isGrace: boolean; + + public beat!: Beat; + public beamingHelper!: BeamingHelper; + public minStringNote: Note | null = null; + public beatEffects: Map = new Map(); + public notesPerString: Map = new Map(); + public noteStringWidth: number = 0; + + public constructor(x: number, y: number, isGrace: boolean) { + super(x, y); + this._isGrace = isGrace; + } + + public buildBoundingsLookup(beatBounds:BeatBounds, cx:number, cy:number) { + for(const note of this._notes) { + note.buildBoundingsLookup(beatBounds, cx + this.x, cy + this.y); + } + } + + public getNoteX(note: Note, requestedPosition: NoteXPosition): number { + if (this.notesPerString.has(note.string)) { + let n = this.notesPerString.get(note.string)!; + + let pos = this.x + n.x; + switch (requestedPosition) { + case NoteXPosition.Left: + break; + case NoteXPosition.Center: + pos += n.noteStringWidth / 2; + break; + case NoteXPosition.Right: + pos += n.width; + break; + } + return pos; + } + return 0; + } + + public getNoteY(note: Note, requestedPosition: NoteYPosition): number { + if (this.notesPerString.has(note.string)) { + const n = this.notesPerString.get(note.string)!; + let pos = this.y + n.y; + + switch (requestedPosition) { + case NoteYPosition.Top: + case NoteYPosition.TopWithStem: + pos -= n.height / 2 + 2 * this.scale; + break; + case NoteYPosition.Center: + break; + case NoteYPosition.Bottom: + case NoteYPosition.BottomWithStem: + pos += n.height / 2; + break; + } + + return pos; + } + return 0; + } + + public doLayout(): void { + let w: number = 0; + let noteStringWidth: number = 0; + for (let i: number = 0, j: number = this._notes.length; i < j; i++) { + let g: NoteNumberGlyph = this._notes[i]; + g.renderer = this.renderer; + g.doLayout(); + if (g.width > w) { + w = g.width; + } + if (g.noteStringWidth > noteStringWidth) { + noteStringWidth = g.noteStringWidth; + } + } + this.noteStringWidth = noteStringWidth; + let tabHeight: number = this.renderer.resources.tablatureFont.size; + let effectY: number = this.getNoteY(this.minStringNote!, NoteYPosition.Center) + tabHeight / 2; + // TODO: take care of actual glyph height + let effectSpacing: number = 7 * this.scale; + this.beatEffects.forEach(g => { + g.y += effectY; + g.x += this.width / 2; + g.renderer = this.renderer; + effectY += effectSpacing; + g.doLayout(); + }); + this.width = w; + } + + public addNoteGlyph(noteGlyph: NoteNumberGlyph, note: Note): void { + this._notes.push(noteGlyph); + this.notesPerString.set(note.string, noteGlyph); + if (!this.minStringNote || note.string < this.minStringNote.string) { + this.minStringNote = note; + } + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + cx += this.x; + cy += this.y; + let res: RenderingResources = this.renderer.resources; + let oldBaseLine: TextBaseline = canvas.textBaseline; + canvas.textBaseline = TextBaseline.Middle; + canvas.font = this._isGrace ? res.graceFont : res.tablatureFont; + let notes: NoteNumberGlyph[] = this._notes; + let w: number = this.width; + for (let g of notes) { + g.renderer = this.renderer; + g.width = w; + g.paint(cx, cy, canvas); + } + canvas.textBaseline = oldBaseLine; + this.beatEffects.forEach(g => { + g.paint(cx, cy, canvas); + }); + } + + public updateBeamingHelper(cx: number): void { + if (this.beamingHelper && this.beamingHelper.isPositionFrom('tab', this.beat)) { + this.beamingHelper.registerBeatLineX('tab', this.beat, cx + this.x + this.width, cx + this.x); + } + } +} diff --git a/src/rendering/glyphs/TabRestGlyph.ts b/src/rendering/glyphs/TabRestGlyph.ts new file mode 100644 index 000000000..c950d57d3 --- /dev/null +++ b/src/rendering/glyphs/TabRestGlyph.ts @@ -0,0 +1,37 @@ +import { Duration } from '@src/model/Duration'; +import { ICanvas } from '@src/platform/ICanvas'; +import { MusicFontGlyph } from '@src/rendering/glyphs/MusicFontGlyph'; +import { ScoreRestGlyph } from '@src/rendering/glyphs/ScoreRestGlyph'; +import { BeamingHelper } from '@src/rendering/utils/BeamingHelper'; + +export class TabRestGlyph extends MusicFontGlyph { + private _isVisibleRest: boolean; + private _duration: Duration; + public beamingHelper!: BeamingHelper; + + public constructor(x: number, y: number, isVisibleRest: boolean, duration: Duration) { + super(x, y, 1, ScoreRestGlyph.getSymbol(duration)); + this._isVisibleRest = isVisibleRest; + this._duration = duration; + } + + public doLayout(): void { + if (this._isVisibleRest) { + this.width = ScoreRestGlyph.getSize(this._duration) * this.scale; + } else { + this.width = 10 * this.scale; + } + } + + public updateBeamingHelper(cx: number): void { + if (this.beamingHelper && this.beamingHelper.isPositionFrom('tab', this.beat)) { + this.beamingHelper.registerBeatLineX('tab', this.beat, cx + this.x + this.width, cx + this.x); + } + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + if (this._isVisibleRest) { + super.paint(cx, cy, canvas); + } + } +} diff --git a/src/rendering/glyphs/TabSlideLineGlyph.ts b/src/rendering/glyphs/TabSlideLineGlyph.ts new file mode 100644 index 000000000..bebc8003f --- /dev/null +++ b/src/rendering/glyphs/TabSlideLineGlyph.ts @@ -0,0 +1,176 @@ +import { Note } from '@src/model/Note'; +import { SlideInType } from '@src/model/SlideInType'; +import { SlideOutType } from '@src/model/SlideOutType'; +import { VibratoType } from '@src/model/VibratoType'; +import { ICanvas } from '@src/platform/ICanvas'; +import { BarRendererBase, NoteYPosition, NoteXPosition } from '@src/rendering/BarRendererBase'; +import { BeatXPosition } from '@src/rendering/BeatXPosition'; +import { BeatContainerGlyph } from '@src/rendering/glyphs/BeatContainerGlyph'; +import { Glyph } from '@src/rendering/glyphs/Glyph'; +import { NoteVibratoGlyph } from '@src/rendering/glyphs/NoteVibratoGlyph'; +import { TabBarRenderer } from '@src/rendering/TabBarRenderer'; + +export class TabSlideLineGlyph extends Glyph { + private _inType: SlideInType; + private _outType: SlideOutType; + private _startNote: Note; + private _parent: BeatContainerGlyph; + + public constructor(inType: SlideInType, outType: SlideOutType, startNote: Note, parent: BeatContainerGlyph) { + super(0, 0); + this._inType = inType; + this._outType = outType; + this._startNote = startNote; + this._parent = parent; + } + + public doLayout(): void { + this.width = 0; + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + this.paintSlideIn(cx, cy, canvas); + this.paintSlideOut(cx, cy, canvas); + } + + private paintSlideIn(cx: number, cy: number, canvas: ICanvas): void { + let startNoteRenderer: TabBarRenderer = this.renderer as TabBarRenderer; + let sizeX: number = 12 * this.scale; + let sizeY: number = 3 * this.scale; + let startX: number = 0; + let startY: number = 0; + let endX: number = 0; + let endY: number = 0; + switch (this._inType) { + case SlideInType.IntoFromBelow: + endX = cx + startNoteRenderer.x + startNoteRenderer.getNoteX(this._startNote, NoteXPosition.Left); + endY = cy + startNoteRenderer.y + startNoteRenderer.getNoteY(this._startNote, NoteYPosition.Center); + startX = endX - sizeX; + startY = cy + startNoteRenderer.y + startNoteRenderer.getNoteY(this._startNote, NoteYPosition.Center) + sizeY; + break; + case SlideInType.IntoFromAbove: + endX = cx + startNoteRenderer.x + startNoteRenderer.getNoteX(this._startNote, NoteXPosition.Left); + endY = cy + startNoteRenderer.y + startNoteRenderer.getNoteY(this._startNote, NoteYPosition.Center); + startX = endX - sizeX; + startY = cy + startNoteRenderer.y + startNoteRenderer.getNoteY(this._startNote, NoteYPosition.Center) - sizeY; + break; + default: + return; + } + this.paintSlideLine(canvas, false, startX, endX, startY, endY); + } + + private paintSlideOut(cx: number, cy: number, canvas: ICanvas): void { + let startNoteRenderer: TabBarRenderer = this.renderer as TabBarRenderer; + let sizeX: number = 12 * this.scale; + let sizeY: number = 3 * this.scale; + let startX: number = 0; + let startY: number = 0; + let endX: number = 0; + let endY: number = 0; + let waves: boolean = false; + + const endXOffset = 2 * this.scale; + + switch (this._outType) { + case SlideOutType.Shift: + case SlideOutType.Legato: + startX = + cx + + startNoteRenderer.x + + startNoteRenderer.getBeatX(this._startNote.beat, BeatXPosition.PostNotes); + startY = cy + startNoteRenderer.y + startNoteRenderer.getNoteY(this._startNote, NoteYPosition.Center); + if (this._startNote.slideTarget) { + let endNoteRenderer: BarRendererBase = this.renderer.scoreRenderer.layout!.getRendererForBar( + this.renderer.staff.staveId, + this._startNote.slideTarget.beat.voice.bar + )!; + if (!endNoteRenderer || endNoteRenderer.staff !== startNoteRenderer.staff) { + endX = cx + startNoteRenderer.x + this._parent.x; + endY = startY; + } else { + endX = + cx + + endNoteRenderer.x + + endNoteRenderer.getBeatX(this._startNote.slideTarget.beat, BeatXPosition.OnNotes) + - endXOffset; + endY = + cy + + endNoteRenderer.y + + endNoteRenderer.getNoteY(this._startNote.slideTarget, NoteYPosition.Center); + } + + if (this._startNote.slideTarget.fret > this._startNote.fret) { + startY += sizeY; + endY -= sizeY; + } else { + startY -= sizeY; + endY += sizeY; + } + } else { + endX = cx + startNoteRenderer.x + this._parent.x; + endY = startY; + } + break; + case SlideOutType.OutUp: + startX = cx + startNoteRenderer.x + startNoteRenderer.getNoteX(this._startNote, NoteXPosition.Right); + startY = cy + startNoteRenderer.y + startNoteRenderer.getNoteY(this._startNote, NoteYPosition.Center); + endX = startX + sizeX - endXOffset; + endY = cy + startNoteRenderer.y + startNoteRenderer.getNoteY(this._startNote, NoteYPosition.Center) - sizeY; + break; + case SlideOutType.OutDown: + startX = cx + startNoteRenderer.x + startNoteRenderer.getNoteX(this._startNote, NoteXPosition.Right); + startY = cy + startNoteRenderer.y + startNoteRenderer.getNoteY(this._startNote, NoteYPosition.Center); + endX = startX + sizeX - endXOffset; + endY = cy + startNoteRenderer.y + startNoteRenderer.getNoteY(this._startNote, NoteYPosition.Center) + sizeY; + break; + case SlideOutType.PickSlideDown: + startX = cx + startNoteRenderer.x + startNoteRenderer.getNoteX(this._startNote, NoteXPosition.Right); + startY = cy + startNoteRenderer.y + startNoteRenderer.getNoteY(this._startNote, NoteYPosition.Center); + endX = + cx + startNoteRenderer.x + startNoteRenderer.getBeatX(this._startNote.beat, BeatXPosition.EndBeat); + endY = startY + sizeY * 3; + waves = true; + break; + case SlideOutType.PickSlideUp: + startX = cx + startNoteRenderer.x + startNoteRenderer.getNoteX(this._startNote, NoteXPosition.Right); + startY = cy + startNoteRenderer.y + startNoteRenderer.getNoteY(this._startNote, NoteYPosition.Center); + endX = + cx + startNoteRenderer.x + startNoteRenderer.getBeatX(this._startNote.beat, BeatXPosition.EndBeat); + endY = startY - sizeY * 3; + waves = true; + break; + default: + return; + } + this.paintSlideLine(canvas, waves, startX, endX, startY, endY); + } + + private paintSlideLine( + canvas: ICanvas, + waves: boolean, + startX: number, + endX: number, + startY: number, + endY: number + ): void { + if (waves) { + let b: number = endX - startX; + let a: number = endY - startY; + let c: number = Math.sqrt(Math.pow(a, 2) + Math.pow(b, 2)); + let angle: number = Math.asin(a / c) * (180 / Math.PI); + canvas.beginRotate(startX, startY, angle); + let glyph: NoteVibratoGlyph = new NoteVibratoGlyph(0, 0, VibratoType.Slight, 1.2); + glyph.renderer = this.renderer; + glyph.doLayout(); + glyph.width = b; + glyph.paint(0, 0, canvas); + canvas.endRotate(); + } else { + canvas.beginPath(); + canvas.moveTo(startX, startY); + canvas.lineTo(endX, endY); + canvas.stroke(); + } + } +} diff --git a/src/rendering/glyphs/TabSlurGlyph.ts b/src/rendering/glyphs/TabSlurGlyph.ts new file mode 100644 index 000000000..5f66be074 --- /dev/null +++ b/src/rendering/glyphs/TabSlurGlyph.ts @@ -0,0 +1,81 @@ +import { Note } from '@src/model/Note'; +import { ICanvas } from '@src/platform/ICanvas'; +import { BarRendererBase } from '@src/rendering/BarRendererBase'; +import { TabTieGlyph } from '@src/rendering/glyphs/TabTieGlyph'; +import { TabBarRenderer } from '@src/rendering/TabBarRenderer'; +import { BeamDirection } from '@src/rendering/utils/BeamDirection'; + +export class TabSlurGlyph extends TabTieGlyph { + private _direction: BeamDirection; + private _forSlide: boolean; + + public constructor(startNote: Note, endNote: Note, forSlide: boolean, forEnd: boolean = false) { + super(startNote, endNote, forEnd); + this._direction = TabTieGlyph.getBeamDirectionForNote(startNote); + this._forSlide = forSlide; + } + + protected getTieHeight(startX: number, startY: number, endX: number, endY: number): number { + return Math.log(endX - startX + 1) * this.renderer.settings.notation.slurHeight; + } + + public tryExpand(startNote: Note, endNote: Note, forSlide: boolean, forEnd: boolean): boolean { + // same type required + if (this._forSlide !== forSlide) { + return false; + } + if (this.forEnd !== forEnd) { + return false; + } + // same start and endbeat + if (this.startNote.beat.id !== startNote.beat.id) { + return false; + } + if (this.endNote.beat.id !== endNote.beat.id) { + return false; + } + // same draw direction + if (this._direction !== TabTieGlyph.getBeamDirectionForNote(startNote)) { + return false; + } + // if we can expand, expand in correct direction + switch (this._direction) { + case BeamDirection.Up: + if (startNote.realValue > this.startNote.realValue) { + this.startNote = startNote; + this.startBeat = startNote.beat; + } + if (endNote.realValue > this.endNote.realValue) { + this.endNote = endNote; + this.endBeat = endNote.beat; + } + break; + case BeamDirection.Down: + if (startNote.realValue < this.startNote.realValue) { + this.startNote = startNote; + this.startBeat = startNote.beat; + } + if (endNote.realValue < this.endNote.realValue) { + this.endNote = endNote; + this.endBeat = endNote.beat; + } + break; + } + return true; + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + let startNoteRenderer: BarRendererBase = this.renderer.scoreRenderer.layout!.getRendererForBar( + this.renderer.staff.staveId, + this.startBeat!.voice.bar + )!; + let direction: BeamDirection = this.getBeamDirection(this.startBeat!, startNoteRenderer); + let slurId: string = 'tab.slur.' + this.startNote.beat.id + '.' + this.endNote.beat.id + '.' + direction; + let renderer: TabBarRenderer = this.renderer as TabBarRenderer; + let isSlurRendered: boolean = renderer.staff.getSharedLayoutData(slurId, false); + if (!isSlurRendered) { + renderer.staff.setSharedLayoutData(slurId, true); + super.paint(cx, cy, canvas); + } + } +} diff --git a/src/rendering/glyphs/TabTieGlyph.ts b/src/rendering/glyphs/TabTieGlyph.ts new file mode 100644 index 000000000..77b79ad60 --- /dev/null +++ b/src/rendering/glyphs/TabTieGlyph.ts @@ -0,0 +1,63 @@ +import { Beat } from '@src/model/Beat'; +import { Note } from '@src/model/Note'; +import { BarRendererBase, NoteYPosition, NoteXPosition } from '@src/rendering/BarRendererBase'; +import { TieGlyph } from '@src/rendering/glyphs/TieGlyph'; +import { BeamDirection } from '@src/rendering/utils/BeamDirection'; + +export class TabTieGlyph extends TieGlyph { + protected startNote: Note; + protected endNote: Note; + + public constructor(startNote: Note, endNote: Note, forEnd: boolean = false) { + super(startNote.beat, endNote.beat, forEnd); + this.startNote = startNote; + this.endNote = endNote; + } + + protected getTieHeight(startX: number, startY: number, endX: number, endY: number): number { + if(this.startNote === this.endNote) { + return 15; + } + return super.getTieHeight(startX, startY, endX, endY); + } + + protected getBeamDirection(beat: Beat, noteRenderer: BarRendererBase): BeamDirection { + if(this.startNote === this.endNote) { + return BeamDirection.Up; + } + return TabTieGlyph.getBeamDirectionForNote(this.startNote); + } + + protected static getBeamDirectionForNote(note: Note): BeamDirection { + return note.string > 3 ? BeamDirection.Up : BeamDirection.Down; + } + + protected getStartY(): number { + if(this.startNote === this.endNote) { + return this.startNoteRenderer!.getNoteY(this.startNote, NoteYPosition.Center); + } + + if(this.tieDirection === BeamDirection.Up) { + return this.startNoteRenderer!.getNoteY(this.startNote, NoteYPosition.Top); + } + return this.startNoteRenderer!.getNoteY(this.startNote, NoteYPosition.Bottom); + } + + protected getEndY(): number { + return this.getStartY(); + } + + protected getStartX(): number { + if(this.startNote === this.endNote) { + return this.getEndX() - 20 * this.scale; + } + return this.startNoteRenderer!.getNoteX(this.startNote, NoteXPosition.Center); + } + + protected getEndX(): number { + if(this.startNote === this.endNote) { + return this.endNoteRenderer!.getNoteX(this.endNote, NoteXPosition.Left); + } + return this.endNoteRenderer!.getNoteX(this.endNote, NoteXPosition.Center); + } +} diff --git a/src/rendering/glyphs/TabTimeSignatureGlyph.ts b/src/rendering/glyphs/TabTimeSignatureGlyph.ts new file mode 100644 index 000000000..87901aa35 --- /dev/null +++ b/src/rendering/glyphs/TabTimeSignatureGlyph.ts @@ -0,0 +1,34 @@ +import { TimeSignatureGlyph } from '@src/rendering/glyphs/TimeSignatureGlyph'; +import { TabBarRenderer } from '@src/rendering/TabBarRenderer'; +import { NoteHeadGlyph } from '@src/rendering/glyphs/NoteHeadGlyph'; + +export class TabTimeSignatureGlyph extends TimeSignatureGlyph { + protected get commonY(): number { + let renderer: TabBarRenderer = this.renderer as TabBarRenderer; + return renderer.getTabY(0, 0); + } + + protected get numeratorY(): number { + let renderer: TabBarRenderer = this.renderer as TabBarRenderer; + let offset: number = renderer.bar.staff.tuning.length <= 4 ? 1 / 4 : 1 / 3; + return renderer.lineOffset * renderer.bar.staff.tuning.length * offset * this.scale; + } + + protected get denominatorY(): number { + let renderer: TabBarRenderer = this.renderer as TabBarRenderer; + let offset: number = 3 / 5; + return renderer.lineOffset * renderer.bar.staff.tuning.length * offset * this.scale; + } + + protected get commonScale(): number { + return 1; + } + + protected get numberScale(): number { + let renderer: TabBarRenderer = this.renderer as TabBarRenderer; + if (renderer.bar.staff.tuning.length <= 4) { + return NoteHeadGlyph.GraceScale; + } + return 1; + } +} diff --git a/src/rendering/glyphs/TabWhammyBarGlyph.ts b/src/rendering/glyphs/TabWhammyBarGlyph.ts new file mode 100644 index 000000000..4fb7a32d8 --- /dev/null +++ b/src/rendering/glyphs/TabWhammyBarGlyph.ts @@ -0,0 +1,274 @@ +import { Beat } from '@src/model/Beat'; +import { BendPoint } from '@src/model/BendPoint'; +import { BendStyle } from '@src/model/BendStyle'; +import { WhammyType } from '@src/model/WhammyType'; +import { NotationMode, NotationElement } from '@src/NotationSettings'; +import { ICanvas, TextAlign } from '@src/platform/ICanvas'; +import { BarRendererBase } from '@src/rendering/BarRendererBase'; +import { BeatXPosition } from '@src/rendering/BeatXPosition'; +import { Glyph } from '@src/rendering/glyphs/Glyph'; +import { TabBendGlyph } from '@src/rendering/glyphs/TabBendGlyph'; +import { TabBarRenderer } from '@src/rendering/TabBarRenderer'; +import { RenderingResources } from '@src/RenderingResources'; + +export class TabWhammyBarGlyph extends Glyph { + private static readonly TopOffsetSharedDataKey: string = 'tab.whammy.topoffset'; + public static readonly PerHalfSize: number = 6; + private static readonly DashSize: number = 3; + private _beat: Beat; + private _renderPoints: BendPoint[]; + private _isSimpleDip: boolean = false; + + public constructor(beat: Beat) { + super(0, 0); + this._beat = beat; + this._renderPoints = this.createRenderingPoints(beat); + } + + private createRenderingPoints(beat: Beat): BendPoint[] { + // advanced rendering + if (beat.whammyBarType === WhammyType.Custom) { + return beat.whammyBarPoints; + } + let renderingPoints: BendPoint[] = []; + // Guitar Pro Rendering Note: + // Last point of bend is always at end of the beat even + // though it might not be 100% correct from timing perspective. + switch (beat.whammyBarType) { + case WhammyType.Dive: + case WhammyType.Hold: + case WhammyType.PrediveDive: + case WhammyType.Predive: + renderingPoints.push(new BendPoint(0, beat.whammyBarPoints[0].value)); + renderingPoints.push(new BendPoint(BendPoint.MaxPosition, beat.whammyBarPoints[1].value)); + break; + case WhammyType.Dip: + renderingPoints.push(new BendPoint(0, beat.whammyBarPoints[0].value)); + renderingPoints.push(new BendPoint((BendPoint.MaxPosition / 2) | 0, beat.whammyBarPoints[1].value)); + renderingPoints.push( + new BendPoint(BendPoint.MaxPosition, beat.whammyBarPoints[beat.whammyBarPoints.length - 1].value) + ); + break; + } + return renderingPoints; + } + + public doLayout(): void { + super.doLayout(); + this._isSimpleDip = + this.renderer.settings.notation.notationMode === NotationMode.SongBook && + this._beat.whammyBarType === WhammyType.Dip; + // + // Get the min and max values for all combined whammys + let minValue: BendPoint | null = null; + let maxValue: BendPoint | null = null; + let beat: Beat | null = this._beat; + while (beat && beat.hasWhammyBar) { + if (!minValue || minValue.value > beat.minWhammyPoint!.value) { + minValue = beat.minWhammyPoint; + } + if (!maxValue || maxValue.value < beat.maxWhammyPoint!.value) { + maxValue = beat.maxWhammyPoint; + } + beat = beat.nextBeat; + } + let topOffset: number = maxValue!.value > 0 ? Math.abs(this.getOffset(maxValue!.value)) : 0; + if ( + topOffset > 0 || + this._beat.whammyBarPoints[0].value !== 0 || + this.renderer.settings.notation.isNotationElementVisible(NotationElement.ZerosOnDiveWhammys) + ) { + topOffset += this.renderer.resources.tablatureFont.size * 2; + } + let bottomOffset: number = minValue!.value < 0 ? Math.abs(this.getOffset(minValue!.value)) : 0; + this.renderer.registerOverflowTop(topOffset + bottomOffset); + let currentOffset: number = this.renderer.staff.getSharedLayoutData( + TabWhammyBarGlyph.TopOffsetSharedDataKey, + -1 + ); + if (topOffset > currentOffset) { + this.renderer.staff.setSharedLayoutData(TabWhammyBarGlyph.TopOffsetSharedDataKey, topOffset); + } + } + + private getOffset(value: number): number { + if (value === 0) { + return 0; + } + let offset: number = + TabWhammyBarGlyph.PerHalfSize * this.scale + + Math.log2(Math.abs(value) / 2) * TabWhammyBarGlyph.PerHalfSize * this.scale; + if (value < 0) { + offset = -offset; + } + return offset; + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + let startNoteRenderer: BarRendererBase = this.renderer; + let endBeat: Beat | null = this._beat.nextBeat; + let endNoteRenderer: TabBarRenderer | null = null; + let endXPositionType: BeatXPosition = BeatXPosition.PreNotes; + if (endBeat) { + endNoteRenderer = this.renderer.scoreRenderer.layout!.getRendererForBar( + this.renderer.staff.staveId, + endBeat.voice.bar + ) as TabBarRenderer; + if (!endNoteRenderer || endNoteRenderer.staff !== startNoteRenderer.staff) { + endBeat = null; + endNoteRenderer = null; + } else if (endNoteRenderer !== startNoteRenderer && !endBeat.hasWhammyBar) { + endBeat = null; + endNoteRenderer = null; + } else { + endXPositionType = + endBeat.hasWhammyBar && + (startNoteRenderer.settings.notation.notationMode !== NotationMode.SongBook || + endBeat.whammyBarType !== WhammyType.Dip) + ? BeatXPosition.MiddleNotes + : BeatXPosition.PreNotes; + } + } + let startX: number = 0; + let endX: number = 0; + if (this._isSimpleDip) { + startX = + cx + + startNoteRenderer.x + + startNoteRenderer.getBeatX(this._beat, BeatXPosition.OnNotes) - + 2 * this.scale; + endX = + cx + + startNoteRenderer.x + + startNoteRenderer.getBeatX(this._beat, BeatXPosition.PostNotes) + + 2 * this.scale; + } else { + startX = cx + startNoteRenderer.x + startNoteRenderer.getBeatX(this._beat, BeatXPosition.MiddleNotes); + endX = !endNoteRenderer + ? cx + startNoteRenderer.x + startNoteRenderer.width - 2 * this.scale + : cx + endNoteRenderer.x + endNoteRenderer.getBeatX(endBeat!, endXPositionType); + } + let old: TextAlign = canvas.textAlign; + canvas.textAlign = TextAlign.Center; + if (this._renderPoints.length >= 2) { + let dx: number = (endX - startX) / BendPoint.MaxPosition; + canvas.beginPath(); + let zeroY: number = + cy + this.renderer.staff.getSharedLayoutData(TabWhammyBarGlyph.TopOffsetSharedDataKey, 0); + let slurText: string = this._beat.whammyStyle === BendStyle.Gradual ? 'grad.' : ''; + for (let i: number = 0, j: number = this._renderPoints.length - 1; i < j; i++) { + let firstPt: BendPoint = this._renderPoints[i]; + let secondPt: BendPoint = this._renderPoints[i + 1]; + let nextPt: BendPoint | null = i < j - 2 ? this._renderPoints[i + 2] : null; + let isFirst: boolean = i === 0; + // draw pre-bend if previous + if (i === 0 && firstPt.value !== 0 && !this._beat.isContinuedWhammy) { + this.paintWhammy(false, new BendPoint(0, 0), firstPt, secondPt, startX, zeroY, dx, canvas); + isFirst = false; + } + this.paintWhammy(isFirst, firstPt, secondPt, nextPt, startX, zeroY, dx, canvas, slurText); + slurText = ''; + } + canvas.stroke(); + } + canvas.textAlign = old; + } + + private paintWhammy( + isFirst: boolean, + firstPt: BendPoint, + secondPt: BendPoint, + nextPt: BendPoint | null, + cx: number, + cy: number, + dx: number, + canvas: ICanvas, + slurText?: string + ): void { + let x1: number = cx + dx * firstPt.offset; + let x2: number = cx + dx * secondPt.offset; + let y1: number = cy - this.getOffset(firstPt.value); + let y2: number = cy - this.getOffset(secondPt.value); + if (firstPt.offset === secondPt.offset) { + let dashSize: number = TabWhammyBarGlyph.DashSize * this.scale; + let dashes: number = Math.abs(y2 - y1) / (dashSize * 2); + if (dashes < 1) { + canvas.moveTo(x1, y1); + canvas.lineTo(x2, y2); + } else { + let dashEndY: number = Math.max(y1, y2); + let dashStartY: number = Math.min(y1, y2); + while (dashEndY > dashStartY) { + canvas.moveTo(x1, dashStartY); + canvas.lineTo(x1, dashStartY + dashSize); + dashStartY += dashSize * 2; + } + } + canvas.stroke(); + } else if (firstPt.value === secondPt.value) { + let dashSize: number = TabWhammyBarGlyph.DashSize * this.scale; + let dashes: number = Math.abs(x2 - x1) / (dashSize * 2); + if (dashes < 1) { + canvas.moveTo(x1, y1); + canvas.lineTo(x2, y2); + } else { + let dashEndX: number = Math.max(x1, x2); + let dashStartX: number = Math.min(x1, x2); + while (dashEndX > dashStartX) { + canvas.moveTo(dashEndX, y1); + canvas.lineTo(dashEndX - dashSize, y1); + dashEndX -= dashSize * 2; + } + } + canvas.stroke(); + } else { + canvas.moveTo(x1, y1); + canvas.lineTo(x2, y2); + } + let res: RenderingResources = this.renderer.resources; + if (isFirst && !this._beat.isContinuedWhammy && !this._isSimpleDip) { + let y: number = y1; + y -= res.tablatureFont.size + 2 * this.scale; + if (this.renderer.settings.notation.isNotationElementVisible(NotationElement.ZerosOnDiveWhammys)) { + canvas.fillText('0', x1, y); + } + if (slurText) { + y -= res.tablatureFont.size + 2 * this.scale; + canvas.fillText(slurText, x1, y); + } + } + let dV: number = Math.abs(secondPt.value); + if ( + (dV !== 0 || (this.renderer.settings.notation.isNotationElementVisible(NotationElement.ZerosOnDiveWhammys) && !this._isSimpleDip)) && + firstPt.value !== secondPt.value + ) { + let s: string = ''; + if (secondPt.value < 0) { + s += '-'; + } + if (dV >= 4) { + let steps: number = (dV / 4) | 0; + s += steps; + // Quaters + dV -= steps * 4; + } else if (dV === 0) { + s += '0'; + } + if (dV > 0) { + s += TabBendGlyph.getFractionSign(dV); + } + let y: number = 0; + if (this._isSimpleDip) { + y = Math.min(y1, y2) - res.tablatureFont.size - 2 * this.scale; + } else { + y = firstPt.offset === secondPt.offset ? Math.min(y1, y2) : y2; + y -= res.tablatureFont.size + 2 * this.scale; + if (nextPt && nextPt.value > secondPt.value) { + y -= 2 * this.scale; + } + } + let x: number = x2; + canvas.fillText(s, x, y); + } + } +} diff --git a/src/rendering/glyphs/TempoGlyph.ts b/src/rendering/glyphs/TempoGlyph.ts new file mode 100644 index 000000000..a5b330b4d --- /dev/null +++ b/src/rendering/glyphs/TempoGlyph.ts @@ -0,0 +1,32 @@ +import { ICanvas } from '@src/platform/ICanvas'; +import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph'; +import { MusicFontSymbol } from '@src/rendering/glyphs/MusicFontSymbol'; +import { RenderingResources } from '@src/RenderingResources'; +import { NoteHeadGlyph } from '@src/rendering/glyphs/NoteHeadGlyph'; + +export class TempoGlyph extends EffectGlyph { + private _tempo: number = 0; + + public constructor(x: number, y: number, tempo: number) { + super(x, y); + this._tempo = tempo; + } + + public doLayout(): void { + super.doLayout(); + this.height = 25 * this.scale; + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + let res: RenderingResources = this.renderer.resources; + canvas.font = res.markerFont; + canvas.fillMusicFontSymbol( + cx + this.x, + cy + this.y + this.height * 0.8, + this.scale * NoteHeadGlyph.GraceScale, + MusicFontSymbol.Tempo, + false + ); + canvas.fillText('= ' + this._tempo, cx + this.x + this.height / 2, cy + this.y + canvas.font.size / 2); + } +} diff --git a/src/rendering/glyphs/TextGlyph.ts b/src/rendering/glyphs/TextGlyph.ts new file mode 100644 index 000000000..3c97ce84a --- /dev/null +++ b/src/rendering/glyphs/TextGlyph.ts @@ -0,0 +1,34 @@ +import { Font } from '@src/model/Font'; +import { ICanvas, TextAlign } from '@src/platform/ICanvas'; +import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph'; + +export class TextGlyph extends EffectGlyph { + private _lines: string[]; + + public font: Font; + public textAlign: TextAlign; + + public constructor(x: number, y: number, text: string, font: Font, textAlign: TextAlign = TextAlign.Left) { + super(x, y); + this._lines = text.split('\n'); + this.font = font; + this.textAlign = textAlign; + } + + public doLayout(): void { + super.doLayout(); + this.height = this.font.size * this._lines.length; + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + canvas.font = this.font; + let old: TextAlign = canvas.textAlign; + canvas.textAlign = this.textAlign; + let y: number = cy + this.y; + for (let line of this._lines) { + canvas.fillText(line, cx + this.x, y); + y += this.font.size; + } + canvas.textAlign = old; + } +} diff --git a/src/rendering/glyphs/TieGlyph.ts b/src/rendering/glyphs/TieGlyph.ts new file mode 100644 index 000000000..c49ad0ea1 --- /dev/null +++ b/src/rendering/glyphs/TieGlyph.ts @@ -0,0 +1,239 @@ +import { Beat } from '@src/model/Beat'; +import { ICanvas } from '@src/platform/ICanvas'; +import { BarRendererBase } from '@src/rendering/BarRendererBase'; +import { Glyph } from '@src/rendering/glyphs/Glyph'; +import { BeamDirection } from '@src/rendering/utils/BeamDirection'; + +export class TieGlyph extends Glyph { + protected startBeat: Beat | null; + protected endBeat: Beat | null; + protected yOffset: number = 0; + protected forEnd: boolean; + + protected startNoteRenderer: BarRendererBase | null = null; + protected endNoteRenderer: BarRendererBase | null = null; + protected tieDirection: BeamDirection = BeamDirection.Up; + + public constructor(startBeat: Beat | null, endBeat: Beat | null, forEnd: boolean) { + super(0, 0); + this.startBeat = startBeat; + this.endBeat = endBeat; + this.forEnd = forEnd; + } + + public doLayout(): void { + this.width = 0; + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + if (!this.endBeat) { + return; + } + + // TODO fix nullability of start/end beat, + let startNoteRenderer = (this.startNoteRenderer = this.renderer.scoreRenderer.layout!.getRendererForBar( + this.renderer.staff.staveId, + this.startBeat!.voice.bar + )); + let endNoteRenderer = (this.endNoteRenderer = this.renderer.scoreRenderer.layout!.getRendererForBar( + this.renderer.staff.staveId, + this.endBeat.voice.bar + )); + + let startX: number = 0; + let endX: number = 0; + let startY: number = 0; + let endY: number = 0; + let shouldDraw: boolean = false; + // if we are on the tie start, we check if we + // either can draw till the end note, or we just can draw till the bar end + this.tieDirection = !startNoteRenderer + ? this.getBeamDirection(this.endBeat, endNoteRenderer!) + : this.getBeamDirection(this.startBeat!, startNoteRenderer); + if (!this.forEnd && startNoteRenderer) { + // line break or bar break + if (startNoteRenderer !== endNoteRenderer) { + startX = cx + startNoteRenderer.x + this.getStartX(); + startY = cy + startNoteRenderer.y + this.getStartY() + this.yOffset; + // line break: to bar end + if (!endNoteRenderer || startNoteRenderer.staff !== endNoteRenderer.staff) { + endX = cx + startNoteRenderer.x + startNoteRenderer.width; + endY = startY; + } else { + endX = cx + endNoteRenderer.x + this.getEndX(); + endY = cy + endNoteRenderer.y + this.getEndY() + this.yOffset; + } + } else { + startX = cx + startNoteRenderer.x + this.getStartX(); + endX = cx + endNoteRenderer.x + this.getEndX(); + startY = cy + startNoteRenderer.y + this.getStartY() + this.yOffset; + endY = cy + endNoteRenderer.y + this.getEndY() + this.yOffset; + } + shouldDraw = true; + } else if (!startNoteRenderer || startNoteRenderer.staff !== endNoteRenderer!.staff) { + startX = cx + endNoteRenderer!.x; + endX = cx + endNoteRenderer!.x + this.getEndX(); + startY = cy + endNoteRenderer!.y + this.getEndY() + this.yOffset; + endY = startY; + shouldDraw = true; + } + if (shouldDraw) { + if(this.shouldDrawBendSlur()) { + TieGlyph.drawBendSlur( + canvas, + startX, + startY, + endX, + endY, + this.tieDirection === BeamDirection.Down, + this.scale + ); + } + else { + TieGlyph.paintTie( + canvas, + this.scale, + startX, + startY, + endX, + endY, + this.tieDirection === BeamDirection.Down, + this.getTieHeight(startX, startY, endX, endY), + 4 + ); + } + + canvas.fill(); + } + } + + protected shouldDrawBendSlur() { + return false; + } + + protected getTieHeight(startX: number, startY: number, endX: number, endY: number): number { + return 22; + } + + protected getBeamDirection(beat: Beat, noteRenderer: BarRendererBase): BeamDirection { + return BeamDirection.Down; + } + + protected getStartY(): number { + return 0; + } + + protected getEndY(): number { + return 0; + } + + protected getStartX(): number { + return 0; + } + + protected getEndX(): number { + return 0; + } + + public static paintTie( + canvas: ICanvas, + scale: number, + x1: number, + y1: number, + x2: number, + y2: number, + down: boolean = false, + offset: number = 22, + size: number = 4 + ): void { + if (x1 === x2 && y1 === y2) { + return; + } + + // ensure endX > startX + if (x2 < x1) { + let t: number = x1; + x1 = x2; + x2 = t; + t = y1; + y1 = y2; + y2 = t; + } + + // + // calculate control points + // + offset *= scale; + size *= scale; + // normal vector + let normalVectorX: number = y2 - y1; + let normalVectorY: number = x2 - x1; + let length: number = Math.sqrt(normalVectorX * normalVectorX + normalVectorY * normalVectorY); + if (down) { + normalVectorX *= -1; + } else { + normalVectorY *= -1; + } + // make to unit vector + normalVectorX /= length; + normalVectorY /= length; + // center of connection + let centerX: number = (x2 + x1) / 2; + let centerY: number = (y2 + y1) / 2; + // control points + let cp1X: number = centerX + offset * normalVectorX; + let cp1Y: number = centerY + offset * normalVectorY; + let cp2X: number = centerX + (offset - size) * normalVectorX; + let cp2Y: number = centerY + (offset - size) * normalVectorY; + canvas.beginPath(); + canvas.moveTo(x1, y1); + canvas.quadraticCurveTo(cp1X, cp1Y, x2, y2); + canvas.quadraticCurveTo(cp2X, cp2Y, x1, y1); + canvas.closePath(); + } + + private static readonly BendSlurHeight: number = 11; + + public static drawBendSlur( + canvas: ICanvas, + x1: number, + y1: number, + x2: number, + y2: number, + down: boolean, + scale: number, + slurText?: string + ): void { + let normalVectorX: number = y2 - y1; + let normalVectorY: number = x2 - x1; + let length: number = Math.sqrt(normalVectorX * normalVectorX + normalVectorY * normalVectorY); + if (down) { + normalVectorX *= -1; + } else { + normalVectorY *= -1; + } + // make to unit vector + normalVectorX /= length; + normalVectorY /= length; + // center of connection + // TODO: should be 1/3 + let centerX: number = (x2 + x1) / 2; + let centerY: number = (y2 + y1) / 2; + let offset: number = TieGlyph.BendSlurHeight * scale; + if (x2 - x1 < 20) { + offset /= 2; + } + let cp1X: number = centerX + offset * normalVectorX; + let cp1Y: number = centerY + offset * normalVectorY; + canvas.beginPath(); + canvas.moveTo(x1, y1); + canvas.lineTo(cp1X, cp1Y); + canvas.lineTo(x2, y2); + canvas.stroke(); + if (slurText) { + let w: number = canvas.measureText(slurText); + let textOffset: number = down ? 0 : -canvas.font.size; + canvas.fillText(slurText, cp1X - w / 2, cp1Y + textOffset); + } + } +} diff --git a/src/rendering/glyphs/TimeSignatureGlyph.ts b/src/rendering/glyphs/TimeSignatureGlyph.ts new file mode 100644 index 000000000..e0bc93b3f --- /dev/null +++ b/src/rendering/glyphs/TimeSignatureGlyph.ts @@ -0,0 +1,58 @@ +import { Glyph } from '@src/rendering/glyphs/Glyph'; +import { GlyphGroup } from '@src/rendering/glyphs/GlyphGroup'; +import { MusicFontGlyph } from '@src/rendering/glyphs/MusicFontGlyph'; +import { MusicFontSymbol } from '@src/rendering/glyphs/MusicFontSymbol'; +import { NumberGlyph } from '@src/rendering/glyphs/NumberGlyph'; + +export abstract class TimeSignatureGlyph extends GlyphGroup { + private _numerator: number = 0; + private _denominator: number = 0; + private _isCommon: boolean; + + public constructor(x: number, y: number, numerator: number, denominator: number, isCommon: boolean) { + super(x, y); + this._numerator = numerator; + this._denominator = denominator; + this._isCommon = isCommon; + } + + protected abstract get commonY(): number; + protected abstract get numeratorY(): number; + protected abstract get denominatorY(): number; + protected abstract get commonScale(): number; + protected abstract get numberScale(): number; + + public doLayout(): void { + if (this._isCommon && this._numerator === 2 && this._denominator === 2) { + let common: MusicFontGlyph = new MusicFontGlyph( + 0, + this.commonY, + this.commonScale, + MusicFontSymbol.TimeSignatureCutCommon + ); + common.width = 14 * this.scale; + this.addGlyph(common); + super.doLayout(); + } else if (this._isCommon && this._numerator === 4 && this._denominator === 4) { + let common: MusicFontGlyph = new MusicFontGlyph( + 0, + this.commonY, + this.commonScale, + MusicFontSymbol.TimeSignatureCommon + ); + common.width = 14 * this.scale; + this.addGlyph(common); + super.doLayout(); + } else { + let numerator: NumberGlyph = new NumberGlyph(0, this.numeratorY, this._numerator, this.numberScale); + let denominator: NumberGlyph = new NumberGlyph(0, this.denominatorY, this._denominator, this.numberScale); + this.addGlyph(numerator); + this.addGlyph(denominator); + super.doLayout(); + for (let i: number = 0, j: number = this.glyphs!.length; i < j; i++) { + let g: Glyph = this.glyphs![i]; + g.x = (this.width - g.width) / 2; + } + } + } +} diff --git a/src/rendering/glyphs/TremoloPickingGlyph.ts b/src/rendering/glyphs/TremoloPickingGlyph.ts new file mode 100644 index 000000000..b1b38c176 --- /dev/null +++ b/src/rendering/glyphs/TremoloPickingGlyph.ts @@ -0,0 +1,26 @@ +import { Duration } from '@src/model/Duration'; +import { MusicFontGlyph } from '@src/rendering/glyphs/MusicFontGlyph'; +import { MusicFontSymbol } from '@src/rendering/glyphs/MusicFontSymbol'; + +export class TremoloPickingGlyph extends MusicFontGlyph { + public constructor(x: number, y: number, duration: Duration) { + super(x, y, 1, TremoloPickingGlyph.getSymbol(duration)); + } + + public doLayout(): void { + this.width = 12 * this.scale; + } + + private static getSymbol(duration: Duration): MusicFontSymbol { + switch (duration) { + case Duration.ThirtySecond: + return MusicFontSymbol.TremoloPickingThirtySecond; + case Duration.Sixteenth: + return MusicFontSymbol.TremoloPickingSixteenth; + case Duration.Eighth: + return MusicFontSymbol.TremoloPickingEighth; + default: + return MusicFontSymbol.None; + } + } +} diff --git a/src/rendering/glyphs/TrillGlyph.ts b/src/rendering/glyphs/TrillGlyph.ts new file mode 100644 index 000000000..ef56e082f --- /dev/null +++ b/src/rendering/glyphs/TrillGlyph.ts @@ -0,0 +1,39 @@ +import { ICanvas } from '@src/platform/ICanvas'; +import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph'; +import { MusicFontSymbol } from '@src/rendering/glyphs/MusicFontSymbol'; +import { RenderingResources } from '@src/RenderingResources'; + +export class TrillGlyph extends EffectGlyph { + public constructor(x: number, y: number) { + super(x, y); + } + + public doLayout(): void { + super.doLayout(); + this.height = 20 * this.scale; + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + let res: RenderingResources = this.renderer.resources; + canvas.font = res.markerFont; + let textw: number = canvas.measureText('tr'); + canvas.fillText('tr', cx + this.x, cy + this.y + canvas.font.size / 2); + let startX: number = textw + 3 * this.scale; + let endX: number = this.width - startX; + let waveScale: number = 1.2; + let step: number = 11 * this.scale * waveScale; + let loops: number = Math.max(1, (endX - startX) / step); + let loopX: number = startX; + let loopY: number = cy + this.y + this.height; + for (let i: number = 0; i < loops; i++) { + canvas.fillMusicFontSymbol( + cx + this.x + loopX, + loopY, + waveScale, + MusicFontSymbol.WaveHorizontalSlight, + false + ); + loopX += step; + } + } +} diff --git a/src/rendering/glyphs/TripletFeelGlyph.ts b/src/rendering/glyphs/TripletFeelGlyph.ts new file mode 100644 index 000000000..d5ed06889 --- /dev/null +++ b/src/rendering/glyphs/TripletFeelGlyph.ts @@ -0,0 +1,164 @@ +import { Font } from '@src/model/Font'; +import { TripletFeel } from '@src/model/TripletFeel'; +import { ICanvas } from '@src/platform/ICanvas'; +import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph'; +import { MusicFontSymbol } from '@src/rendering/glyphs/MusicFontSymbol'; +import { NoteHeadGlyph } from '@src/rendering/glyphs/NoteHeadGlyph'; + +export enum TripletFeelGlyphBarType { + Full, + PartialLeft, + PartialRight +} + +export class TripletFeelGlyph extends EffectGlyph { + private static readonly NoteScale: number = 0.4; + private static readonly NoteHeight: number = 12; + private static readonly NoteSeparation: number = 12; + private static readonly BarHeight: number = 2; + private static readonly BarSeparation: number = 3; + + private _tripletFeel: TripletFeel; + + public constructor(tripletFeel: TripletFeel) { + super(0, 0); + this._tripletFeel = tripletFeel; + } + + public doLayout(): void { + super.doLayout(); + this.height = 25 * this.scale; + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + cx += this.x; + cy += this.y; + let noteY: number = cy + this.height * NoteHeadGlyph.GraceScale; + canvas.font = this.renderer.resources.effectFont; + canvas.fillText('(', cx, cy + this.height * 0.3); + let leftNoteX: number = cx + 10 * this.scale; + let rightNoteX: number = cx + 40 * this.scale; + switch (this._tripletFeel) { + case TripletFeel.NoTripletFeel: + this.renderBarNote(leftNoteX, noteY, TripletFeelGlyph.NoteScale, canvas, [TripletFeelGlyphBarType.Full]); + this.renderBarNote(rightNoteX, noteY, TripletFeelGlyph.NoteScale, canvas, [TripletFeelGlyphBarType.Full]); + break; + case TripletFeel.Triplet8th: + this.renderBarNote(leftNoteX, noteY, TripletFeelGlyph.NoteScale, canvas, [TripletFeelGlyphBarType.Full]); + canvas.fillMusicFontSymbol(rightNoteX, noteY, TripletFeelGlyph.NoteScale, MusicFontSymbol.Tempo, false); + canvas.fillMusicFontSymbol(rightNoteX + TripletFeelGlyph.NoteSeparation * this.scale, noteY, TripletFeelGlyph.NoteScale, MusicFontSymbol.NoteEighth, false); + this.renderTriplet(rightNoteX, cy, canvas); + break; + case TripletFeel.Triplet16th: + this.renderBarNote(leftNoteX, noteY, TripletFeelGlyph.NoteScale, canvas, [ + TripletFeelGlyphBarType.Full, + TripletFeelGlyphBarType.Full + ]); + this.renderBarNote(rightNoteX, noteY, TripletFeelGlyph.NoteScale, canvas, [ + TripletFeelGlyphBarType.Full, + TripletFeelGlyphBarType.PartialRight + ]); + this.renderTriplet(rightNoteX, cy, canvas); + break; + case TripletFeel.Dotted8th: + this.renderBarNote(leftNoteX, noteY, TripletFeelGlyph.NoteScale, canvas, [TripletFeelGlyphBarType.Full]); + this.renderBarNote(rightNoteX, noteY, TripletFeelGlyph.NoteScale, canvas, [ + TripletFeelGlyphBarType.Full, + TripletFeelGlyphBarType.PartialRight + ]); + canvas.fillCircle(rightNoteX + 9 * this.scale, noteY, this.scale); + break; + case TripletFeel.Dotted16th: + this.renderBarNote(leftNoteX, noteY, TripletFeelGlyph.NoteScale, canvas, [ + TripletFeelGlyphBarType.Full, + TripletFeelGlyphBarType.Full + ]); + this.renderBarNote(rightNoteX, noteY, TripletFeelGlyph.NoteScale, canvas, [ + TripletFeelGlyphBarType.Full, + TripletFeelGlyphBarType.Full, + TripletFeelGlyphBarType.PartialRight + ]); + canvas.fillCircle(rightNoteX + 9 * this.scale, noteY, this.scale); + break; + case TripletFeel.Scottish8th: + this.renderBarNote(leftNoteX, noteY, TripletFeelGlyph.NoteScale, canvas, [TripletFeelGlyphBarType.Full]); + this.renderBarNote(rightNoteX, noteY, TripletFeelGlyph.NoteScale, canvas, [ + TripletFeelGlyphBarType.Full, + TripletFeelGlyphBarType.PartialLeft + ]); + canvas.fillCircle(rightNoteX + TripletFeelGlyph.NoteSeparation * this.scale + 8 * this.scale, noteY, this.scale); + break; + case TripletFeel.Scottish16th: + this.renderBarNote(leftNoteX, noteY, TripletFeelGlyph.NoteScale, canvas, [ + TripletFeelGlyphBarType.Full, + TripletFeelGlyphBarType.Full + ]); + this.renderBarNote(rightNoteX, noteY, TripletFeelGlyph.NoteScale, canvas, [ + TripletFeelGlyphBarType.Full, + TripletFeelGlyphBarType.Full, + TripletFeelGlyphBarType.PartialLeft + ]); + canvas.fillCircle(rightNoteX + TripletFeelGlyph.NoteSeparation * this.scale + 8 * this.scale, noteY, this.scale); + break; + } + canvas.fillText('=', cx + 30 * this.scale, cy + 5 * this.scale); + canvas.fillText(')', cx + 65 * this.scale, cy + this.height * 0.3); + } + + private renderBarNote( + cx: number, + noteY: number, + noteScale: number, + canvas: ICanvas, + bars: TripletFeelGlyphBarType[] + ): void { + canvas.fillMusicFontSymbol(cx, noteY, noteScale, MusicFontSymbol.Tempo, false); + let partialBarWidth: number = (TripletFeelGlyph.NoteSeparation / 2) * this.scale; + for (let i: number = 0; i < bars.length; i++) { + switch (bars[i]) { + case TripletFeelGlyphBarType.Full: + canvas.fillRect( + cx + 4 * this.scale, + noteY - TripletFeelGlyph.NoteHeight * this.scale + TripletFeelGlyph.BarSeparation * this.scale * i, + TripletFeelGlyph.NoteSeparation * this.scale, + TripletFeelGlyph.BarHeight * this.scale + ); + break; + case TripletFeelGlyphBarType.PartialLeft: + canvas.fillRect( + cx + 4 * this.scale, + noteY - TripletFeelGlyph.NoteHeight * this.scale + TripletFeelGlyph.BarSeparation * this.scale * i, + partialBarWidth, + TripletFeelGlyph.BarHeight * this.scale + ); + break; + case TripletFeelGlyphBarType.PartialRight: + canvas.fillRect( + cx + 4 * this.scale + partialBarWidth, + noteY - TripletFeelGlyph.NoteHeight * this.scale + TripletFeelGlyph.BarSeparation * this.scale * i, + partialBarWidth, + TripletFeelGlyph.BarHeight * this.scale + ); + break; + } + } + canvas.fillMusicFontSymbol(cx + TripletFeelGlyph.NoteSeparation * this.scale, noteY, noteScale, MusicFontSymbol.Tempo, false); + } + + private renderTriplet(cx: number, cy: number, canvas: ICanvas): void { + cy += 2 * this.scale; + let font: Font = this.renderer.resources.effectFont; + canvas.font = new Font(font.family, font.size * 0.8, font.style); + let rightX: number = cx + TripletFeelGlyph.NoteSeparation * this.scale + 3 * this.scale; + canvas.beginPath(); + canvas.moveTo(cx, cy + 3 * this.scale); + canvas.lineTo(cx, cy); + canvas.lineTo(cx + 5 * this.scale, cy); + canvas.moveTo(rightX + 5 * this.scale, cy + 3 * this.scale); + canvas.lineTo(rightX + 5 * this.scale, cy); + canvas.lineTo(rightX, cy); + canvas.stroke(); + canvas.fillText('3', cx + 7 * this.scale, cy - 10 * this.scale); + canvas.font = font; + } +} diff --git a/src/rendering/glyphs/TuningGlyph.ts b/src/rendering/glyphs/TuningGlyph.ts new file mode 100644 index 000000000..36490bc79 --- /dev/null +++ b/src/rendering/glyphs/TuningGlyph.ts @@ -0,0 +1,40 @@ +import { Tuning } from '@src/model/Tuning'; +import { TextAlign } from '@src/platform/ICanvas'; +import { GlyphGroup } from '@src/rendering/glyphs/GlyphGroup'; +import { TextGlyph } from '@src/rendering/glyphs/TextGlyph'; +import { RenderingResources } from '@src/RenderingResources'; + +export class TuningGlyph extends GlyphGroup { + private _scale: number = 0; + private _resources: RenderingResources; + public height: number = 0; + + public constructor(x: number, y: number, scale: number, resources: RenderingResources, tuning: Tuning) { + super(x, y); + this._scale = scale; + this._resources = resources; + this.createGlyphs(tuning); + } + + private createGlyphs(tuning: Tuning): void { + // Name + this.addGlyph(new TextGlyph(0, 0, tuning.name, this._resources.effectFont, TextAlign.Left)); + this.height += 15 * this._scale; + if (!tuning.isStandard) { + // Strings + let stringsPerColumn: number = Math.ceil(tuning.tunings.length / 2.0) | 0; + let currentX: number = 0; + let currentY: number = this.height; + for (let i: number = 0, j: number = tuning.tunings.length; i < j; i++) { + let str: string = '(' + (i + 1) + ') = ' + Tuning.getTextForTuning(tuning.tunings[i], false); + this.addGlyph(new TextGlyph(currentX, currentY, str, this._resources.effectFont, TextAlign.Left)); + currentY += this.height; + if (i === stringsPerColumn - 1) { + currentY = this.height; + currentX += 43 * this._scale; + } + } + this.height += stringsPerColumn * (15 * this._scale); + } + } +} diff --git a/src/rendering/glyphs/VoiceContainerGlyph.ts b/src/rendering/glyphs/VoiceContainerGlyph.ts new file mode 100644 index 000000000..467cb5f4c --- /dev/null +++ b/src/rendering/glyphs/VoiceContainerGlyph.ts @@ -0,0 +1,106 @@ +import { TupletGroup } from '@src/model/TupletGroup'; +import { Voice } from '@src/model/Voice'; +import { ICanvas } from '@src/platform/ICanvas'; +import { BeatContainerGlyph } from '@src/rendering/glyphs/BeatContainerGlyph'; +import { Glyph } from '@src/rendering/glyphs/Glyph'; +import { GlyphGroup } from '@src/rendering/glyphs/GlyphGroup'; +import { BarLayoutingInfo } from '@src/rendering/staves/BarLayoutingInfo'; + +/** + * This glyph acts as container for handling + * multiple voice rendering + */ +export class VoiceContainerGlyph extends GlyphGroup { + public static readonly KeySizeBeat: string = 'Beat'; + + public beatGlyphs: BeatContainerGlyph[]; + public voice: Voice; + public minWidth: number = 0; + public tupletGroups: TupletGroup[]; + + public constructor(x: number, y: number, voice: Voice) { + super(x, y); + this.voice = voice; + this.beatGlyphs = []; + this.tupletGroups = []; + } + + public scaleToWidth(width: number): void { + let force: number = this.renderer.layoutingInfo.spaceToForce(width); + this.scaleToForce(force); + } + + private scaleToForce(force: number): void { + this.width = this.renderer.layoutingInfo.calculateVoiceWidth(force); + let positions: Map = this.renderer.layoutingInfo.buildOnTimePositions(force); + let beatGlyphs: BeatContainerGlyph[] = this.beatGlyphs; + for (let i: number = 0, j: number = beatGlyphs.length; i < j; i++) { + let currentBeatGlyph: BeatContainerGlyph = beatGlyphs[i]; + let time: number = currentBeatGlyph.beat.absoluteDisplayStart; + currentBeatGlyph.x = positions.get(time)! - currentBeatGlyph.onTimeX; + // size always previousl glyph after we know the position + // of the next glyph + if (i > 0) { + let beatWidth: number = currentBeatGlyph.x - beatGlyphs[i - 1].x; + beatGlyphs[i - 1].scaleToWidth(beatWidth); + } + // for the last glyph size based on the full width + if (i === j - 1) { + let beatWidth: number = this.width - beatGlyphs[beatGlyphs.length - 1].x; + currentBeatGlyph.scaleToWidth(beatWidth); + } + } + } + + public registerLayoutingInfo(info: BarLayoutingInfo): void { + info.updateVoiceSize(this.width); + let beatGlyphs: BeatContainerGlyph[] = this.beatGlyphs; + for (let b of beatGlyphs) { + b.registerLayoutingInfo(info); + } + } + + public applyLayoutingInfo(info: BarLayoutingInfo): void { + let beatGlyphs: BeatContainerGlyph[] = this.beatGlyphs; + for (let b of beatGlyphs) { + b.applyLayoutingInfo(info); + } + this.scaleToForce(Math.max(this.renderer.settings.display.stretchForce, info.minStretchForce)); + } + + public addGlyph(g: Glyph): void { + let bg: BeatContainerGlyph = g as BeatContainerGlyph; + g.x = + this.beatGlyphs.length === 0 + ? 0 + : this.beatGlyphs[this.beatGlyphs.length - 1].x + this.beatGlyphs[this.beatGlyphs.length - 1].width; + g.renderer = this.renderer; + g.doLayout(); + this.beatGlyphs.push(bg); + this.width = g.x + g.width; + if (bg.beat.hasTuplet && bg.beat.tupletGroup!.beats[0].id === bg.beat.id) { + this.tupletGroups.push(bg.beat.tupletGroup!); + } + } + + public doLayout(): void { + this.minWidth = this.width; + } + + // private static Random Random = new Random(); + public paint(cx: number, cy: number, canvas: ICanvas): void { + // canvas.Color = Color.Random(); + // canvas.StrokeRect(cx + X, cy + Y, Width, 100); + // if (Voice.Index===0) + // { + // PaintSprings(cx + X, cy + Y, canvas); + // } + canvas.color = + this.voice.index === 0 + ? this.renderer.resources.mainGlyphColor + : this.renderer.resources.secondaryGlyphColor; + for (let i: number = 0, j: number = this.beatGlyphs.length; i < j; i++) { + this.beatGlyphs[i].paint(cx + this.x, cy + this.y, canvas); + } + } +} diff --git a/src/rendering/layout/HorizontalScreenLayout.ts b/src/rendering/layout/HorizontalScreenLayout.ts new file mode 100644 index 000000000..37504e592 --- /dev/null +++ b/src/rendering/layout/HorizontalScreenLayout.ts @@ -0,0 +1,169 @@ +import { MasterBar } from '@src/model/MasterBar'; +import { Score } from '@src/model/Score'; +import { ICanvas, TextAlign } from '@src/platform/ICanvas'; +import { ScoreLayout } from '@src/rendering/layout/ScoreLayout'; +import { RenderFinishedEventArgs } from '@src/rendering/RenderFinishedEventArgs'; +import { ScoreRenderer } from '@src/rendering/ScoreRenderer'; +import { StaveGroup } from '@src/rendering/staves/StaveGroup'; +import { Logger } from '@src/Logger'; +import { EventEmitterOfT } from '@src/EventEmitter'; + +export class HorizontalScreenLayoutPartialInfo { + public width: number = 0; + public masterBars: MasterBar[] = []; +} + +/** + * This layout arranges the bars all horizontally + */ +export class HorizontalScreenLayout extends ScoreLayout { + public static PagePadding: Float32Array = new Float32Array([20, 20, 20, 20]); + public static readonly GroupSpacing: number = 20; + private _group: StaveGroup | null = null; + private _pagePadding: Float32Array | null = null; + + public get name(): string { + return 'HorizontalScreen'; + } + + public constructor(renderer: ScoreRenderer) { + super(renderer); + } + + public get supportsResize(): boolean { + return false; + } + + public resize(): void {} + + protected doLayoutAndRender(): void { + this._pagePadding = this.renderer.settings.display.padding; + if (!this._pagePadding) { + this._pagePadding = HorizontalScreenLayout.PagePadding; + } + if (this._pagePadding.length === 1) { + this._pagePadding = new Float32Array([ + this._pagePadding[0], + this._pagePadding[0], + this._pagePadding[0], + this._pagePadding[0] + ]); + } else if (this._pagePadding.length === 2) { + this._pagePadding = new Float32Array([ + this._pagePadding[0], + this._pagePadding[1], + this._pagePadding[0], + this._pagePadding[1] + ]); + } + let score: Score = this.renderer.score!; + let canvas: ICanvas = this.renderer.canvas!; + let startIndex: number = this.renderer.settings.display.startBar; + startIndex--; // map to array index + + startIndex = Math.min(score.masterBars.length - 1, Math.max(0, startIndex)); + let currentBarIndex: number = startIndex; + let endBarIndex: number = this.renderer.settings.display.barCount; + if (endBarIndex <= 0) { + endBarIndex = score.masterBars.length; + } + endBarIndex = startIndex + endBarIndex - 1; // map count to array index + + endBarIndex = Math.min(score.masterBars.length - 1, Math.max(0, endBarIndex)); + this._group = this.createEmptyStaveGroup(); + this._group.isLast = true; + this._group.x = this._pagePadding[0]; + this._group.y = this._pagePadding[1]; + let countPerPartial: number = this.renderer.settings.display.barCountPerPartial; + let partials: HorizontalScreenLayoutPartialInfo[] = []; + let currentPartial: HorizontalScreenLayoutPartialInfo = new HorizontalScreenLayoutPartialInfo(); + while (currentBarIndex <= endBarIndex) { + let result = this._group.addBars(this.renderer.tracks!, currentBarIndex); + if (result) { + // if we detect that the new renderer is linked to the previous + // renderer, we need to put it into the previous partial + if (currentPartial.masterBars.length === 0 && result.isLinkedToPrevious && partials.length > 0) { + let previousPartial: HorizontalScreenLayoutPartialInfo = partials[partials.length - 1]; + previousPartial.masterBars.push(score.masterBars[currentBarIndex]); + previousPartial.width += result.width; + } else { + currentPartial.masterBars.push(score.masterBars[currentBarIndex]); + currentPartial.width += result.width; + // no targetPartial here because previous partials already handled this code + if (currentPartial.masterBars.length >= countPerPartial) { + if (partials.length === 0) { + currentPartial.width += this._group.x + this._group.accoladeSpacing; + } + partials.push(currentPartial); + Logger.debug( + this.name, + 'Finished partial from bar ' + + currentPartial.masterBars[0].index + + ' to ' + + currentPartial.masterBars[currentPartial.masterBars.length - 1].index, + null + ); + currentPartial = new HorizontalScreenLayoutPartialInfo(); + } + } + } + currentBarIndex++; + } + // don't miss the last partial if not empty + if (currentPartial.masterBars.length > 0) { + if (partials.length === 0) { + currentPartial.width += this._group.x + this._group.accoladeSpacing; + } + partials.push(currentPartial); + Logger.debug( + this.name, + 'Finished partial from bar ' + + currentPartial.masterBars[0].index + + ' to ' + + currentPartial.masterBars[currentPartial.masterBars.length - 1].index, + null + ); + } + this._group.finalizeGroup(); + this.height = this._group.y + this._group.height + this._pagePadding[3]; + this.width = this._group.x + this._group.width + this._pagePadding[2]; + currentBarIndex = 0; + for (let i: number = 0; i < partials.length; i++) { + let partial: HorizontalScreenLayoutPartialInfo = partials[i]; + canvas.beginRender(partial.width, this.height); + canvas.color = this.renderer.settings.display.resources.mainGlyphColor; + canvas.textAlign = TextAlign.Left; + let renderX: number = this._group.getBarX(partial.masterBars[0].index) + this._group.accoladeSpacing; + if (i === 0) { + renderX -= this._group.x + this._group.accoladeSpacing; + } + Logger.debug( + this.name, + 'Rendering partial from bar ' + + partial.masterBars[0].index + + ' to ' + + partial.masterBars[partial.masterBars.length - 1].index, + null + ); + this._group.paintPartial( + -renderX, + this._group.y, + this.renderer.canvas!, + currentBarIndex, + partial.masterBars.length + ); + let result: unknown = canvas.endRender(); + + let e = new RenderFinishedEventArgs(); + e.totalWidth = this.width; + e.totalHeight = this.height; + e.width = partial.width; + e.height = this.height; + e.renderResult = result; + e.firstMasterBarIndex = partial.masterBars[0].index; + e.lastMasterBarIndex = partial.masterBars[partial.masterBars.length - 1].index; + (this.renderer.partialRenderFinished as EventEmitterOfT).trigger(e); + currentBarIndex += partial.masterBars.length; + } + } +} diff --git a/src/rendering/layout/PageViewLayout.ts b/src/rendering/layout/PageViewLayout.ts new file mode 100644 index 000000000..ce263bb84 --- /dev/null +++ b/src/rendering/layout/PageViewLayout.ts @@ -0,0 +1,356 @@ +import { ICanvas, TextAlign } from '@src/platform/ICanvas'; +import { TextGlyph } from '@src/rendering/glyphs/TextGlyph'; +import { ScoreLayout } from '@src/rendering/layout/ScoreLayout'; +import { RenderFinishedEventArgs } from '@src/rendering/RenderFinishedEventArgs'; +import { ScoreRenderer } from '@src/rendering/ScoreRenderer'; +import { MasterBarsRenderers } from '@src/rendering/staves/MasterBarsRenderers'; +import { StaveGroup } from '@src/rendering/staves/StaveGroup'; +import { RenderingResources } from '@src/RenderingResources'; +import { Logger } from '@src/Logger'; +import { EventEmitterOfT } from '@src/EventEmitter'; +import { NotationElement } from '@src/NotationSettings'; + +/** + * This layout arranges the bars into a fixed width and dynamic height region. + */ +export class PageViewLayout extends ScoreLayout { + public static PagePadding: Float32Array = new Float32Array([40, 40, 40, 40]); + public static readonly GroupSpacing: number = 20; + private _groups: StaveGroup[] = []; + private _allMasterBarRenderers: MasterBarsRenderers[] = []; + private _barsFromPreviousGroup: MasterBarsRenderers[] = []; + private _pagePadding: Float32Array | null = null; + + public get name(): string { + return 'PageView'; + } + + public constructor(renderer: ScoreRenderer) { + super(renderer); + } + + protected doLayoutAndRender(): void { + this._pagePadding = this.renderer.settings.display.padding; + if (!this._pagePadding) { + this._pagePadding = PageViewLayout.PagePadding; + } + if (this._pagePadding.length === 1) { + this._pagePadding = new Float32Array([ + this._pagePadding[0], + this._pagePadding[0], + this._pagePadding[0], + this._pagePadding[0] + ]); + } else if (this._pagePadding.length === 2) { + this._pagePadding = new Float32Array([ + this._pagePadding[0], + this._pagePadding[1], + this._pagePadding[0], + this._pagePadding[1] + ]); + } + let x: number = this._pagePadding[0]; + let y: number = this._pagePadding[1]; + this.width = this.renderer.width; + this._allMasterBarRenderers = []; + // + // 1. Score Info + y = this.layoutAndRenderScoreInfo(x, y, -1); + // + // 2. Chord Diagrms + y = this.layoutAndRenderChordDiagrams(y, -1); + // + // 3. One result per StaveGroup + y = this.layoutAndRenderScore(x, y); + this.height = y + this._pagePadding[3]; + } + + public get supportsResize(): boolean { + return true; + } + + public resize(): void { + let x: number = this._pagePadding![0]; + let y: number = this._pagePadding![1]; + this.width = this.renderer.width; + let oldHeight: number = this.height; + // + // 1. Score Info + y = this.layoutAndRenderScoreInfo(x, y, oldHeight); + // + // 2. Chord Digrams + y = this.layoutAndRenderChordDiagrams(y, oldHeight); + // + // 2. One result per StaveGroup + y = this.resizeAndRenderScore(x, y, oldHeight); + this.height = y + this._pagePadding![3]; + } + + private layoutAndRenderChordDiagrams(y: number, totalHeight: number = -1): number { + if (!this.chordDiagrams) { + return y; + } + let res: RenderingResources = this.renderer.settings.display.resources; + this.chordDiagrams.width = this.width; + this.chordDiagrams.doLayout(); + let canvas: ICanvas = this.renderer.canvas!; + canvas.beginRender(this.width, this.chordDiagrams.height); + canvas.color = res.scoreInfoColor; + canvas.textAlign = TextAlign.Center; + this.chordDiagrams.paint(0, 0, canvas); + let result: unknown = canvas.endRender(); + y += this.chordDiagrams.height; + + let e = new RenderFinishedEventArgs(); + e.width = this.width; + e.height = this.chordDiagrams.height; + e.renderResult = result; + e.totalWidth = this.width; + e.totalHeight = totalHeight < 0 ? y : totalHeight; + e.firstMasterBarIndex = -1; + e.lastMasterBarIndex = -1; + (this.renderer.partialRenderFinished as EventEmitterOfT).trigger(e); + return y; + } + + private layoutAndRenderScoreInfo(x: number, y: number, totalHeight: number = -1): number { + Logger.debug(this.name, 'Layouting score info'); + let scale: number = this.scale; + let res: RenderingResources = this.renderer.settings.display.resources; + let centeredGlyphs: NotationElement[] = [ + NotationElement.ScoreTitle, + NotationElement.ScoreSubTitle, + NotationElement.ScoreArtist, + NotationElement.ScoreAlbum, + NotationElement.ScoreWordsAndMusic + ]; + for (let i: number = 0; i < centeredGlyphs.length; i++) { + if (this.scoreInfoGlyphs.has(centeredGlyphs[i])) { + let glyph: TextGlyph = this.scoreInfoGlyphs.get(centeredGlyphs[i])!; + glyph.x = this.width / 2; + glyph.y = y; + glyph.textAlign = TextAlign.Center; + y += glyph.font.size; + } + } + let musicOrWords: boolean = false; + let musicOrWordsHeight: number = 0; + if (this.scoreInfoGlyphs.has(NotationElement.ScoreMusic)) { + let glyph: TextGlyph = this.scoreInfoGlyphs.get(NotationElement.ScoreMusic)!; + glyph.x = this.width - this._pagePadding![2]; + glyph.y = y; + glyph.textAlign = TextAlign.Right; + musicOrWords = true; + musicOrWordsHeight = glyph.font.size; + } + if (this.scoreInfoGlyphs.has(NotationElement.ScoreWords)) { + let glyph: TextGlyph = this.scoreInfoGlyphs.get(NotationElement.ScoreWords)!; + glyph.x = x; + glyph.y = y; + glyph.textAlign = TextAlign.Left; + musicOrWords = true; + musicOrWordsHeight = glyph.font.size; + } + if (musicOrWords) { + y += musicOrWordsHeight; + } + if (this.tuningGlyph) { + y += 20 * scale; + this.tuningGlyph.x = x; + this.tuningGlyph.y = y; + y += this.tuningGlyph.height; + } + y += 20 * scale; + let canvas: ICanvas = this.renderer.canvas!; + canvas.beginRender(this.width, y); + canvas.color = res.scoreInfoColor; + canvas.textAlign = TextAlign.Center; + this.scoreInfoGlyphs.forEach(g => { + g.paint(0, 0, canvas); + }); + if (this.tuningGlyph) { + this.tuningGlyph.paint(0, 0, canvas); + } + let result: unknown = canvas.endRender(); + + let e = new RenderFinishedEventArgs(); + e.width = this.width; + e.height = y; + e.renderResult = result; + e.totalWidth = this.width; + e.totalHeight = totalHeight < 0 ? y : totalHeight; + e.firstMasterBarIndex = -1; + e.lastMasterBarIndex = -1; + + (this.renderer.partialRenderFinished as EventEmitterOfT).trigger(e); + return y; + } + + private resizeAndRenderScore(x: number, y: number, oldHeight: number): number { + let canvas: ICanvas = this.renderer.canvas!; + // if we have a fixed number of bars per row, we only need to refit them. + if (this.renderer.settings.display.barsPerRow !== -1) { + for (let i: number = 0; i < this._groups.length; i++) { + let group: StaveGroup = this._groups[i]; + this.fitGroup(group); + group.finalizeGroup(); + y += this.paintGroup(group, oldHeight, canvas); + } + } else { + this._groups = []; + let currentIndex: number = 0; + let maxWidth: number = this.maxWidth; + let group: StaveGroup = this.createEmptyStaveGroup(); + group.index = this._groups.length; + group.x = x; + group.y = y; + while (currentIndex < this._allMasterBarRenderers.length) { + // if the current renderer still has space in the current group add it + // also force adding in case the group is empty + let renderers: MasterBarsRenderers | null = this._allMasterBarRenderers[currentIndex]; + if (group.width + renderers.width <= maxWidth || group.masterBarsRenderers.length === 0) { + group.addMasterBarRenderers(this.renderer.tracks!, renderers); + // move to next group + currentIndex++; + } else { + // if we cannot wrap on the current bar, we remove the last bar + // (this might even remove multiple ones until we reach a bar that can wrap); + while (renderers && !renderers.canWrap && group.masterBarsRenderers.length > 1) { + renderers = group.revertLastBar(); + currentIndex--; + } + // in case we do not have space, we create a new group + group.isFull = true; + group.isLast = this.lastBarIndex === group.lastBarIndex; + this._groups.push(group); + this.fitGroup(group); + group.finalizeGroup(); + y += this.paintGroup(group, oldHeight, canvas); + // note: we do not increase currentIndex here to have it added to the next group + group = this.createEmptyStaveGroup(); + group.index = this._groups.length; + group.x = x; + group.y = y; + } + } + group.isLast = this.lastBarIndex === group.lastBarIndex; + // don't forget to finish the last group + this.fitGroup(group); + group.finalizeGroup(); + y += this.paintGroup(group, oldHeight, canvas); + } + return y; + } + + private layoutAndRenderScore(x: number, y: number): number { + let canvas: ICanvas = this.renderer.canvas!; + let startIndex: number = this.firstBarIndex; + let currentBarIndex: number = startIndex; + let endBarIndex: number = this.lastBarIndex; + this._groups = []; + while (currentBarIndex <= endBarIndex) { + // create group and align set proper coordinates + let group: StaveGroup = this.createStaveGroup(currentBarIndex, endBarIndex); + this._groups.push(group); + group.x = x; + group.y = y; + currentBarIndex = group.lastBarIndex + 1; + // finalize group (sizing etc). + this.fitGroup(group); + group.finalizeGroup(); + Logger.debug( + this.name, + 'Rendering partial from bar ' + group.firstBarIndex + ' to ' + group.lastBarIndex, + null + ); + y += this.paintGroup(group, y, canvas); + } + return y; + } + + private paintGroup(group: StaveGroup, totalHeight: number, canvas: ICanvas): number { + // paint into canvas + let height: number = group.height + 20 * this.scale; + canvas.beginRender(this.width, height); + this.renderer.canvas!.color = this.renderer.settings.display.resources.mainGlyphColor; + this.renderer.canvas!.textAlign = TextAlign.Left; + // NOTE: we use this negation trick to make the group paint itself to 0/0 coordinates + // since we use partial drawing + group.paint(0, -group.y, canvas); + // calculate coordinates for next group + totalHeight += height; + let result: unknown = canvas.endRender(); + let args: RenderFinishedEventArgs = new RenderFinishedEventArgs(); + args.totalWidth = this.width; + args.totalHeight = totalHeight; + args.width = this.width; + args.height = height; + args.renderResult = result; + args.firstMasterBarIndex = group.firstBarIndex; + args.lastMasterBarIndex = group.lastBarIndex; + (this.renderer.partialRenderFinished as EventEmitterOfT).trigger(args); + return height; + } + + /** + * Realignes the bars in this line according to the available space + */ + private fitGroup(group: StaveGroup): void { + if (group.isFull || group.width > this.maxWidth) { + group.scaleToWidth(this.maxWidth); + } + this.width = Math.max(this.width, group.width); + } + + private createStaveGroup(currentBarIndex: number, endIndex: number): StaveGroup { + let group: StaveGroup = this.createEmptyStaveGroup(); + group.index = this._groups.length; + let barsPerRow: number = this.renderer.settings.display.barsPerRow; + let maxWidth: number = this.maxWidth; + let end: number = endIndex + 1; + for (let i: number = currentBarIndex; i < end; i++) { + if (this._barsFromPreviousGroup.length > 0) { + for (let renderer of this._barsFromPreviousGroup) { + group.addMasterBarRenderers(this.renderer.tracks!, renderer); + i = renderer.masterBar.index; + } + } else { + let renderers: MasterBarsRenderers | null = group.addBars(this.renderer.tracks!, i); + if (renderers) { + this._allMasterBarRenderers.push(renderers); + } + } + this._barsFromPreviousGroup = []; + let groupIsFull: boolean = false; + // can bar placed in this line? + if (barsPerRow === -1 && group.width >= maxWidth && group.masterBarsRenderers.length !== 0) { + groupIsFull = true; + } else if (group.masterBarsRenderers.length === barsPerRow + 1) { + groupIsFull = true; + } + if (groupIsFull) { + let reverted = group.revertLastBar(); + if (reverted) { + this._barsFromPreviousGroup.push(reverted); + while (reverted && !reverted.canWrap && group.masterBarsRenderers.length > 1) { + reverted = group.revertLastBar(); + if (reverted) { + this._barsFromPreviousGroup.push(reverted); + } + } + } + group.isFull = true; + group.isLast = false; + this._barsFromPreviousGroup.reverse(); + return group; + } + group.x = 0; + } + group.isLast = endIndex === group.lastBarIndex; + return group; + } + + private get maxWidth(): number { + return this.renderer.width - this._pagePadding![0] - this._pagePadding![2]; + } +} diff --git a/src/rendering/layout/ScoreLayout.ts b/src/rendering/layout/ScoreLayout.ts new file mode 100644 index 000000000..44c490ef7 --- /dev/null +++ b/src/rendering/layout/ScoreLayout.ts @@ -0,0 +1,264 @@ +import { StaveProfile } from '@src/DisplaySettings'; +import { Environment } from '@src/Environment'; +import { Bar } from '@src/model/Bar'; +import { Chord } from '@src/model/Chord'; +import { Font, FontStyle } from '@src/model/Font'; +import { Score } from '@src/model/Score'; +import { Staff } from '@src/model/Staff'; +import { Track } from '@src/model/Track'; +import { Tuning } from '@src/model/Tuning'; +import { ICanvas, TextAlign } from '@src/platform/ICanvas'; +import { BarRendererBase } from '@src/rendering/BarRendererBase'; +import { BarRendererFactory } from '@src/rendering/BarRendererFactory'; +import { ChordDiagramContainerGlyph } from '@src/rendering/glyphs/ChordDiagramContainerGlyph'; +import { TextGlyph } from '@src/rendering/glyphs/TextGlyph'; +import { TuningGlyph } from '@src/rendering/glyphs/TuningGlyph'; +import { RenderFinishedEventArgs } from '@src/rendering/RenderFinishedEventArgs'; +import { ScoreRenderer } from '@src/rendering/ScoreRenderer'; +import { RenderStaff } from '@src/rendering/staves/RenderStaff'; +import { StaveGroup } from '@src/rendering/staves/StaveGroup'; +import { RenderingResources } from '@src/RenderingResources'; +import { Logger } from '@src/Logger'; +import { EventEmitterOfT } from '@src/EventEmitter'; +import { NotationSettings, NotationElement } from '@src/NotationSettings'; + +/** + * This is the base public class for creating new layouting engines for the score renderer. + */ +export abstract class ScoreLayout { + private _barRendererLookup: Map> = new Map(); + + public abstract get name(): string; + + public renderer: ScoreRenderer; + public width: number = 0; + public height: number = 0; + + protected scoreInfoGlyphs: Map = new Map(); + protected chordDiagrams: ChordDiagramContainerGlyph | null = null; + protected tuningGlyph: TuningGlyph | null = null; + + protected constructor(renderer: ScoreRenderer) { + this.renderer = renderer; + } + + public abstract get supportsResize(): boolean; + + public abstract resize(): void; + + public layoutAndRender(): void { + let score: Score = this.renderer.score!; + let startIndex: number = this.renderer.settings.display.startBar; + startIndex--; // map to array index + + startIndex = Math.min(score.masterBars.length - 1, Math.max(0, startIndex)); + this.firstBarIndex = startIndex; + let endBarIndex: number = this.renderer.settings.display.barCount; + if (endBarIndex < 0) { + endBarIndex = score.masterBars.length; + } + endBarIndex = startIndex + endBarIndex - 1; // map count to array index + + endBarIndex = Math.min(score.masterBars.length - 1, Math.max(0, endBarIndex)); + this.lastBarIndex = endBarIndex; + this.createScoreInfoGlyphs(); + this.doLayoutAndRender(); + } + + protected abstract doLayoutAndRender(): void; + + private createScoreInfoGlyphs(): void { + Logger.debug('ScoreLayout', 'Creating score info glyphs'); + let notation: NotationSettings = this.renderer.settings.notation; + let score: Score = this.renderer.score!; + let res: RenderingResources = this.renderer.settings.display.resources; + this.scoreInfoGlyphs = new Map(); + if (score.title && notation.isNotationElementVisible(NotationElement.ScoreTitle)) { + this.scoreInfoGlyphs.set( + NotationElement.ScoreTitle, + new TextGlyph(0, 0, score.title, res.titleFont, TextAlign.Center) + ); + } + if (score.subTitle && notation.isNotationElementVisible(NotationElement.ScoreSubTitle)) { + this.scoreInfoGlyphs.set( + NotationElement.ScoreSubTitle, + new TextGlyph(0, 0, score.subTitle, res.subTitleFont, TextAlign.Center) + ); + } + if (score.artist && notation.isNotationElementVisible(NotationElement.ScoreArtist)) { + this.scoreInfoGlyphs.set( + NotationElement.ScoreArtist, + new TextGlyph(0, 0, score.artist, res.subTitleFont, TextAlign.Center) + ); + } + if (score.album && notation.isNotationElementVisible(NotationElement.ScoreAlbum)) { + this.scoreInfoGlyphs.set( + NotationElement.ScoreAlbum, + new TextGlyph(0, 0, score.album, res.subTitleFont, TextAlign.Center) + ); + } + if ( + score.music && + score.music === score.words && + notation.isNotationElementVisible(NotationElement.ScoreWordsAndMusic) + ) { + this.scoreInfoGlyphs.set( + NotationElement.ScoreWordsAndMusic, + new TextGlyph(0, 0, 'Music and Words by ' + score.words, res.wordsFont, TextAlign.Center) + ); + } else { + if (score.music && notation.isNotationElementVisible(NotationElement.ScoreMusic)) { + this.scoreInfoGlyphs.set( + NotationElement.ScoreMusic, + new TextGlyph(0, 0, 'Music by ' + score.music, res.wordsFont, TextAlign.Right) + ); + } + if (score.words && notation.isNotationElementVisible(NotationElement.ScoreWords)) { + this.scoreInfoGlyphs.set( + NotationElement.ScoreWords, + new TextGlyph(0, 0, 'Words by ' + score.words, res.wordsFont, TextAlign.Left) + ); + } + } + if (notation.isNotationElementVisible(NotationElement.GuitarTuning)) { + let staffWithTuning: Staff | null = null; + for (let track of this.renderer.tracks!) { + for (let staff of track.staves) { + if (!staff.isPercussion && staff.isStringed && staff.tuning.length > 0) { + staffWithTuning = staff; + break; + } + } + if (staffWithTuning) { + break; + } + } + // tuning info + if (staffWithTuning) { + let tuning: Tuning | null = Tuning.findTuning(staffWithTuning.tuning); + if (!tuning) { + tuning = new Tuning('', staffWithTuning.tuning, false); + } + this.tuningGlyph = new TuningGlyph(0, 0, this.scale, res, tuning); + } + } + // chord diagram glyphs + if (notation.isNotationElementVisible(NotationElement.ChordDiagrams)) { + this.chordDiagrams = new ChordDiagramContainerGlyph(0, 0); + this.chordDiagrams.renderer = new BarRendererBase( + this.renderer, + this.renderer.tracks![0].staves[0].bars[0] + ); + let chords: Map = new Map(); + for (let track of this.renderer.tracks!) { + for (let staff of track.staves) { + staff.chords.forEach((chord, chordId) => { + if (!chords.has(chordId)) { + if (chord.showDiagram) { + chords.set(chordId, chord); + this.chordDiagrams!.addChord(chord); + } + } + }); + } + } + } + } + + public get scale(): number { + return this.renderer.settings.display.scale; + } + + public firstBarIndex: number = 0; + + public lastBarIndex: number = 0; + + protected createEmptyStaveGroup(): StaveGroup { + let group: StaveGroup = new StaveGroup(); + group.layout = this; + for (let trackIndex: number = 0; trackIndex < this.renderer.tracks!.length; trackIndex++) { + let track: Track = this.renderer.tracks![trackIndex]; + let hasScore: boolean = false; + for (let staff of track.staves) { + if (staff.showStandardNotation) { + hasScore = true; + break; + } + } + for (let staffIndex: number = 0; staffIndex < track.staves.length; staffIndex++) { + let staff: Staff = track.staves[staffIndex]; + // use optimal profile for track + let staveProfile: StaveProfile; + if (staff.isPercussion) { + staveProfile = StaveProfile.Score; + } else if (this.renderer.settings.display.staveProfile !== StaveProfile.Default) { + staveProfile = this.renderer.settings.display.staveProfile; + } else if (staff.showTablature && staff.showStandardNotation) { + staveProfile = StaveProfile.ScoreTab; + } else if (staff.showTablature) { + staveProfile = hasScore ? StaveProfile.TabMixed : StaveProfile.Tab; + } else if (staff.showStandardNotation) { + staveProfile = StaveProfile.Score; + } else { + continue; + } + let profile: BarRendererFactory[] = Environment.staveProfiles.get(staveProfile)!; + for (let factory of profile) { + if (factory.canCreate(track, staff)) { + group.addStaff(track, new RenderStaff(trackIndex, staff, factory)); + } + } + } + } + return group; + } + + public registerBarRenderer(key: string, renderer: BarRendererBase): void { + if (!this._barRendererLookup.has(key)) { + this._barRendererLookup.set(key, new Map()); + } + this._barRendererLookup.get(key)!.set(renderer.bar.id, renderer); + } + + public unregisterBarRenderer(key: string, renderer: BarRendererBase): void { + if (this._barRendererLookup.has(key)) { + let lookup: Map = this._barRendererLookup.get(key)!; + lookup.delete(renderer.bar.id); + } + } + + public getRendererForBar(key: string, bar: Bar): BarRendererBase | null { + let barRendererId: number = bar.id; + if (this._barRendererLookup.has(key) && this._barRendererLookup.get(key)!.has(barRendererId)) { + return this._barRendererLookup.get(key)!.get(barRendererId)!; + } + return null; + } + + public renderAnnotation(): void { + // attention, you are not allowed to remove change this notice within any version of this library without permission! + let msg: string = 'rendered by alphaTab (https://alphaTab.net)'; + let canvas: ICanvas = this.renderer.canvas!; + let resources: RenderingResources = this.renderer.settings.display.resources; + let size: number = 12 * this.renderer.settings.display.scale; + let height: number = size * 2; + this.height += height; + let x: number = this.width / 2; + canvas.beginRender(this.width, height); + canvas.color = resources.mainGlyphColor; + canvas.font = new Font(resources.copyrightFont.family, size, FontStyle.Bold); + canvas.textAlign = TextAlign.Center; + canvas.fillText(msg, x, size); + let result: unknown = canvas.endRender(); + + let e = new RenderFinishedEventArgs(); + e.width = this.width; + e.height = height; + e.renderResult = result; + e.totalWidth = this.width; + e.totalHeight = this.height; + e.firstMasterBarIndex = -1; + e.lastMasterBarIndex = -1; + (this.renderer.partialRenderFinished as EventEmitterOfT).trigger(e); + } +} diff --git a/src/rendering/staves/BarLayoutingInfo.ts b/src/rendering/staves/BarLayoutingInfo.ts new file mode 100644 index 000000000..0e4edafcf --- /dev/null +++ b/src/rendering/staves/BarLayoutingInfo.ts @@ -0,0 +1,226 @@ +import { MidiUtils } from '@src/midi/MidiUtils'; +import { Beat } from '@src/model/Beat'; +import { Duration } from '@src/model/Duration'; +import { Spring } from '@src/rendering/staves/Spring'; +import { ModelUtils } from '@src/model/ModelUtils'; + +/** + * This public class stores size information about a stave. + * It is used by the layout engine to collect the sizes of score parts + * to align the parts across multiple staves. + */ +export class BarLayoutingInfo { + private static readonly MinDuration: number = 30; + private static readonly MinDurationWidth: number = 10; + + private _timeSortedSprings: Spring[] = []; + private _xMin: number = 0; + private _minTime: number = -1; + private _onTimePositionsForce: number = 0; + private _onTimePositions: Map = new Map(); + + /** + * an internal version number that increments whenever a change was made. + */ + public version: number = 0; + + public preBeatSizes: Map = new Map(); + public onBeatSizes: Map = new Map(); + public onBeatCenterX: Map = new Map(); + public preBeatSize: number = 0; + public postBeatSize: number = 0; + public voiceSize: number = 0; + public minStretchForce: number = 0; + public totalSpringConstant: number = 0; + + public updateVoiceSize(size: number): void { + if (size > this.voiceSize) { + this.voiceSize = size; + this.version++; + } + } + + public setPreBeatSize(beat: Beat, size: number): void { + if (!this.preBeatSizes.has(beat.index) || this.preBeatSizes.get(beat.index)! < size) { + this.preBeatSizes.set(beat.index, size); + this.version++; + } + } + + public getPreBeatSize(beat: Beat): number { + if (this.preBeatSizes.has(beat.index)) { + return this.preBeatSizes.get(beat.index)!; + } + return 0; + } + + public setOnBeatSize(beat: Beat, size: number): void { + if (!this.onBeatSizes.has(beat.index) || this.onBeatSizes.get(beat.index)! < size) { + this.onBeatSizes.set(beat.index, size); + this.version++; + } + } + + public getOnBeatSize(beat: Beat): number { + if (this.onBeatSizes.has(beat.index)) { + return this.onBeatSizes.get(beat.index)!; + } + return 0; + } + + public getBeatCenterX(beat: Beat): number { + if (this.onBeatCenterX.has(beat.index)) { + return this.onBeatCenterX.get(beat.index)!; + } + return 0; + } + + public setBeatCenterX(beat: Beat, x: number): void { + if (!this.onBeatCenterX.has(beat.index) || this.onBeatCenterX.get(beat.index)! < x) { + this.onBeatCenterX.set(beat.index, x); + this.version++; + } + } + + public updateMinStretchForce(force: number): void { + if (this.minStretchForce < force) { + this.minStretchForce = force; + this.version++; + } + } + + public springs: Map = new Map(); + + public addSpring(start: number, duration: number, preSpringSize: number, postSpringSize: number): Spring { + this.version++; + let spring: Spring; + if (!this.springs.has(start)) { + spring = new Spring(); + spring.timePosition = start; + spring.allDurations.push(duration); + // check in the previous spring for the shortest duration that overlaps with this spring + // Gourlay defines that we need the smallest note duration that either starts **or continues** on the current spring. + if (this._timeSortedSprings.length > 0) { + let smallestDuration: number = duration; + let previousSpring: Spring = this._timeSortedSprings[this._timeSortedSprings.length - 1]; + for (let prevDuration of previousSpring.allDurations) { + let end: number = previousSpring.timePosition + prevDuration; + if (end >= start && prevDuration < smallestDuration) { + smallestDuration = prevDuration; + } + } + } + spring.longestDuration = duration; + spring.postSpringWidth = postSpringSize; + spring.preSpringWidth = preSpringSize; + this.springs.set(start, spring); + let timeSorted: Spring[] = this._timeSortedSprings; + let insertPos: number = timeSorted.length - 1; + while (insertPos > 0 && timeSorted[insertPos].timePosition > start) { + insertPos--; + } + this._timeSortedSprings.splice(insertPos + 1, 0, spring); + } else { + spring = this.springs.get(start)!; + if (spring.postSpringWidth < postSpringSize) { + spring.postSpringWidth = postSpringSize; + } + if (spring.preSpringWidth < preSpringSize) { + spring.preSpringWidth = preSpringSize; + } + if (duration < spring.smallestDuration) { + spring.smallestDuration = duration; + } + if (duration > spring.longestDuration) { + spring.longestDuration = duration; + } + spring.allDurations.push(duration); + } + if (this._minTime === -1 || this._minTime > start) { + this._minTime = start; + } + return spring; + } + + public addBeatSpring(beat: Beat, preBeatSize: number, postBeatSize: number): Spring { + let start: number = beat.absoluteDisplayStart; + return this.addSpring(start, beat.displayDuration, preBeatSize, postBeatSize); + } + + public finish(): void { + this.calculateSpringConstants(); + this.version++; + } + + private calculateSpringConstants(): void { + this._xMin = 0; + let springs: Map = this.springs; + springs.forEach(spring => { + if (spring.springWidth < this._xMin) { + this._xMin = spring.springWidth; + } + }); + let totalSpringConstant: number = 0; + let sortedSprings: Spring[] = this._timeSortedSprings; + for (let i: number = 0; i < sortedSprings.length; i++) { + let currentSpring: Spring = sortedSprings[i]; + let duration: number = 0; + if (i === sortedSprings.length - 1) { + duration = currentSpring.longestDuration; + } else { + let nextSpring: Spring = sortedSprings[i + 1]; + duration = Math.abs(nextSpring.timePosition - currentSpring.timePosition); + } + currentSpring.springConstant = this.calculateSpringConstant(currentSpring, duration); + totalSpringConstant += 1 / currentSpring.springConstant; + } + this.totalSpringConstant = 1 / totalSpringConstant; + // calculate the force required to have at least the minimum size. + for (let i: number = 0; i < sortedSprings.length; i++) { + let force: number = sortedSprings[i].springWidth * sortedSprings[i].springConstant; + this.updateMinStretchForce(force); + } + } + + private calculateSpringConstant(spring: Spring, duration: number): number { + if (duration <= 0) { + duration = MidiUtils.toTicks(Duration.SixtyFourth); + } + if (spring.smallestDuration === 0) { + spring.smallestDuration = duration; + } + let minDuration: number = spring.smallestDuration; + let phi: number = 1 + 0.6 * Math.log2(duration / BarLayoutingInfo.MinDuration); + return (minDuration / duration) * (1 / (phi * BarLayoutingInfo.MinDurationWidth)); + } + + public spaceToForce(space: number): number { + return space * this.totalSpringConstant; + } + + public calculateVoiceWidth(force: number): number { + return this.calculateWidth(force, this.totalSpringConstant); + } + + public calculateWidth(force: number, springConstant: number): number { + return force / springConstant; + } + + public buildOnTimePositions(force: number): Map { + if (ModelUtils.isAlmostEqualTo(this._onTimePositionsForce, force) && this._onTimePositions) { + return this._onTimePositions; + } + this._onTimePositionsForce = force; + let positions: Map = (this._onTimePositions = new Map()); + let sortedSprings: Spring[] = this._timeSortedSprings; + if (sortedSprings.length === 0) { + return positions; + } + let springX: number = sortedSprings[0].preSpringWidth; + for (let i: number = 0; i < sortedSprings.length; i++) { + positions.set(sortedSprings[i].timePosition, springX); + springX += this.calculateWidth(force, sortedSprings[i].springConstant); + } + return positions; + } +} diff --git a/src/rendering/staves/MasterBarsRenderers.ts b/src/rendering/staves/MasterBarsRenderers.ts new file mode 100644 index 000000000..11dcfeb3f --- /dev/null +++ b/src/rendering/staves/MasterBarsRenderers.ts @@ -0,0 +1,16 @@ +import { MasterBar } from '@src/model/MasterBar'; +import { BarRendererBase } from '@src/rendering/BarRendererBase'; +import { BarLayoutingInfo } from '@src/rendering/staves/BarLayoutingInfo'; + +/** + * This container represents a single column of bar renderers independent from any staves. + * This container can be used to reorganize renderers into a new staves. + */ +export class MasterBarsRenderers { + public width: number = 0; + public isLinkedToPrevious: boolean = false; + public canWrap: boolean = true; + public masterBar!: MasterBar; + public renderers: BarRendererBase[] = []; + public layoutingInfo!: BarLayoutingInfo; +} diff --git a/src/rendering/staves/RenderStaff.ts b/src/rendering/staves/RenderStaff.ts new file mode 100644 index 000000000..7adc95f06 --- /dev/null +++ b/src/rendering/staves/RenderStaff.ts @@ -0,0 +1,188 @@ +import { Bar } from '@src/model/Bar'; +import { Staff } from '@src/model/Staff'; +import { ICanvas } from '@src/platform/ICanvas'; +import { BarRendererBase } from '@src/rendering/BarRendererBase'; +import { BarRendererFactory } from '@src/rendering/BarRendererFactory'; +import { BarLayoutingInfo } from '@src/rendering/staves/BarLayoutingInfo'; +import { StaveGroup } from '@src/rendering/staves/StaveGroup'; +import { StaveTrackGroup } from '@src/rendering/staves/StaveTrackGroup'; + +/** + * A Staff represents a single line within a StaveGroup. + * It stores BarRenderer instances created from a given factory. + */ +export class RenderStaff { + private _factory: BarRendererFactory; + private _sharedLayoutData: Map = new Map(); + + public staveTrackGroup!: StaveTrackGroup; + public staveGroup!: StaveGroup; + public barRenderers: BarRendererBase[] = []; + public x: number = 0; + public y: number = 0; + public height: number = 0; + public index: number = 0; + public staffIndex: number = 0; + + /** + * This is the index of the track being rendered. This is not the index of the track within the model, + * but the n-th track being rendered. It is the index of the {@link ScoreRenderer.tracks} array defining + * which tracks should be rendered. + * For single-track rendering this will always be zero. + */ + public trackIndex: number = 0; + public modelStaff: Staff; + + public get staveId(): string { + return this._factory.staffId; + } + + /** + * This is the visual offset from top where the + * Staff contents actually start. Used for grouping + * using a accolade + */ + public staveTop: number = 0; + + public topSpacing: number = 20; + public bottomSpacing: number = 5; + + /** + * This is the visual offset from top where the + * Staff contents actually ends. Used for grouping + * using a accolade + */ + public staveBottom: number = 0; + + public isFirstInAccolade: boolean = false; + public isLastInAccolade: boolean = false; + + public constructor(trackIndex: number, staff: Staff, factory: BarRendererFactory) { + this._factory = factory; + this.trackIndex = trackIndex; + this.modelStaff = staff; + } + + public getSharedLayoutData(key: string, def: T): T { + if (this._sharedLayoutData.has(key)) { + return this._sharedLayoutData.get(key) as T; + } + return def; + } + + public setSharedLayoutData(key: string, def: T): void { + this._sharedLayoutData.set(key, def); + } + + public get isInAccolade(): boolean { + return this._factory.isInAccolade; + } + + public get isRelevantForBoundsLookup(): boolean { + return this._factory.isRelevantForBoundsLookup; + } + + public registerStaffTop(offset: number): void { + this.staveTop = offset; + } + + public registerStaffBottom(offset: number): void { + this.staveBottom = offset; + } + + public addBarRenderer(renderer: BarRendererBase): void { + renderer.staff = this; + renderer.index = this.barRenderers.length; + renderer.reLayout(); + this.barRenderers.push(renderer); + this.staveGroup.layout.registerBarRenderer(this.staveId, renderer); + } + + public addBar(bar: Bar, layoutingInfo: BarLayoutingInfo): void { + let renderer: BarRendererBase; + if (!bar) { + renderer = new BarRendererBase(this.staveGroup.layout.renderer, bar); + } else { + renderer = this._factory.create(this.staveGroup.layout.renderer, bar); + } + renderer.staff = this; + renderer.index = this.barRenderers.length; + renderer.layoutingInfo = layoutingInfo; + renderer.doLayout(); + renderer.registerLayoutingInfo(); + this.barRenderers.push(renderer); + if (bar) { + this.staveGroup.layout.registerBarRenderer(this.staveId, renderer); + } + } + + public revertLastBar(): BarRendererBase { + let lastBar: BarRendererBase = this.barRenderers[this.barRenderers.length - 1]; + this.barRenderers.splice(this.barRenderers.length - 1, 1); + this.staveGroup.layout.unregisterBarRenderer(this.staveId, lastBar); + return lastBar; + } + + public scaleToWidth(width: number): void { + this._sharedLayoutData = new Map(); + // Note: here we could do some "intelligent" distribution of + // the space over the bar renderers, for now we evenly apply the space to all bars + let difference: number = width - this.staveGroup.width; + let spacePerBar: number = difference / this.barRenderers.length; + for (let i: number = 0, j: number = this.barRenderers.length; i < j; i++) { + this.barRenderers[i].scaleToWidth(this.barRenderers[i].width + spacePerBar); + } + } + + public get topOverflow(): number { + let m: number = 0; + for (let i: number = 0, j: number = this.barRenderers.length; i < j; i++) { + let r: BarRendererBase = this.barRenderers[i]; + if (r.topOverflow > m) { + m = r.topOverflow; + } + } + return m; + } + + public get bottomOverflow(): number { + let m: number = 0; + for (let i: number = 0, j: number = this.barRenderers.length; i < j; i++) { + let r: BarRendererBase = this.barRenderers[i]; + if (r.bottomOverflow > m) { + m = r.bottomOverflow; + } + } + return m; + } + + public finalizeStaff(): void { + let x: number = 0; + this.height = 0; + let topOverflow: number = this.topOverflow; + let bottomOverflow: number = this.bottomOverflow; + for (let i: number = 0; i < this.barRenderers.length; i++) { + this.barRenderers[i].x = x; + this.barRenderers[i].y = this.topSpacing + topOverflow; + this.height = Math.max(this.height, this.barRenderers[i].height); + this.barRenderers[i].finalizeRenderer(); + x += this.barRenderers[i].width; + } + if (this.height > 0) { + this.height += this.topSpacing + topOverflow + bottomOverflow + this.bottomSpacing; + } + } + + public paint(cx: number, cy: number, canvas: ICanvas, startIndex: number, count: number): void { + if (this.height === 0 || count === 0) { + return; + } + for ( + let i: number = startIndex, j: number = Math.min(startIndex + count, this.barRenderers.length); + i < j; + i++ + ) { + this.barRenderers[i].paint(cx + this.x, cy + this.y, canvas); + } + } +} diff --git a/src/rendering/staves/Spring.ts b/src/rendering/staves/Spring.ts new file mode 100644 index 000000000..31a8cb57e --- /dev/null +++ b/src/rendering/staves/Spring.ts @@ -0,0 +1,15 @@ +export class Spring { + public timePosition: number = 0; + public longestDuration: number = 0; + public smallestDuration: number = 0; + public force: number = 0; + public springConstant: number = 0; + + public get springWidth(): number { + return this.preSpringWidth + this.postSpringWidth; + } + + public preSpringWidth: number = 0; + public postSpringWidth: number = 0; + public allDurations: number[] = []; +} diff --git a/src/rendering/staves/StaveGroup.ts b/src/rendering/staves/StaveGroup.ts new file mode 100644 index 000000000..04e0621c9 --- /dev/null +++ b/src/rendering/staves/StaveGroup.ts @@ -0,0 +1,438 @@ +import { Bar } from '@src/model/Bar'; +import { Font } from '@src/model/Font'; +import { Track } from '@src/model/Track'; +import { ICanvas } from '@src/platform/ICanvas'; +import { BarRendererBase } from '@src/rendering/BarRendererBase'; +import { ScoreLayout } from '@src/rendering/layout/ScoreLayout'; +import { BarLayoutingInfo } from '@src/rendering/staves/BarLayoutingInfo'; +import { MasterBarsRenderers } from '@src/rendering/staves/MasterBarsRenderers'; +import { RenderStaff } from '@src/rendering/staves/RenderStaff'; +import { StaveTrackGroup } from '@src/rendering/staves/StaveTrackGroup'; +import { Bounds } from '@src/rendering/utils/Bounds'; +import { MasterBarBounds } from '@src/rendering/utils/MasterBarBounds'; +import { StaveGroupBounds } from '@src/rendering/utils/StaveGroupBounds'; +import { RenderingResources } from '@src/RenderingResources'; +import { NotationElement } from '@src/NotationSettings'; + +/** + * A Staff consists of a list of different staves and groups + * them using an accolade. + */ +export class StaveGroup { + private static readonly AccoladeLabelSpacing: number = 10; + + private _allStaves: RenderStaff[] = []; + private _firstStaffInAccolade: RenderStaff | null = null; + private _lastStaffInAccolade: RenderStaff | null = null; + private _accoladeSpacingCalculated: boolean = false; + + public x: number = 0; + public y: number = 0; + public index: number = 0; + + public accoladeSpacing: number = 0; + + /** + * Indicates whether this line is full or not. If the line is full the + * bars can be aligned to the maximum width. If the line is not full + * the bars will not get stretched. + */ + public isFull: boolean = false; + + /** + * The width that the content bars actually need + */ + public width: number = 0; + + public isLast: boolean = false; + public masterBarsRenderers: MasterBarsRenderers[] = []; + public staves: StaveTrackGroup[] = []; + public layout!: ScoreLayout; + + public get firstBarIndex(): number { + return this.masterBarsRenderers[0].masterBar.index; + } + + public get lastBarIndex(): number { + return this.masterBarsRenderers[this.masterBarsRenderers.length - 1].masterBar.index; + } + + public addMasterBarRenderers(tracks: Track[], renderers: MasterBarsRenderers): MasterBarsRenderers | null { + if (tracks.length === 0) { + return null; + } + this.masterBarsRenderers.push(renderers); + this.calculateAccoladeSpacing(tracks); + renderers.layoutingInfo.preBeatSize = 0; + let src: number = 0; + for (let i: number = 0, j: number = this.staves.length; i < j; i++) { + let g: StaveTrackGroup = this.staves[i]; + for (let k: number = 0, l: number = g.staves.length; k < l; k++) { + let s: RenderStaff = g.staves[k]; + let renderer: BarRendererBase = renderers.renderers[src++]; + s.addBarRenderer(renderer); + } + } + // Width += renderers.Width; + this.updateWidth(); + return renderers; + } + + public addBars(tracks: Track[], barIndex: number): MasterBarsRenderers | null { + if (tracks.length === 0) { + return null; + } + let result: MasterBarsRenderers = new MasterBarsRenderers(); + result.layoutingInfo = new BarLayoutingInfo(); + result.masterBar = tracks[0].score.masterBars[barIndex]; + this.masterBarsRenderers.push(result); + this.calculateAccoladeSpacing(tracks); + // add renderers + let barLayoutingInfo: BarLayoutingInfo = result.layoutingInfo; + for (let g of this.staves) { + for (let s of g.staves) { + let bar: Bar = g.track.staves[s.modelStaff.index].bars[barIndex]; + s.addBar(bar, barLayoutingInfo); + let renderer: BarRendererBase = s.barRenderers[s.barRenderers.length - 1]; + result.renderers.push(renderer); + if (renderer.isLinkedToPrevious) { + result.isLinkedToPrevious = true; + } + if (!renderer.canWrap) { + result.canWrap = false; + } + } + } + barLayoutingInfo.finish(); + // ensure same widths of new renderer + result.width = this.updateWidth(); + return result; + } + + public revertLastBar(): MasterBarsRenderers | null { + if (this.masterBarsRenderers.length > 1) { + let toRemove: MasterBarsRenderers = this.masterBarsRenderers[this.masterBarsRenderers.length - 1]; + this.masterBarsRenderers.splice(this.masterBarsRenderers.length - 1, 1); + let w: number = 0; + for (let i: number = 0, j: number = this._allStaves.length; i < j; i++) { + let s: RenderStaff = this._allStaves[i]; + let lastBar: BarRendererBase = s.revertLastBar(); + w = Math.max(w, lastBar.width); + } + this.width -= w; + return toRemove; + } + return null; + } + + public updateWidth(): number { + let realWidth: number = 0; + for (let i: number = 0, j: number = this._allStaves.length; i < j; i++) { + let s: RenderStaff = this._allStaves[i]; + s.barRenderers[s.barRenderers.length - 1].applyLayoutingInfo(); + if (s.barRenderers[s.barRenderers.length - 1].width > realWidth) { + realWidth = s.barRenderers[s.barRenderers.length - 1].width; + } + } + this.width += realWidth; + return realWidth; + } + + private calculateAccoladeSpacing(tracks: Track[]): void { + if (!this._accoladeSpacingCalculated && this.index === 0) { + this._accoladeSpacingCalculated = true; + if (!this.layout.renderer.settings.notation.isNotationElementVisible(NotationElement.TrackNames)) { + this.accoladeSpacing = 0; + } else { + let canvas: ICanvas = this.layout.renderer.canvas!; + let res: Font = this.layout.renderer.settings.display.resources.effectFont; + canvas.font = res; + for (let t of tracks) { + this.accoladeSpacing = Math.ceil(Math.max(this.accoladeSpacing, canvas.measureText(t.shortName))); + } + this.accoladeSpacing *= this.layout.scale; + this.accoladeSpacing += 2 * StaveGroup.AccoladeLabelSpacing * this.layout.scale; + this.width += this.accoladeSpacing; + } + } + } + + private getStaveTrackGroup(track: Track): StaveTrackGroup | null { + for (let i: number = 0, j: number = this.staves.length; i < j; i++) { + let g: StaveTrackGroup = this.staves[i]; + if (g.track === track) { + return g; + } + } + return null; + } + + public addStaff(track: Track, staff: RenderStaff): void { + let group: StaveTrackGroup | null = this.getStaveTrackGroup(track); + if (!group) { + group = new StaveTrackGroup(this, track); + this.staves.push(group); + } + staff.staveTrackGroup = group; + staff.staveGroup = this; + staff.index = this._allStaves.length; + this._allStaves.push(staff); + group.addStaff(staff); + if (staff.isInAccolade) { + if (!this._firstStaffInAccolade) { + this._firstStaffInAccolade = staff; + staff.isFirstInAccolade = true; + } + if (!group.firstStaffInAccolade) { + group.firstStaffInAccolade = staff; + } + if (!this._lastStaffInAccolade) { + this._lastStaffInAccolade = staff; + staff.isLastInAccolade = true; + } + if (this._lastStaffInAccolade) { + this._lastStaffInAccolade.isLastInAccolade = false; + } + this._lastStaffInAccolade = staff; + this._lastStaffInAccolade.isLastInAccolade = true; + group.lastStaffInAccolade = staff; + } + } + + public get height(): number { + return this._allStaves[this._allStaves.length - 1].y + this._allStaves[this._allStaves.length - 1].height; + } + + public scaleToWidth(width: number): void { + for (let i: number = 0, j: number = this._allStaves.length; i < j; i++) { + this._allStaves[i].scaleToWidth(width); + } + this.width = width; + } + + public paint(cx: number, cy: number, canvas: ICanvas): void { + this.paintPartial(cx + this.x, cy + this.y, canvas, 0, this.masterBarsRenderers.length); + } + + public paintPartial(cx: number, cy: number, canvas: ICanvas, startIndex: number, count: number): void { + this.buildBoundingsLookup(cx, cy); + for (let i: number = 0, j: number = this._allStaves.length; i < j; i++) { + this._allStaves[i].paint(cx, cy, canvas, startIndex, count); + } + let res: RenderingResources = this.layout.renderer.settings.display.resources; + if (this.staves.length > 0 && startIndex === 0) { + // + // Draw start grouping + // + canvas.color = res.barSeparatorColor; + if (this._firstStaffInAccolade && this._lastStaffInAccolade) { + // + // draw grouping line for all staves + // + let firstStart: number = + cy + + this._firstStaffInAccolade.y + + this._firstStaffInAccolade.staveTop + + this._firstStaffInAccolade.topSpacing + + this._firstStaffInAccolade.topOverflow; + let lastEnd: number = + cy + + this._lastStaffInAccolade.y + + this._lastStaffInAccolade.topSpacing + + this._lastStaffInAccolade.topOverflow + + this._lastStaffInAccolade.staveBottom; + let acooladeX: number = cx + this._firstStaffInAccolade.x; + canvas.beginPath(); + canvas.moveTo(acooladeX, firstStart); + canvas.lineTo(acooladeX, lastEnd); + canvas.stroke(); + } + // + // Draw accolade for each track group + // + canvas.font = res.effectFont; + for (let i: number = 0, j: number = this.staves.length; i < j; i++) { + let g: StaveTrackGroup = this.staves[i]; + if (g.firstStaffInAccolade && g.lastStaffInAccolade) { + let firstStart: number = + cy + + g.firstStaffInAccolade.y + + g.firstStaffInAccolade.staveTop + + g.firstStaffInAccolade.topSpacing + + g.firstStaffInAccolade.topOverflow; + let lastEnd: number = + cy + + g.lastStaffInAccolade.y + + g.lastStaffInAccolade.topSpacing + + g.lastStaffInAccolade.topOverflow + + g.lastStaffInAccolade.staveBottom; + let acooladeX: number = cx + g.firstStaffInAccolade.x; + let barSize: number = 3 * this.layout.renderer.settings.display.scale; + let barOffset: number = barSize; + let accoladeStart: number = firstStart - barSize * 4; + let accoladeEnd: number = lastEnd + barSize * 4; + // text + if (this.index === 0 && this.layout.renderer.settings.notation.isNotationElementVisible(NotationElement.TrackNames)) { + canvas.fillText( + g.track.shortName, + cx + StaveGroup.AccoladeLabelSpacing * this.layout.scale, + firstStart + ); + } + // rect + canvas.fillRect( + acooladeX - barOffset - barSize, + accoladeStart, + barSize, + accoladeEnd - accoladeStart + ); + let spikeStartX: number = acooladeX - barOffset - barSize; + let spikeEndX: number = acooladeX + barSize * 2; + // top spike + canvas.beginPath(); + canvas.moveTo(spikeStartX, accoladeStart); + canvas.bezierCurveTo( + spikeStartX, + accoladeStart, + spikeStartX, + accoladeStart, + spikeEndX, + accoladeStart - barSize + ); + canvas.bezierCurveTo( + acooladeX, + accoladeStart + barSize, + spikeStartX, + accoladeStart + barSize, + spikeStartX, + accoladeStart + barSize + ); + canvas.closePath(); + canvas.fill(); + // bottom spike + canvas.beginPath(); + canvas.moveTo(spikeStartX, accoladeEnd); + canvas.bezierCurveTo( + spikeStartX, + accoladeEnd, + acooladeX, + accoladeEnd, + spikeEndX, + accoladeEnd + barSize + ); + canvas.bezierCurveTo( + acooladeX, + accoladeEnd - barSize, + spikeStartX, + accoladeEnd - barSize, + spikeStartX, + accoladeEnd - barSize + ); + canvas.closePath(); + canvas.fill(); + } + } + } + } + + public finalizeGroup(): void { + let currentY: number = 0; + for (let staff of this._allStaves) { + staff.x = this.accoladeSpacing; + staff.y = currentY; + staff.finalizeStaff(); + currentY += staff.height; + } + } + + private buildBoundingsLookup(cx: number, cy: number): void { + if (this.layout.renderer.boundsLookup!.isFinished) { + return; + } + if (!this._firstStaffInAccolade || !this._lastStaffInAccolade) { + return; + } + let lastStaff: RenderStaff = this._allStaves[this._allStaves.length - 1]; + let visualTop: number = cy + this.y + this._firstStaffInAccolade.y; + let visualBottom: number = cy + this.y + this._lastStaffInAccolade.y + this._lastStaffInAccolade.height; + let realTop: number = cy + this.y + this._allStaves[0].y; + let realBottom: number = cy + this.y + lastStaff.y + lastStaff.height; + let lineTop: number = + cy + + this.y + + this._firstStaffInAccolade.y + + this._firstStaffInAccolade.topSpacing + + this._firstStaffInAccolade.topOverflow + + (this._firstStaffInAccolade.barRenderers.length > 0 + ? this._firstStaffInAccolade.barRenderers[0].topPadding + : 0); + let lineBottom: number = + cy + + this.y + + lastStaff.y + + lastStaff.height - + lastStaff.bottomSpacing - + lastStaff.bottomOverflow - + (lastStaff.barRenderers.length > 0 ? lastStaff.barRenderers[0].bottomPadding : 0); + let visualHeight: number = visualBottom - visualTop; + let lineHeight: number = lineBottom - lineTop; + let realHeight: number = realBottom - realTop; + let x: number = this.x + this._firstStaffInAccolade.x; + let staveGroupBounds: StaveGroupBounds = new StaveGroupBounds(); + staveGroupBounds.visualBounds = new Bounds(); + staveGroupBounds.visualBounds.x = cx; + staveGroupBounds.visualBounds.y = cy + this.y; + staveGroupBounds.visualBounds.w = this.width; + staveGroupBounds.visualBounds.h = this.height; + staveGroupBounds.realBounds = new Bounds(); + staveGroupBounds.realBounds.x = cx; + staveGroupBounds.realBounds.y = cy + this.y; + staveGroupBounds.realBounds.w = this.width; + staveGroupBounds.realBounds.h = this.height; + this.layout.renderer.boundsLookup!.addStaveGroup(staveGroupBounds); + let masterBarBoundsLookup: Map = new Map(); + for (let i: number = 0; i < this.staves.length; i++) { + for (let staff of this.staves[i].stavesRelevantForBoundsLookup) { + for (let renderer of staff.barRenderers) { + let masterBarBounds: MasterBarBounds; + if (!masterBarBoundsLookup.has(renderer.bar.masterBar.index)) { + masterBarBounds = new MasterBarBounds(); + masterBarBounds.index = renderer.bar.masterBar.index; + masterBarBounds.isFirstOfLine = renderer.isFirstOfLine; + masterBarBounds.realBounds = new Bounds(); + masterBarBounds.realBounds.x = x + renderer.x; + masterBarBounds.realBounds.y = realTop; + masterBarBounds.realBounds.w = renderer.width; + masterBarBounds.realBounds.h = realHeight; + + masterBarBounds.visualBounds = new Bounds(); + masterBarBounds.visualBounds.x = x + renderer.x; + masterBarBounds.visualBounds.y = visualTop; + masterBarBounds.visualBounds.w = renderer.width; + masterBarBounds.visualBounds.h = visualHeight; + + masterBarBounds.lineAlignedBounds = new Bounds(); + masterBarBounds.lineAlignedBounds.x = x + renderer.x; + masterBarBounds.lineAlignedBounds.y = lineTop; + masterBarBounds.lineAlignedBounds.w = renderer.width; + masterBarBounds.lineAlignedBounds.h = lineHeight; + this.layout.renderer.boundsLookup!.addMasterBar(masterBarBounds); + masterBarBoundsLookup.set(masterBarBounds.index, masterBarBounds); + } else { + masterBarBounds = masterBarBoundsLookup.get(renderer.bar.masterBar.index)!; + } + renderer.buildBoundingsLookup(masterBarBounds, x, cy + this.y + staff.y); + } + } + } + } + + public getBarX(index: number): number { + if (!this._firstStaffInAccolade || this.layout.renderer.tracks!.length === 0) { + return 0; + } + let bar: Bar = this.layout.renderer.tracks![0].staves[0].bars[index]; + let renderer: BarRendererBase = this.layout.getRendererForBar(this._firstStaffInAccolade.staveId, bar)!; + return renderer.x; + } +} diff --git a/src/rendering/staves/StaveTrackGroup.ts b/src/rendering/staves/StaveTrackGroup.ts new file mode 100644 index 000000000..e2490b71d --- /dev/null +++ b/src/rendering/staves/StaveTrackGroup.ts @@ -0,0 +1,24 @@ +import { Track } from '@src/model/Track'; +import { RenderStaff } from '@src/rendering/staves/RenderStaff'; +import { StaveGroup } from '@src/rendering/staves/StaveGroup'; + +export class StaveTrackGroup { + public track: Track; + public staveGroup: StaveGroup; + public staves: RenderStaff[] = []; + public stavesRelevantForBoundsLookup: RenderStaff[] = []; + public firstStaffInAccolade: RenderStaff | null = null; + public lastStaffInAccolade: RenderStaff | null = null; + + public constructor(staveGroup: StaveGroup, track: Track) { + this.staveGroup = staveGroup; + this.track = track; + } + + public addStaff(staff: RenderStaff): void { + this.staves.push(staff); + if (staff.isRelevantForBoundsLookup) { + this.stavesRelevantForBoundsLookup.push(staff); + } + } +} diff --git a/src/rendering/utils/AccidentalHelper.ts b/src/rendering/utils/AccidentalHelper.ts new file mode 100644 index 000000000..ecc6637d2 --- /dev/null +++ b/src/rendering/utils/AccidentalHelper.ts @@ -0,0 +1,228 @@ +import { AccidentalType } from '@src/model/AccidentalType'; +import { Bar } from '@src/model/Bar'; +import { Beat } from '@src/model/Beat'; +import { Clef } from '@src/model/Clef'; +import { Note } from '@src/model/Note'; +import { NoteAccidentalMode } from '@src/model/NoteAccidentalMode'; +import { Staff } from '@src/model/Staff'; +import { PercussionMapper } from '@src/rendering/utils/PercussionMapper'; +import { ModelUtils } from '@src/model/ModelUtils'; + +/** + * This small utilty public class allows the assignment of accidentals within a + * desired scope. + */ +export class AccidentalHelper { + private _bar: Bar; + + /** + * a lookup list containing an info whether the notes within an octave + * need an accidental rendered. the accidental symbol is determined based on the type of key signature. + */ + private static KeySignatureLookup: Array = [ + // Flats (where the value is true, a flat accidental is required for the notes) + [true, true, true, true, true, true, true, true, true, true, true, true], + [true, true, true, true, true, false, true, true, true, true, true, true], + [false, true, true, true, true, false, true, true, true, true, true, true], + [false, true, true, true, true, false, false, false, true, true, true, true], + [false, false, false, true, true, false, false, false, true, true, true, true], + [false, false, false, true, true, false, false, false, false, false, true, true], + [false, false, false, false, false, false, false, false, false, false, true, true], + // natural + [false, false, false, false, false, false, false, false, false, false, false, false], + // sharps (where the value is true, a flat accidental is required for the notes) + [false, false, false, false, false, true, true, false, false, false, false, false], + [true, true, false, false, false, true, true, false, false, false, false, false], + [true, true, false, false, false, true, true, true, true, false, false, false], + [true, true, true, true, false, true, true, true, true, false, false, false], + [true, true, true, true, false, true, true, true, true, true, true, false], + [true, true, true, true, true, true, true, true, true, true, true, false], + [true, true, true, true, true, true, true, true, true, true, true, true] + ]; + + /** + * Contains the list of notes within an octave have accidentals set. + */ + // prettier-ignore + private static AccidentalNotes: boolean[] = [ + false, true, false, true, false, false, true, false, true, false, true, false + ]; + + /** + * We always have 7 steps per octave. + * (by a step the offsets inbetween score lines is meant, + * 0 steps is on the first line (counting from top) + * 1 steps is on the space inbetween the first and the second line + */ + private static readonly StepsPerOctave: number = 7; + + /** + * Those are the amount of steps for the different clefs in case of a note value 0 + * [Neutral, C3, C4, F4, G2] + */ + private static OctaveSteps: number[] = [40, 34, 32, 28, 40]; + + /** + * The step offsets of the notes within an octave in case of for sharp keysignatures + */ + private static SharpNoteSteps: number[] = [0, 0, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6]; + + /** + * The step offsets of the notes within an octave in case of for flat keysignatures + */ + private static FlatNoteSteps: number[] = [0, 1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 6]; + + private _registeredAccidentals: Map = new Map(); + private _appliedScoreLines: Map = new Map(); + private _appliedScoreLinesByValue: Map = new Map(); + private _notesByValue: Map = new Map(); + + public maxNoteValueBeat: Beat | null = null; + public minNoteValueBeat: Beat | null = null; + public maxNoteValue: number = -1; + public minNoteValue: number = -1; + + public constructor(bar: Bar) { + this._bar = bar; + } + + /** + * Calculates the accidental for the given note and assignes the value to it. + * The new accidental type is also registered within the current scope + * @param note + * @returns + */ + public applyAccidental(note: Note): AccidentalType { + let staff: Staff = this._bar.staff; + let noteValue: number = staff.isPercussion + ? PercussionMapper.mapNoteForDisplay(note.displayValue) + : note.displayValue; + let quarterBend: boolean = note.hasQuarterToneOffset; + let line: number = this.registerNoteLine(note, noteValue); + if (this.minNoteValue === -1 || noteValue < this.minNoteValue) { + this.minNoteValue = noteValue; + this.minNoteValueBeat = note.beat; + } + if (this.maxNoteValue === -1 || noteValue > this.maxNoteValue) { + this.maxNoteValue = noteValue; + this.maxNoteValueBeat = note.beat; + } + return this.getAccidental(line, noteValue, quarterBend); + } + + /** + * Calculates the accidental for the given note value and assignes the value to it. + * The new accidental type is also registered within the current scope + * @param relatedBeat + * @param noteValue + * @param quarterBend + * @returns + */ + public applyAccidentalForValue(relatedBeat: Beat, noteValue: number, quarterBend: boolean): AccidentalType { + let staff: Staff = this._bar.staff; + if (staff.isPercussion) { + noteValue = PercussionMapper.mapNoteForDisplay(noteValue); + } + let line: number = this.registerNoteValueLine(noteValue); + if (this.minNoteValue === -1 || noteValue < this.minNoteValue) { + this.minNoteValue = noteValue; + this.minNoteValueBeat = relatedBeat; + } + if (this.maxNoteValue === -1 || noteValue > this.maxNoteValue) { + this.maxNoteValue = noteValue; + this.maxNoteValueBeat = relatedBeat; + } + return this.getAccidental(line, noteValue, quarterBend); + } + + private getAccidental(line: number, noteValue: number, quarterBend: boolean): AccidentalType { + let accidentalToSet: AccidentalType = AccidentalType.None; + if (!this._bar.staff.isPercussion) { + let ks: number = this._bar.masterBar.keySignature; + let ksi: number = ks + 7; + let index: number = noteValue % 12; + // the key signature symbol required according to + let keySignatureAccidental: AccidentalType = ksi < 7 ? AccidentalType.Flat : AccidentalType.Sharp; + // determine whether the current note requires an accidental according to the key signature + let hasNoteAccidentalForKeySignature: boolean = AccidentalHelper.KeySignatureLookup[ksi][index]; + let isAccidentalNote: boolean = AccidentalHelper.AccidentalNotes[index]; + if (quarterBend) { + accidentalToSet = isAccidentalNote ? keySignatureAccidental : AccidentalType.Natural; + } else { + let isAccidentalRegistered: boolean = this._registeredAccidentals.has(line); + if (hasNoteAccidentalForKeySignature !== isAccidentalNote && !isAccidentalRegistered) { + this._registeredAccidentals.set(line, true); + accidentalToSet = isAccidentalNote ? keySignatureAccidental : AccidentalType.Natural; + } else if (hasNoteAccidentalForKeySignature === isAccidentalNote && isAccidentalRegistered) { + this._registeredAccidentals.delete(line); + accidentalToSet = isAccidentalNote ? keySignatureAccidental : AccidentalType.Natural; + } + } + } + // TODO: change accidentalToSet according to note.AccidentalMode + if (quarterBend) { + switch (accidentalToSet) { + case AccidentalType.Natural: + return AccidentalType.NaturalQuarterNoteUp; + case AccidentalType.Sharp: + return AccidentalType.SharpQuarterNoteUp; + case AccidentalType.Flat: + return AccidentalType.FlatQuarterNoteUp; + } + } + return accidentalToSet; + } + + private registerNoteLine(n: Note, noteValue: number): number { + let steps: number = this.calculateNoteLine(noteValue, n.accidentalMode); + this._appliedScoreLines.set(n.id, steps); + this._notesByValue.set(noteValue, n); + return steps; + } + + private registerNoteValueLine(noteValue: number): number { + let steps: number = this.calculateNoteLine(noteValue, NoteAccidentalMode.Default); + this._appliedScoreLinesByValue.set(noteValue, steps); + return steps; + } + + private calculateNoteLine(noteValue: number, mode: NoteAccidentalMode): number { + let value: number = noteValue; + let ks: number = this._bar.masterBar.keySignature; + let clef: Clef = this._bar.clef; + let index: number = value % 12; + let octave: number = ((value / 12) | 0) - 1; + // Initial Position + let steps: number = AccidentalHelper.OctaveSteps[clef]; + // Move to Octave + steps -= octave * AccidentalHelper.StepsPerOctave; + // get the step list for the current keySignature + let stepList = + ModelUtils.keySignatureIsSharp(ks) || ModelUtils.keySignatureIsNatural(ks) + ? AccidentalHelper.SharpNoteSteps + : AccidentalHelper.FlatNoteSteps; + // Add offset for note itself + switch (mode) { + default: + // normal behavior: simply use the position where + // the keysignature defines the position + break; + } + steps -= stepList[index]; + return steps; + } + + public getNoteLine(n: Note): number { + return this._appliedScoreLines.get(n.id)!; + } + + public getNoteLineForValue(rawValue: number, searchForNote: boolean = false): number { + if (this._appliedScoreLinesByValue.has(rawValue)) { + return this._appliedScoreLinesByValue.get(rawValue)!; + } + if (searchForNote && this._notesByValue.has(rawValue)) { + return this.getNoteLine(this._notesByValue.get(rawValue)!); + } + return 0; + } +} diff --git a/src/rendering/utils/BarBounds.ts b/src/rendering/utils/BarBounds.ts new file mode 100644 index 000000000..3e8c5af2b --- /dev/null +++ b/src/rendering/utils/BarBounds.ts @@ -0,0 +1,61 @@ +import { Bar } from '@src/model/Bar'; +import { BeatBounds } from '@src/rendering/utils/BeatBounds'; +import { Bounds } from '@src/rendering/utils/Bounds'; +import { MasterBarBounds } from '@src/rendering/utils/MasterBarBounds'; + +/** + * Represents the boundaries of a single bar. + */ +export class BarBounds { + /** + * Gets or sets the reference to the related {@link MasterBarBounds} + */ + public masterBarBounds!: MasterBarBounds; + + /** + * Gets or sets the bounds covering all visually visible elements spanning this bar. + */ + public visualBounds!: Bounds; + + /** + * Gets or sets the actual bounds of the elements in this bar including whitespace areas. + */ + public realBounds!: Bounds; + + /** + * Gets or sets the bar related to this boundaries. + */ + public bar!: Bar; + + /** + * Gets or sets a list of the beats contained in this lookup. + */ + public beats: BeatBounds[] = []; + + /** + * Adds a new beat to this lookup. + * @param bounds The beat bounds to add. + */ + public addBeat(bounds: BeatBounds): void { + bounds.barBounds = this; + this.beats.push(bounds); + this.masterBarBounds.addBeat(bounds); + } + + /** + * Tries to find the beat at the given X-position. + * @param x The X-position of the beat to find. + * @returns The beat at the given X-position or null if none was found. + */ + public findBeatAtPos(x: number): BeatBounds | null { + let beat: BeatBounds | null = null; + for (let t of this.beats) { + if (!beat || t.realBounds.x < x) { + beat = t; + } else if (t.realBounds.x > x) { + break; + } + } + return beat; + } +} diff --git a/src/rendering/utils/BarHelpers.ts b/src/rendering/utils/BarHelpers.ts new file mode 100644 index 000000000..a019e545e --- /dev/null +++ b/src/rendering/utils/BarHelpers.ts @@ -0,0 +1,63 @@ +import { Bar } from '@src/model/Bar'; +import { Beat } from '@src/model/Beat'; +import { GraceType } from '@src/model/GraceType'; +import { Voice } from '@src/model/Voice'; +import { BeamingHelper } from '@src/rendering/utils/BeamingHelper'; + +export class BarHelpers { + public beamHelpers: BeamingHelper[][] = []; + public beamHelperLookup: Map[] = []; + + public constructor(bar: Bar) { + let currentBeamHelper: BeamingHelper | null = null; + let currentGraceBeamHelper: BeamingHelper | null = null; + if (bar) { + for (let i: number = 0, j: number = bar.voices.length; i < j; i++) { + let v: Voice = bar.voices[i]; + this.beamHelpers.push([]); + this.beamHelperLookup.push(new Map()); + for (let k: number = 0, l: number = v.beats.length; k < l; k++) { + let b: Beat = v.beats[k]; + let helperForBeat: BeamingHelper | null; + if (b.graceType !== GraceType.None) { + helperForBeat = currentGraceBeamHelper; + } else { + helperForBeat = currentBeamHelper; + currentGraceBeamHelper = null; + } + // if a new beaming helper was started, we close our tuplet grouping as well + if (!b.isRest) { + // try to fit beam to current beamhelper + if (!helperForBeat || !helperForBeat.checkBeat(b)) { + if (helperForBeat) { + helperForBeat.finish(); + } + // if not possible, create the next beaming helper + helperForBeat = new BeamingHelper(bar.staff); + helperForBeat.checkBeat(b); + if (b.graceType !== GraceType.None) { + currentGraceBeamHelper = helperForBeat; + } else { + currentBeamHelper = helperForBeat; + } + this.beamHelpers[v.index].push(helperForBeat); + } + } + this.beamHelperLookup[v.index].set(b.index, helperForBeat!); + } + if (currentBeamHelper) { + currentBeamHelper.finish(); + } + if (currentGraceBeamHelper) { + currentGraceBeamHelper.finish(); + } + currentBeamHelper = null; + currentGraceBeamHelper = null; + } + } + } + + public getBeamingHelperForBeat(beat: Beat): BeamingHelper { + return this.beamHelperLookup[beat.voice.index].get(beat.index)!; + } +} diff --git a/src/rendering/utils/BeamBarType.ts b/src/rendering/utils/BeamBarType.ts new file mode 100644 index 000000000..36723eb47 --- /dev/null +++ b/src/rendering/utils/BeamBarType.ts @@ -0,0 +1,17 @@ +/** + * Lists all types how two voices can be joined with bars. + */ +export enum BeamBarType { + /** + * Full Bar from current to next + */ + Full, + /** + * A small Bar from current to previous + */ + PartLeft, + /** + * A small bar from current to next + */ + PartRight +} diff --git a/src/rendering/utils/BeamDirection.ts b/src/rendering/utils/BeamDirection.ts new file mode 100644 index 000000000..451022f63 --- /dev/null +++ b/src/rendering/utils/BeamDirection.ts @@ -0,0 +1,4 @@ +export enum BeamDirection { + Up, + Down +} \ No newline at end of file diff --git a/src/rendering/utils/BeamingHelper.ts b/src/rendering/utils/BeamingHelper.ts new file mode 100644 index 000000000..192efa42f --- /dev/null +++ b/src/rendering/utils/BeamingHelper.ts @@ -0,0 +1,447 @@ +import { Bar } from '@src/model/Bar'; +import { Beat } from '@src/model/Beat'; +import { Duration } from '@src/model/Duration'; +import { Fingers } from '@src/model/Fingers'; +import { GraceType } from '@src/model/GraceType'; +import { HarmonicType } from '@src/model/HarmonicType'; +import { Note } from '@src/model/Note'; +import { Staff } from '@src/model/Staff'; +import { Voice } from '@src/model/Voice'; +import { BeamDirection } from '@src/rendering/utils/BeamDirection'; +import { BeatLinePositions } from '@src/rendering/utils/BeatLinePositions'; +import { IBeamYCalculator } from '@src/rendering/utils/IBeamYCalculator'; +import { PercussionMapper } from '@src/rendering/utils/PercussionMapper'; +import { ModelUtils } from '@src/model/ModelUtils'; +import { MidiUtils } from '@src/midi/MidiUtils'; + +/** + * This public class helps drawing beams and bars for notes. + */ +export class BeamingHelper { + private static ScoreMiddleKeys: number[] = [71, 60, 57, 50, 71]; + + private _staff: Staff; + private _beatLineXPositions: Map = new Map(); + public voice: Voice | null = null; + + public beats: Beat[] = []; + + public shortestDuration: Duration = Duration.QuadrupleWhole; + + /** + * the number of fingering indicators that will be drawn + */ + public fingeringCount: number = 0; + + /** + * an indicator whether any beat has a tuplet on it. + */ + public hasTuplet: boolean = false; + + /** + * the first min note within this group + */ + public firstMinNoteValue: number = -1; + + /** + * the first max note within this group + */ + public firstMaxNoteValue: number = -1; + + /** + * the last min note within this group + */ + public lastMinNoteValue: number = -1; + + /** + * the last max note within this group + */ + public lastMaxNoteValue: number = -1; + + /** + * the overall min note value within this group. + * This includes values caused by bends. + */ + public minNoteValue: number = -1; + + public minNoteBeat: Beat | null = null; + + /** + * the overall max note value within this group + * This includes values caused by bends. + */ + public maxNoteValue: number = 0; + + public maxNoteBeat: Beat | null = null; + public invertBeamDirection: boolean = false; + public preferredBeamDirection: BeamDirection | null = null; + public isGrace: boolean = false; + + public constructor(staff: Staff) { + this._staff = staff; + this.beats = []; + } + + private getValue(n: Note): number { + if (this._staff.isPercussion) { + return PercussionMapper.mapNoteForDisplay(n.displayValue); + } + return n.displayValue; + } + + private getMaxValue(n: Note): number { + let value: number = this.getValue(n); + if (n.harmonicType !== HarmonicType.None && n.harmonicType !== HarmonicType.Natural) { + value = n.realValue - this._staff.displayTranspositionPitch; + } + return value; + } + + private getMinValue(n: Note): number { + return this.getValue(n); + } + + public getBeatLineX(beat: Beat): number { + if (this.hasBeatLineX(beat)) { + if (this.direction === BeamDirection.Up) { + return this._beatLineXPositions.get(beat.index)!.up; + } + return this._beatLineXPositions.get(beat.index)!.down; + } + return 0; + } + + public hasBeatLineX(beat: Beat): boolean { + return this._beatLineXPositions.has(beat.index); + } + + public registerBeatLineX(staffId: string, beat: Beat, up: number, down: number): void { + let positions: BeatLinePositions = this.getOrCreateBeatPositions(beat); + positions.staffId = staffId; + positions.up = up; + positions.down = down; + } + + private getOrCreateBeatPositions(beat: Beat): BeatLinePositions { + if (!this._beatLineXPositions.has(beat.index)) { + this._beatLineXPositions.set(beat.index, new BeatLinePositions()); + } + return this._beatLineXPositions.get(beat.index)!; + } + + public direction: BeamDirection = BeamDirection.Up; + + public finish(): void { + this.direction = this.calculateDirection(); + } + + private calculateDirection(): BeamDirection { + let preferredBeamDirection = this.preferredBeamDirection; + if (preferredBeamDirection !== null) { + return preferredBeamDirection; + } + + if (!this.voice) { + return BeamDirection.Up; + } + + // multivoice handling + if (this.voice.index > 0) { + return this.invert(BeamDirection.Down); + } + if (this.voice.bar.voices.length > 1) { + for (let v: number = 1; v < this.voice.bar.voices.length; v++) { + if (!this.voice.bar.voices[v].isEmpty) { + return this.invert(BeamDirection.Up); + } + } + } + if (this.beats[0].graceType !== GraceType.None) { + return this.invert(BeamDirection.Up); + } + // the average key is used for determination + // key lowerequal than middle line -> up + // key higher than middle line -> down + let avg: number = ((this.maxNoteValue + this.minNoteValue) / 2) | 0; + return this.invert( + avg < BeamingHelper.ScoreMiddleKeys[this.beats[this.beats.length - 1].voice.bar.clef] + ? BeamDirection.Up + : BeamDirection.Down + ); + } + + private invert(direction: BeamDirection): BeamDirection { + if (!this.invertBeamDirection) { + return direction; + } + switch (direction) { + case BeamDirection.Down: + return BeamDirection.Up; + case BeamDirection.Up: + default: + return BeamDirection.Down; + } + } + + public checkBeat(beat: Beat): boolean { + if (beat.invertBeamDirection) { + this.invertBeamDirection = true; + } + if (!this.voice) { + this.voice = beat.voice; + } + // allow adding if there are no beats yet + let add: boolean = false; + if (this.beats.length === 0) { + add = true; + } else if (BeamingHelper.canJoin(this.beats[this.beats.length - 1], beat)) { + add = true; + } + if (add) { + if (beat.preferredBeamDirection !== null) { + this.preferredBeamDirection = beat.preferredBeamDirection; + } + + this.beats.push(beat); + if (beat.graceType !== GraceType.None) { + this.isGrace = true; + } + let positions: BeatLinePositions = this.getOrCreateBeatPositions(beat); + if (beat.hasTuplet) { + this.hasTuplet = true; + } + let fingeringCount: number = 0; + for (let n: number = 0; n < beat.notes.length; n++) { + let note: Note = beat.notes[n]; + if (note.leftHandFinger !== Fingers.Unknown || note.rightHandFinger !== Fingers.Unknown) { + fingeringCount++; + } + } + if (fingeringCount > this.fingeringCount) { + this.fingeringCount = fingeringCount; + } + this.lastMinNoteValue = -1; + this.lastMaxNoteValue = -1; + this.checkNote(beat.minNote); + this.checkNote(beat.maxNote); + positions.minNoteValue = this.lastMinNoteValue; + positions.maxNoteValue = this.lastMaxNoteValue; + if (this.shortestDuration < beat.duration) { + this.shortestDuration = beat.duration; + } + if (beat.hasTuplet) { + this.hasTuplet = true; + } + } + return add; + } + + private checkNote(note: Note | null): void { + if (!note) { + return; + } + + let value: number = this.getValue(note); + if (this.beats.length === 1 && this.beats[0] === note.beat) { + if (this.firstMinNoteValue === -1 || value < this.firstMinNoteValue) { + this.firstMinNoteValue = value; + } + if (this.firstMaxNoteValue === -1 || value > this.firstMaxNoteValue) { + this.firstMaxNoteValue = value; + } + } + if (this.lastMinNoteValue === -1 || value < this.lastMinNoteValue) { + this.lastMinNoteValue = value; + } + if (this.lastMaxNoteValue === -1 || value > this.lastMaxNoteValue) { + this.lastMaxNoteValue = value; + } + let minValue: number = this.getMinValue(note); + if (this.minNoteValue === -1 || this.minNoteValue > minValue) { + this.minNoteValue = minValue; + this.minNoteBeat = note.beat; + } + let maxValue: number = this.getMaxValue(note); + if (this.maxNoteValue === -1 || this.maxNoteValue < maxValue) { + this.maxNoteValue = maxValue; + this.maxNoteBeat = note.beat; + } + } + + public calculateBeamY( + stemSize: number, + xCorrection: number, + xPosition: number, + scale: number, + yPosition: IBeamYCalculator + ): number { + return this.calculateBeamYWithDirection(stemSize, xCorrection, xPosition, scale, yPosition, this.direction); + } + + public calculateBeamYWithDirection( + stemSize: number, + xCorrection: number, + xPosition: number, + scale: number, + yPosition: IBeamYCalculator, + direction: BeamDirection + ): number { + // create a line between the min and max note of the group + if (this.beats.length === 1) { + if (direction === BeamDirection.Up) { + return yPosition.getYPositionForNoteValue(this.maxNoteValue) - stemSize; + } + return yPosition.getYPositionForNoteValue(this.minNoteValue) + stemSize; + } + // we use the min/max notes to place the beam along their real position + // we only want a maximum of 10 offset for their gradient + let maxDistance: number = 10 * scale; + // if the min note is not first or last, we can align notes directly to the position + // of the min note + if ( + direction === BeamDirection.Down && + this.minNoteBeat !== this.beats[0] && + this.minNoteBeat !== this.beats[this.beats.length - 1] + ) { + return yPosition.getYPositionForNoteValue(this.minNoteValue) + stemSize; + } + if ( + direction === BeamDirection.Up && + this.maxNoteBeat !== this.beats[0] && + this.minNoteBeat !== this.beats[this.beats.length - 1] + ) { + return yPosition.getYPositionForNoteValue(this.maxNoteValue) - stemSize; + } + let startX: number = this.getBeatLineX(this.beats[0]) + xCorrection; + let startY: number = + direction === BeamDirection.Up + ? yPosition.getYPositionForNoteValue(this.firstMaxNoteValue) - stemSize + : yPosition.getYPositionForNoteValue(this.firstMinNoteValue) + stemSize; + let endX: number = this.getBeatLineX(this.beats[this.beats.length - 1]) + xCorrection; + let endY: number = + direction === BeamDirection.Up + ? yPosition.getYPositionForNoteValue(this.lastMaxNoteValue) - stemSize + : yPosition.getYPositionForNoteValue(this.lastMinNoteValue) + stemSize; + // ensure the maxDistance + if (direction === BeamDirection.Down && startY > endY && startY - endY > maxDistance) { + endY = startY - maxDistance; + } + if (direction === BeamDirection.Down && endY > startY && endY - startY > maxDistance) { + startY = endY - maxDistance; + } + if (direction === BeamDirection.Up && startY < endY && endY - startY > maxDistance) { + endY = startY + maxDistance; + } + if (direction === BeamDirection.Up && endY < startY && startY - endY > maxDistance) { + startY = endY + maxDistance; + } + // get the y position of the given beat on this curve + if (startX === endX) { + return startY; + } + // y(x) = ( (y2 - y1) / (x2 - x1) ) * (x - x1) + y1; + return ((endY - startY) / (endX - startX)) * (xPosition - startX) + startY; + } + + // TODO: Check if this beaming is really correct, I'm not sure if we are connecting beats correctly + private static canJoin(b1: Beat, b2: Beat): boolean { + // is this a voice we can join with? + if ( + !b1 || + !b2 || + b1.isRest || + b2.isRest || + b1.graceType !== b2.graceType || + b1.graceType === GraceType.BendGrace || + b2.graceType === GraceType.BendGrace + ) { + return false; + } + if (b1.graceType !== GraceType.None && b2.graceType !== GraceType.None) { + return true; + } + let m1: Bar = b1.voice.bar; + let m2: Bar = b1.voice.bar; + // only join on same measure + if (m1 !== m2) { + return false; + } + // get times of those voices and check if the times + // are in the same division + let start1: number = b1.playbackStart; + let start2: number = b2.playbackStart; + // we can only join 8th, 16th, 32th and 64th voices + if (!BeamingHelper.canJoinDuration(b1.duration) || !BeamingHelper.canJoinDuration(b2.duration)) { + return start1 === start2; + } + // break between different tuplet groups + if (b1.tupletGroup !== b2.tupletGroup) { + return false; + } + if (b1.hasTuplet && b2.hasTuplet) { + // force joining for full tuplet groups + if (b1.tupletGroup === b2.tupletGroup && b1.tupletGroup!.isFull) { + return true; + } + } + // TODO: create more rules for automatic beaming + let divisionLength: number = MidiUtils.QuarterTime; + switch (m1.masterBar.timeSignatureDenominator) { + case 8: + if (m1.masterBar.timeSignatureNumerator % 3 === 0) { + divisionLength += (MidiUtils.QuarterTime / 2) | 0; + } + break; + } + // check if they are on the same division + let division1: number = ((divisionLength + start1) / divisionLength) | 0 | 0; + let division2: number = ((divisionLength + start2) / divisionLength) | 0 | 0; + return division1 === division2; + } + + private static canJoinDuration(d: Duration): boolean { + switch (d) { + case Duration.Whole: + case Duration.Half: + case Duration.Quarter: + return false; + default: + return true; + } + } + + public static isFullBarJoin(a: Beat, b: Beat, barIndex: number): boolean { + // TODO: this getindex call seems expensive since we call this method very often. + return ModelUtils.getIndex(a.duration) - 2 - barIndex > 0 && ModelUtils.getIndex(b.duration) - 2 - barIndex > 0; + } + + /** + * Returns whether the the position of the given beat, was registered by the staff of the given ID + * @param staffId + * @param beat + * @returns + */ + public isPositionFrom(staffId: string, beat: Beat): boolean { + if (!this._beatLineXPositions.has(beat.index)) { + return true; + } + return ( + this._beatLineXPositions.get(beat.index)!.staffId === staffId || + !this._beatLineXPositions.get(beat.index)!.staffId + ); + } + + public getBeatMinValue(beat: Beat): number { + if (!this._beatLineXPositions.has(beat.index)) { + return beat.minNote!.displayValue; + } + return this._beatLineXPositions.get(beat.index)!.minNoteValue; + } + + public getBeatMaxValue(beat: Beat): number { + if (!this._beatLineXPositions.has(beat.index)) { + return beat.maxNote!.displayValue; + } + return this._beatLineXPositions.get(beat.index)!.maxNoteValue; + } +} diff --git a/src/rendering/utils/BeatBounds.ts b/src/rendering/utils/BeatBounds.ts new file mode 100644 index 000000000..4abd37e33 --- /dev/null +++ b/src/rendering/utils/BeatBounds.ts @@ -0,0 +1,70 @@ +import { Beat } from '@src/model/Beat'; +import { Note } from '@src/model/Note'; +import { BarBounds } from '@src/rendering/utils/BarBounds'; +import { Bounds } from '@src/rendering/utils/Bounds'; +import { NoteBounds } from '@src/rendering/utils/NoteBounds'; + +/** + * Represents the bounds of a single beat. + */ +export class BeatBounds { + /** + * Gets or sets the reference to the parent {@link BarBounds}. + */ + public barBounds!: BarBounds; + + /** + * Gets or sets the bounds covering all visually visible elements spanning this beat. + */ + public visualBounds!: Bounds; + + /** + * Gets or sets the actual bounds of the elements in this beat including whitespace areas. + */ + public realBounds!: Bounds; + + /** + * Gets or sets the beat related to this bounds. + */ + public beat!: Beat; + + /** + * Gets or sets the individual note positions of this beat (if {@link CoreSettings.includeNoteBounds} was set to true). + */ + public notes: NoteBounds[] | null = null; + + /** + * Adds a new note to this bounds. + * @param bounds The note bounds to add. + */ + public addNote(bounds: NoteBounds): void { + if (!this.notes) { + this.notes = []; + } + bounds.beatBounds = this; + this.notes.push(bounds); + } + + /** + * Tries to find a note at the given position. + * @param x The X-position of the note to find. + * @param y The Y-position of the note to find. + * @returns The note at the given position or null if no note was found, or the note lookup was not enabled before rendering. + */ + public findNoteAtPos(x: number, y: number): Note | null { + if (!this.notes) { + return null; + } + // TODO: can be likely optimized + // a beat is mostly vertically aligned, we could sort the note bounds by Y + // and then do a binary search on the Y-axis. + for (let note of this.notes) { + let bottom: number = note.noteHeadBounds.y + note.noteHeadBounds.h; + let right: number = note.noteHeadBounds.x + note.noteHeadBounds.w; + if (note.noteHeadBounds.x >= x && note.noteHeadBounds.y >= y && x <= right && y <= bottom) { + return note.note; + } + } + return null; + } +} diff --git a/src/rendering/utils/BeatLinePositions.ts b/src/rendering/utils/BeatLinePositions.ts new file mode 100644 index 000000000..f29d48895 --- /dev/null +++ b/src/rendering/utils/BeatLinePositions.ts @@ -0,0 +1,7 @@ +export class BeatLinePositions { + public staffId: string = ''; + public up: number = 0; + public down: number = 0; + public minNoteValue: number = 0; + public maxNoteValue: number = 0; +} diff --git a/src/rendering/utils/Bounds.ts b/src/rendering/utils/Bounds.ts new file mode 100644 index 000000000..d0b4ba0d9 --- /dev/null +++ b/src/rendering/utils/Bounds.ts @@ -0,0 +1,24 @@ +/** + * Represents a rectangular area within the renderer music notation. + */ +export class Bounds { + /** + * Gets or sets the X-position of the rectangle within the music notation. + */ + public x: number = 0; + + /** + * Gets or sets the Y-position of the rectangle within the music notation. + */ + public y: number = 0; + + /** + * Gets or sets the width of the rectangle. + */ + public w: number = 0; + + /** + * Gets or sets the height of the rectangle. + */ + public h: number = 0; +} diff --git a/src/rendering/utils/BoundsLookup.ts b/src/rendering/utils/BoundsLookup.ts new file mode 100644 index 000000000..b8bf1161e --- /dev/null +++ b/src/rendering/utils/BoundsLookup.ts @@ -0,0 +1,290 @@ +import { Beat } from '@src/model/Beat'; +import { MasterBar } from '@src/model/MasterBar'; +import { Note } from '@src/model/Note'; +import { Score } from '@src/model/Score'; +import { BarBounds } from '@src/rendering/utils/BarBounds'; +import { BeatBounds } from '@src/rendering/utils/BeatBounds'; +import { Bounds } from '@src/rendering/utils/Bounds'; +import { MasterBarBounds } from '@src/rendering/utils/MasterBarBounds'; +import { NoteBounds } from '@src/rendering/utils/NoteBounds'; +import { StaveGroupBounds } from '@src/rendering/utils/StaveGroupBounds'; + +export class BoundsLookup { + /** + * @target web + */ + public toJson(): unknown { + let json: any = {} as any; + let staveGroups: StaveGroupBounds[] = []; + json.staveGroups = staveGroups; + for (let group of this.staveGroups) { + let g: StaveGroupBounds = {} as any; + g.visualBounds = this.boundsToJson(group.visualBounds); + g.realBounds = this.boundsToJson(group.realBounds); + g.bars = []; + for (let masterBar of group.bars) { + let mb: MasterBarBounds = {} as any; + mb.lineAlignedBounds = this.boundsToJson(masterBar.lineAlignedBounds); + mb.visualBounds = this.boundsToJson(masterBar.visualBounds); + mb.realBounds = this.boundsToJson(masterBar.realBounds); + mb.index = masterBar.index; + mb.bars = []; + for (let bar of masterBar.bars) { + let b: BarBounds = {} as any; + b.visualBounds = this.boundsToJson(bar.visualBounds); + b.realBounds = this.boundsToJson(bar.realBounds); + b.beats = []; + for (let beat of bar.beats) { + let bb: BeatBounds = {} as any; + bb.visualBounds = this.boundsToJson(beat.visualBounds); + bb.realBounds = this.boundsToJson(beat.realBounds); + let bbd: any = bb; + bbd.beatIndex = beat.beat.index; + bbd.voiceIndex = beat.beat.voice.index; + bbd.barIndex = beat.beat.voice.bar.index; + bbd.staffIndex = beat.beat.voice.bar.staff.index; + bbd.trackIndex = beat.beat.voice.bar.staff.track.index; + if (beat.notes) { + let notes: NoteBounds[] = (bb.notes = []); + for (let note of beat.notes) { + let n: NoteBounds = {} as any; + let nd: any = n; + nd.index = note.note.index; + n.noteHeadBounds = this.boundsToJson(note.noteHeadBounds); + notes.push(n); + } + } + b.beats.push(bb); + } + mb.bars.push(b); + } + g.bars.push(mb); + } + staveGroups.push(g); + } + return json; + } + + /** + * @target web + */ + public static fromJson(json: unknown, score: Score): BoundsLookup { + let lookup: BoundsLookup = new BoundsLookup(); + let staveGroups: StaveGroupBounds[] = (json as any)['staveGroups']; + for (let staveGroup of staveGroups) { + let sg: StaveGroupBounds = new StaveGroupBounds(); + sg.visualBounds = staveGroup.visualBounds; + sg.realBounds = staveGroup.realBounds; + lookup.addStaveGroup(sg); + for (let masterBar of staveGroup.bars) { + let mb: MasterBarBounds = new MasterBarBounds(); + mb.index = masterBar.index; + mb.isFirstOfLine = masterBar.isFirstOfLine; + mb.lineAlignedBounds = masterBar.lineAlignedBounds; + mb.visualBounds = masterBar.visualBounds; + mb.realBounds = masterBar.realBounds; + sg.addBar(mb); + for (let bar of masterBar.bars) { + let b: BarBounds = new BarBounds(); + b.visualBounds = bar.visualBounds; + b.realBounds = bar.realBounds; + mb.addBar(b); + for (let beat of bar.beats) { + let bb: BeatBounds = new BeatBounds(); + bb.visualBounds = beat.visualBounds; + bb.realBounds = beat.realBounds; + let bd: any = beat; + bb.beat = + score.tracks[bd.trackIndex].staves[bd.staffIndex].bars[bd.barIndex].voices[ + bd.voiceIndex + ].beats[bd.beatIndex]; + if (beat.notes) { + bb.notes = []; + for (let note of beat.notes) { + let n: NoteBounds = new NoteBounds(); + let nd: any = note; + n.note = bb.beat.notes[nd.index]; + n.noteHeadBounds = note.noteHeadBounds; + bb.addNote(n); + } + } + b.addBeat(bb); + } + } + } + } + return lookup; + } + + /** + * @target web + */ + private boundsToJson(bounds: Bounds): Bounds { + let json: Bounds = {} as any; + json.x = bounds.x; + json.y = bounds.y; + json.w = bounds.w; + json.h = bounds.h; + return json; + } + + private _beatLookup: Map = new Map(); + private _masterBarLookup: Map = new Map(); + private _currentStaveGroup: StaveGroupBounds | null = null; + /** + * Gets a list of all individual stave groups contained in the rendered music notation. + */ + public staveGroups: StaveGroupBounds[] = []; + + /** + * Gets or sets a value indicating whether this lookup was finished already. + */ + public isFinished: boolean = false; + + /** + * Finishes the lookup for optimized access. + */ + public finish(): void { + for (let t of this.staveGroups) { + t.finish(); + } + this.isFinished = true; + } + + /** + * Adds a new note to the lookup. + * @param bounds The note bounds to add. + */ + public addNote(bounds: NoteBounds): void { + let beat = this.findBeat(bounds.note.beat); + beat!.addNote(bounds); + } + + /** + * Adds a new stave group to the lookup. + * @param bounds The stave group bounds to add. + */ + public addStaveGroup(bounds: StaveGroupBounds): void { + bounds.index = this.staveGroups.length; + bounds.boundsLookup = this; + this.staveGroups.push(bounds); + this._currentStaveGroup = bounds; + } + + /** + * Adds a new master bar to the lookup. + * @param bounds The master bar bounds to add. + */ + public addMasterBar(bounds: MasterBarBounds): void { + if (!bounds.staveGroupBounds) { + bounds.staveGroupBounds = this._currentStaveGroup!; + this._masterBarLookup.set(bounds.index, bounds); + this._currentStaveGroup!.addBar(bounds); + } else { + this._masterBarLookup.set(bounds.index, bounds); + } + } + + /** + * Adds a new beat to the lookup. + * @param bounds The beat bounds to add. + */ + public addBeat(bounds: BeatBounds): void { + this._beatLookup.set(bounds.beat.id, bounds); + } + + /** + * Tries to find the master bar bounds by a given index. + * @param index The index of the master bar to find. + * @returns The master bar bounds if it was rendered, or null if no boundary information is available. + */ + public findMasterBarByIndex(index: number): MasterBarBounds | null { + if (this._masterBarLookup.has(index)) { + return this._masterBarLookup.get(index)!; + } + return null; + } + + /** + * Tries to find the master bar bounds by a given master bar. + * @param bar The master bar to find. + * @returns The master bar bounds if it was rendered, or null if no boundary information is available. + */ + public findMasterBar(bar: MasterBar): MasterBarBounds | null { + let id: number = bar.index; + if (this._masterBarLookup.has(id)) { + return this._masterBarLookup.get(id)!; + } + return null; + } + + /** + * Tries to find the bounds of a given beat. + * @param beat The beat to find. + * @returns The beat bounds if it was rendered, or null if no boundary information is available. + */ + public findBeat(beat: Beat): BeatBounds | null { + let id: number = beat.id; + if (this._beatLookup.has(id)) { + return this._beatLookup.get(id)!; + } + return null; + } + + /** + * Tries to find a beat at the given absolute position. + * @param x The absolute X-position of the beat to find. + * @param y The absolute Y-position of the beat to find. + * @returns The beat found at the given position or null if no beat could be found. + */ + public getBeatAtPos(x: number, y: number): Beat | null { + // + // find a bar which matches in y-axis + let bottom: number = 0; + let top: number = this.staveGroups.length - 1; + let staveGroupIndex: number = -1; + while (bottom <= top) { + let middle: number = ((top + bottom) / 2) | 0; + let group: StaveGroupBounds = this.staveGroups[middle]; + // found? + if (y >= group.realBounds.y && y <= group.realBounds.y + group.realBounds.h) { + staveGroupIndex = middle; + break; + } + // search in lower half + if (y < group.realBounds.y) { + top = middle - 1; + } else { + bottom = middle + 1; + } + } + // no bar found + if (staveGroupIndex === -1) { + return null; + } + // + // Find the matching bar in the row + let staveGroup: StaveGroupBounds = this.staveGroups[staveGroupIndex]; + let bar: MasterBarBounds | null = staveGroup.findBarAtPos(x); + if (bar) { + return bar.findBeatAtPos(x, y); + } + return null; + } + + /** + * Tries to find the note at the given position using the given beat for fast access. + * Use {@link findBeat} to find a beat for a given position first. + * @param beat The beat containing the note. + * @param x The X-position of the note. + * @param y The Y-position of the note. + * @returns The note at the given position within the beat. + */ + public getNoteAtPos(beat: Beat, x: number, y: number): Note | null { + let beatBounds: BeatBounds | null = this.findBeat(beat); + if (!beatBounds) { + return null; + } + return beatBounds.findNoteAtPos(x, y); + } +} diff --git a/src/rendering/utils/IBeamYCalculator.ts b/src/rendering/utils/IBeamYCalculator.ts new file mode 100644 index 000000000..e827a01d6 --- /dev/null +++ b/src/rendering/utils/IBeamYCalculator.ts @@ -0,0 +1,3 @@ +export interface IBeamYCalculator { + getYPositionForNoteValue(noteValue: number): number; +} diff --git a/src/rendering/utils/MasterBarBounds.ts b/src/rendering/utils/MasterBarBounds.ts new file mode 100644 index 000000000..8803960fd --- /dev/null +++ b/src/rendering/utils/MasterBarBounds.ts @@ -0,0 +1,100 @@ +import { Beat } from '@src/model/Beat'; +import { BarBounds } from '@src/rendering/utils/BarBounds'; +import { BeatBounds } from '@src/rendering/utils/BeatBounds'; +import { Bounds } from '@src/rendering/utils/Bounds'; +import { StaveGroupBounds } from '@src/rendering/utils/StaveGroupBounds'; + +/** + * Represents the boundaries of a list of bars related to a single master bar. + */ +export class MasterBarBounds { + /** + * Gets or sets the index of this bounds relative within the parent lookup. + */ + public index: number = 0; + + /** + * Gets or sets a value indicating whether this bounds are the first of the line. + */ + public isFirstOfLine: boolean = false; + + /** + * Gets or sets the bounds covering all visually visible elements spanning all bars of this master bar. + */ + public visualBounds!: Bounds; + + /** + * Gets or sets the actual bounds of the elements in this master bar including whitespace areas. + */ + public realBounds!: Bounds; + + /** + * Gets or sets the actual bounds which are exactly aligned with the lines of the staffs. + */ + public lineAlignedBounds!: Bounds; + + /** + * Gets or sets the list of individual bars within this lookup. + */ + public bars: BarBounds[] = []; + + /** + * Gets or sets a reference to the parent {@link staveGroupBounds}. + */ + public staveGroupBounds!: StaveGroupBounds; + + /** + * Adds a new bar to this lookup. + * @param bounds The bar bounds to add to this lookup. + */ + public addBar(bounds: BarBounds): void { + bounds.masterBarBounds = this; + this.bars.push(bounds); + } + + /** + * Tries to find a beat at the given location. + * @param x The absolute X position where the beat spans across. + * @param y The absolute Y position where the beat spans across. + * @returns The beat that spans across the given point, or null if none of the contained bars had a beat at this position. + */ + public findBeatAtPos(x: number, y: number): Beat | null { + let beat: BeatBounds | null = null; + for (let bar of this.bars) { + let b = bar.findBeatAtPos(x); + if (b && (!beat || beat.realBounds.x < b.realBounds.x)) { + beat = b; + } + } + return !beat ? null : beat.beat; + } + + /** + * Finishes the lookup object and optimizes itself for fast access. + */ + public finish(): void { + this.bars.sort((a, b) => { + if (a.realBounds.y < b.realBounds.y) { + return -1; + } + if (a.realBounds.y > b.realBounds.y) { + return 1; + } + if (a.realBounds.x < b.realBounds.x) { + return -1; + } + if (a.realBounds.x > b.realBounds.x) { + return 1; + } + return 0; + }); + } + + /** + * Adds a new beat to the lookup. + * @param bounds The beat bounds to add. + */ + public addBeat(bounds: BeatBounds): void { + this.staveGroupBounds.boundsLookup.addBeat(bounds); + } +} diff --git a/src/rendering/utils/NoteBounds.ts b/src/rendering/utils/NoteBounds.ts new file mode 100644 index 000000000..8b52d9209 --- /dev/null +++ b/src/rendering/utils/NoteBounds.ts @@ -0,0 +1,25 @@ +import { Note } from '@src/model/Note'; +import { Bounds } from '@src/rendering/utils/Bounds'; +import { BeatBounds } from './BeatBounds'; + +/** + * Represents the bounds of a single note + */ +export class NoteBounds { + + /** + * Gets or sets the reference to the beat boudns this note relates to. + */ + public beatBounds!: BeatBounds; + + + /** + * Gets or sets the bounds of the individual note head. + */ + public noteHeadBounds!: Bounds; + + /** + * Gets or sets the note related to this instance. + */ + public note!: Note; +} diff --git a/src/rendering/utils/PercussionMapper.ts b/src/rendering/utils/PercussionMapper.ts new file mode 100644 index 000000000..cc0b8988e --- /dev/null +++ b/src/rendering/utils/PercussionMapper.ts @@ -0,0 +1,67 @@ +import { Note } from '@src/model/Note'; + +export class PercussionMapper { + private static ElementVariationToMidi: number[][] = [ + [35, 35, 35], + [38, 38, 37], + [56, 56, 56], + [56, 56, 56], + [56, 56, 56], + [41, 41, 41], + [43, 43, 43], + [45, 45, 45], + [47, 47, 47], + [48, 48, 48], + [42, 46, 46], + [44, 44, 44], + [49, 49, 49], + [57, 57, 57], + [55, 55, 55], + [51, 59, 53], + [52, 52, 52] + ]; + + public static midiFromElementVariation(note: Note): number { + return PercussionMapper.ElementVariationToMidi[note.element][note.variation]; + } + + /** + * Maps the given note to a normal note value to place the note at the + * correct line on score notation + * @param value + * @returns + */ + public static mapNoteForDisplay(value: number): number { + if (value === 61 || value === 66 || value === 44) { + return 62; + } + if (value === 60 || value === 65) { + return 64; + } + if (value >= 35 && value <= 36) { + return 65; + } + if (value === 41 || value === 64) { + return 67; + } + if (value === 43 || value === 62) { + return 69; + } + if (value === 45 || value === 63) { + return 71; + } + if (value === 47 || value === 54) { + return 74; + } + if (value === 48 || value === 56) { + return 76; + } + if (value === 50) { + return 77; + } + if (value === 42 || value === 46 || (value >= 49 && value <= 53) || value === 57 || value === 59) { + return 79; + } + return 72; + } +} diff --git a/src/rendering/utils/StaveGroupBounds.ts b/src/rendering/utils/StaveGroupBounds.ts new file mode 100644 index 000000000..663f77763 --- /dev/null +++ b/src/rendering/utils/StaveGroupBounds.ts @@ -0,0 +1,71 @@ +import { Bounds } from '@src/rendering/utils/Bounds'; +import { BoundsLookup } from '@src/rendering/utils/BoundsLookup'; +import { MasterBarBounds } from '@src/rendering/utils/MasterBarBounds'; + +/** + * Represents the bounds of a stave group. + */ +export class StaveGroupBounds { + /** + * Gets or sets the index of the bounds within the parent lookup. + * This allows fast access of the next/previous groups. + */ + public index: number = 0; + + /** + * Gets or sets the bounds covering all visually visible elements of this stave group. + */ + public visualBounds!: Bounds; + + /** + * Gets or sets the actual bounds of the elements in this stave group including whitespace areas. + */ + public realBounds!: Bounds; + + /** + * Gets or sets the list of master bar bounds related to this stave group. + */ + public bars: MasterBarBounds[] = []; + + /** + * Gets or sets a reference to the parent bounds lookup. + */ + public boundsLookup!: BoundsLookup; + + /** + * Finished the lookup for optimized access. + */ + public finish(): void { + for (let t of this.bars) { + t.finish(); + } + } + + /** + * Adds a new master bar to this lookup. + * @param bounds The master bar bounds to add. + */ + public addBar(bounds: MasterBarBounds): void { + this.boundsLookup.addMasterBar(bounds); + bounds.staveGroupBounds = this; + this.bars.push(bounds); + } + + /** + * Tries to find the master bar bounds that are located at the given X-position. + * @param x The X-position to find a master bar. + * @returns The master bounds at the given X-position. + */ + public findBarAtPos(x: number): MasterBarBounds | null { + let b: MasterBarBounds | null = null; + // move from left to right as long we find bars that start before the clicked position + for (let bar of this.bars) { + if (!b || bar.realBounds.x < x) { + b = bar; + } else if (x > bar.realBounds.x + bar.realBounds.w) { + break; + } + } + return b; + } +} diff --git a/src/synth/AlphaSynth.ts b/src/synth/AlphaSynth.ts new file mode 100644 index 000000000..47deba32f --- /dev/null +++ b/src/synth/AlphaSynth.ts @@ -0,0 +1,325 @@ +import { MidiFile } from '@src/midi/MidiFile'; +import { IAlphaSynth } from '@src/synth/IAlphaSynth'; +import { ISynthOutput } from '@src/synth/ISynthOutput'; +import { MidiFileSequencer } from '@src/synth/MidiFileSequencer'; +import { PlaybackRange } from '@src/synth/PlaybackRange'; +import { PlayerState } from '@src/synth/PlayerState'; +import { PlayerStateChangedEventArgs } from '@src/synth/PlayerStateChangedEventArgs'; +import { PositionChangedEventArgs } from '@src/synth/PositionChangedEventArgs'; +import { Hydra } from '@src/synth/soundfont/Hydra'; +import { TinySoundFont } from '@src/synth/synthesis/TinySoundFont'; +import { SynthHelper } from '@src/synth/SynthHelper'; +import { EventEmitter, IEventEmitter, IEventEmitterOfT, EventEmitterOfT } from '@src/EventEmitter'; +import { ByteBuffer } from '@src/io/ByteBuffer'; +import { Logger } from '@src/Logger'; +import { LogLevel } from '@src/LogLevel'; +import { SynthConstants } from '@src/synth/SynthConstants'; + +/** + * This is the main synthesizer component which can be used to + * play a {@link MidiFile} via a {@link ISynthOutput}. + */ +export class AlphaSynth implements IAlphaSynth { + private _sequencer: MidiFileSequencer; + private _synthesizer: TinySoundFont; + private _isSoundFontLoaded: boolean = false; + private _isMidiLoaded: boolean = false; + private _tickPosition: number = 0; + private _timePosition: number = 0; + private _metronomeVolume: number = 0; + + /** + * Gets the {@link ISynthOutput} used for playing the generated samples. + */ + public readonly output: ISynthOutput; + + public isReady: boolean = false; + + public get isReadyForPlayback(): boolean { + return this.isReady && this._isSoundFontLoaded && this._isMidiLoaded; + } + + public state: PlayerState = PlayerState.Paused; + + public get logLevel(): LogLevel { + return Logger.logLevel; + } + + public set logLevel(value: LogLevel) { + Logger.logLevel = value; + } + + public get masterVolume(): number { + return this._synthesizer.globalGainDb; + } + + public set masterVolume(value: number) { + value = SynthHelper.clamp(value, SynthConstants.MinVolume, SynthConstants.MaxVolume); + this._synthesizer.globalGainDb = value; + } + + public get metronomeVolume(): number { + return this._metronomeVolume; + } + + public set metronomeVolume(value: number) { + value = SynthHelper.clamp(value, SynthConstants.MinVolume, SynthConstants.MaxVolume); + this._metronomeVolume = value; + this._synthesizer.metronomeVolume = value; + } + + public get playbackSpeed(): number { + return this._sequencer.playbackSpeed; + } + + public set playbackSpeed(value: number) { + value = SynthHelper.clamp(value, SynthConstants.MinPlaybackSpeed, SynthConstants.MaxPlaybackSpeed); + let oldSpeed: number = this._sequencer.playbackSpeed; + this._sequencer.playbackSpeed = value; + this.updateTimePosition(this._timePosition * (oldSpeed / value)); + } + + public get tickPosition(): number { + return this._tickPosition; + } + + public set tickPosition(value: number) { + this.timePosition = this._sequencer.tickPositionToTimePosition(value); + } + + public get timePosition(): number { + return this._timePosition; + } + + public set timePosition(value: number) { + Logger.debug('AlphaSynth', `Seeking to position ${value}ms`); + + // tell the sequencer to jump to the given position + this._sequencer.seek(value); + + // update the internal position + this.updateTimePosition(value); + + // tell the output to reset the already synthesized buffers and request data again + this.output.resetSamples(); + } + + public get playbackRange(): PlaybackRange | null { + return this._sequencer.playbackRange; + } + + public set playbackRange(value: PlaybackRange | null) { + this._sequencer.playbackRange = value; + if (value) { + this.tickPosition = value.startTick; + } + } + + public get isLooping(): boolean { + return this._sequencer.isLooping; + } + + public set isLooping(value: boolean) { + this._sequencer.isLooping = value; + } + + public destroy(): void { + Logger.debug('AlphaSynth', 'Destroying player'); + this.stop(); + } + + /** + * Initializes a new instance of the {@link AlphaSynth} class. + * @param output The output to use for playing the generated samples. + */ + public constructor(output: ISynthOutput) { + Logger.debug('AlphaSynth', 'Initializing player'); + this.state = PlayerState.Paused; + + Logger.debug('AlphaSynth', 'Creating output'); + this.output = output; + this.output.ready.on(() => { + this.isReady = true; + (this.ready as EventEmitter).trigger(); + this.checkReadyForPlayback(); + }); + this.output.finished.on(() => { + // stop everything + this.stop(); + Logger.debug('AlphaSynth', 'Finished playback'); + (this.finished as EventEmitter).trigger(); + if (this._sequencer.isLooping) { + this.play(); + } + }); + this.output.sampleRequest.on(() => { + // synthesize buffer + this._sequencer.fillMidiEventQueue(); + let samples: Float32Array = this._synthesizer.synthesize(); + // send it to output + this.output.addSamples(samples); + // tell sequencer to check whether its work is done + this._sequencer.checkForStop(); + }); + this.output.samplesPlayed.on(this.onSamplesPlayed.bind(this)); + + Logger.debug('AlphaSynth', 'Creating synthesizer'); + this._synthesizer = new TinySoundFont(this.output.sampleRate); + this._sequencer = new MidiFileSequencer(this._synthesizer); + this._sequencer.finished.on(this.output.sequencerFinished.bind(this.output)); + + Logger.debug('AlphaSynth', 'Opening output'); + this.output.open(); + } + + public play(): boolean { + if (this.state === PlayerState.Playing || !this.isReadyForPlayback) { + return false; + } + this.output.activate(); + this._synthesizer.setupMetronomeChannel(this.metronomeVolume); + Logger.debug('AlphaSynth', 'Starting playback'); + this.state = PlayerState.Playing; + (this.stateChanged as EventEmitterOfT).trigger( + new PlayerStateChangedEventArgs(this.state, false) + ); + this.output.play(); + return true; + } + + public pause(): void { + if (this.state === PlayerState.Paused || !this.isReadyForPlayback) { + return; + } + Logger.debug('AlphaSynth', 'Pausing playback'); + this.state = PlayerState.Paused; + (this.stateChanged as EventEmitterOfT).trigger( + new PlayerStateChangedEventArgs(this.state, false) + ); + this.output.pause(); + this._synthesizer.noteOffAll(false); + } + + public playPause(): void { + if (this.state === PlayerState.Playing || !this.isReadyForPlayback) { + this.pause(); + } else { + this.play(); + } + } + + public stop(): void { + if (!this.isReadyForPlayback) { + return; + } + Logger.debug('AlphaSynth', 'Stopping playback'); + this.state = PlayerState.Paused; + this.output.pause(); + this._sequencer.stop(); + this._synthesizer.noteOffAll(true); + this.tickPosition = this._sequencer.playbackRange ? this._sequencer.playbackRange.startTick : 0; + (this.stateChanged as EventEmitterOfT).trigger( + new PlayerStateChangedEventArgs(this.state, true) + ); + } + + public loadSoundFont(data: Uint8Array): void { + this.pause(); + + let input: ByteBuffer = ByteBuffer.fromBuffer(data); + try { + Logger.debug('AlphaSynth', 'Loading soundfont from bytes'); + let soundFont: Hydra = new Hydra(); + soundFont.load(input); + this._synthesizer.loadPresets(soundFont); + this._isSoundFontLoaded = true; + (this.soundFontLoaded as EventEmitter).trigger(); + + Logger.debug('AlphaSynth', 'soundFont successfully loaded'); + this.checkReadyForPlayback(); + } catch (e) { + Logger.error('AlphaSynth', 'Could not load soundfont from bytes ' + e); + (this.soundFontLoadFailed as EventEmitterOfT).trigger(e); + } + } + + private checkReadyForPlayback(): void { + if (this.isReadyForPlayback) { + this._synthesizer.setupMetronomeChannel(this.metronomeVolume); + (this.readyForPlayback as EventEmitter).trigger(); + } + } + + /** + * Loads the given midi file for playback. + * @param midiFile The midi file to load + */ + public loadMidiFile(midiFile: MidiFile): void { + this.stop(); + + try { + Logger.debug('AlphaSynth', 'Loading midi from model'); + this._sequencer.loadMidi(midiFile); + this._isMidiLoaded = true; + (this.midiLoaded as EventEmitter).trigger(); + + Logger.debug('AlphaSynth', 'Midi successfully loaded'); + this.checkReadyForPlayback(); + this.tickPosition = 0; + } catch (e) { + Logger.error('AlphaSynth', 'Could not load midi from model ' + e); + (this.midiLoadFailed as EventEmitterOfT).trigger(e); + } + } + + public setChannelMute(channel: number, mute: boolean): void { + this._synthesizer.channelSetMute(channel, mute); + } + + public resetChannelStates(): void { + this._synthesizer.resetChannelStates(); + } + + public setChannelSolo(channel: number, solo: boolean): void { + this._synthesizer.channelSetSolo(channel, solo); + } + + public setChannelVolume(channel: number, volume: number): void { + volume = SynthHelper.clamp(volume, SynthConstants.MinVolume, SynthConstants.MaxVolume); + this._synthesizer.channelSetMixVolume(channel, volume); + } + + private onSamplesPlayed(sampleCount: number): void { + let playedMillis: number = (sampleCount / this._synthesizer.outSampleRate) * 1000; + this.updateTimePosition(this._timePosition + playedMillis); + } + + private updateTimePosition(timePosition: number): void { + // update the real positions + const currentTime: number = (this._timePosition = timePosition); + const currentTick: number = (this._tickPosition = this._sequencer.timePositionToTickPosition(currentTime)); + const endTime: number = this._sequencer.endTime; + const endTick: number = this._sequencer.endTick; + Logger.debug( + 'AlphaSynth', + `Position changed: (time: ${currentTime}/${endTime}, tick: ${currentTick}/${endTime}, Active Voices: ${this._synthesizer.activeVoiceCount}` + ); + (this.positionChanged as EventEmitterOfT).trigger( + new PositionChangedEventArgs(currentTime, endTime, currentTick, endTick) + ); + } + + readonly ready: IEventEmitter = new EventEmitter(); + readonly readyForPlayback: IEventEmitter = new EventEmitter(); + readonly finished: IEventEmitter = new EventEmitter(); + readonly soundFontLoaded: IEventEmitter = new EventEmitter(); + readonly soundFontLoadFailed: IEventEmitterOfT = new EventEmitterOfT(); + readonly midiLoaded: IEventEmitter = new EventEmitter(); + readonly midiLoadFailed: IEventEmitterOfT = new EventEmitterOfT(); + readonly stateChanged: IEventEmitterOfT = new EventEmitterOfT< + PlayerStateChangedEventArgs + >(); + readonly positionChanged: IEventEmitterOfT = new EventEmitterOfT< + PositionChangedEventArgs + >(); +} diff --git a/src/synth/IAlphaSynth.ts b/src/synth/IAlphaSynth.ts new file mode 100644 index 000000000..c243a9635 --- /dev/null +++ b/src/synth/IAlphaSynth.ts @@ -0,0 +1,177 @@ +import { MidiFile } from '@src/midi/MidiFile'; +import { PlaybackRange } from '@src/synth/PlaybackRange'; +import { PlayerState } from '@src/synth/PlayerState'; +import { PlayerStateChangedEventArgs } from '@src/synth/PlayerStateChangedEventArgs'; +import { PositionChangedEventArgs } from '@src/synth/PositionChangedEventArgs'; +import { IEventEmitter, IEventEmitterOfT } from '@src/EventEmitter'; +import { LogLevel } from '@src/LogLevel'; + +/** + * The public API interface for interacting with the synthesizer. + */ +export interface IAlphaSynth { + /** + * Gets or sets whether the synthesizer is ready for interaction. (output and worker are initialized) + */ + readonly isReady: boolean; + + /** + * Gets or sets whether the synthesizer is ready for playback. (output, worker are initialized, soundfont and midi are loaded) + */ + readonly isReadyForPlayback: boolean; + + /** + * Gets the current player state. + */ + readonly state: PlayerState; + + /** + * Gets or sets the loging level. + */ + logLevel: LogLevel; + + /** + * Gets or sets the current master volume as percentage. (range: 0.0-3.0, default 1.0) + */ + masterVolume: number; + + /** + * Gets or sets the metronome volume. (range: 0.0-3.0, default 0.0) + */ + metronomeVolume: number; + + /** + * Gets or sets the current playback speed as percentage. (range: 0.125-8.0, default: 1.0) + */ + playbackSpeed: number; + + /** + * Gets or sets the position within the song in midi ticks. + */ + tickPosition: number; + + /** + * Gets or sets the position within the song in milliseconds. + */ + timePosition: number; + + /** + * Gets or sets the range of the song that should be played. Set this to null + * to play the whole song. + */ + playbackRange: PlaybackRange | null; + + /** + * Gets or sets whether the playback should automatically restart after it finished. + */ + isLooping: boolean; + + /** + * Destroys the synthesizer and all related components + */ + destroy(): void; + + /** + * Starts the playback if possible + * @returns true if the playback was started, otherwise false. Reasons for not starting can be that the player is not ready or already playing. + */ + play(): boolean; + + /** + * Pauses the playback if was running + */ + pause(): void; + + /** + * Starts the playback if possible, pauses the playback if was running + */ + playPause(): void; + + /** + * Stopps the playback + */ + stop(): void; + + /** + * Loads a soundfont from the given data + * @param data a byte array to load the data from + */ + loadSoundFont(data: Uint8Array): void; + + /** + * Loads the given midi file structure. + * @param midi + */ + loadMidiFile(midi: MidiFile): void; + + /** + * Sets the mute state of a channel. + * @param channel The channel number + * @param mute true if the channel should be muted, otherwise false. + */ + setChannelMute(channel: number, mute: boolean): void; + + /** + * Resets the mute/solo state of all channels + */ + resetChannelStates(): void; + + /** + * Gets the solo state of a channel. + * @param channel The channel number + * @param solo true if the channel should be played solo, otherwise false. + */ + setChannelSolo(channel: number, solo: boolean): void; + + /** + * Gets or sets the current and initial volume of the given channel. + * @param channel The channel number. + * @param volume The volume of of the channel (0.0-1.0) + */ + setChannelVolume(channel: number, volume: number): void; + + /** + * This event is fired when the player is ready to be interacted with. + */ + readonly ready: IEventEmitter; + + /** + * This event is fired when all required data for playback is loaded and ready. + */ + readonly readyForPlayback: IEventEmitter; + + /** + * This event is fired when the playback of the whole song finished. + */ + readonly finished: IEventEmitter; + + /** + * This event is fired when the SoundFont needed for playback was loaded. + */ + readonly soundFontLoaded: IEventEmitter; + + /** + * This event is fired when the loading of the SoundFont failed. + */ + readonly soundFontLoadFailed: IEventEmitterOfT; + + /** + * This event is fired when the Midi file needed for playback was loaded. + */ + readonly midiLoaded: IEventEmitter; + + /** + * This event is fired when the loading of the Midi file failed. + */ + readonly midiLoadFailed: IEventEmitterOfT + + /** + * This event is fired when the playback state changed. + */ + readonly stateChanged: IEventEmitterOfT; + + /** + * This event is fired when the current playback position of/ the song changed. + */ + readonly positionChanged: IEventEmitterOfT; +} diff --git a/src/synth/ISynthOutput.ts b/src/synth/ISynthOutput.ts new file mode 100644 index 000000000..d0870370c --- /dev/null +++ b/src/synth/ISynthOutput.ts @@ -0,0 +1,70 @@ +import { IEventEmitter, IEventEmitterOfT } from '@src/EventEmitter'; + +/** + * This is the base interface for output devices which can + * request and playback audio samples. + * @csharp_public + */ +export interface ISynthOutput { + /** + * Gets the sample rate required by the output. + */ + readonly sampleRate: number; + + /** + * Called when the output should be opened. + */ + open(): void; + + /** + * Called when the sequencer finished the playback. + * This tells the output not to request any samples anymore after the existing buffers are finished. + */ + sequencerFinished(): void; + + /** + * Called when the output should start the playback. + */ + play(): void; + + /** + * Called when the output should stop the playback. + */ + pause(): void; + + /** + * Called when samples have been synthesized and should be added to the playback buffer. + * @param samples + */ + addSamples(samples: Float32Array): void; + + /** + * Called when the samples in the output buffer should be reset. This is neeed for instance when seeking to another position. + */ + resetSamples(): void; + + /** + * Activates the output component. + */ + activate(): void; + + /** + * Fired when the output has been successfully opened and is ready to play samples. + */ + readonly ready: IEventEmitter; + + /** + * Fired when a certain number of samples have been played. + */ + readonly samplesPlayed: IEventEmitterOfT; + + /** + * Fired when the output needs more samples to be played. + */ + readonly sampleRequest: IEventEmitter; + + /** + * Fired when the last samples after calling SequencerFinished have been played. + */ + readonly finished: IEventEmitter; +} diff --git a/src/synth/MidiFileSequencer.ts b/src/synth/MidiFileSequencer.ts new file mode 100644 index 000000000..70a3fd668 --- /dev/null +++ b/src/synth/MidiFileSequencer.ts @@ -0,0 +1,309 @@ +import { MetaDataEvent } from '@src/midi/MetaDataEvent'; +import { MetaEventType } from '@src/midi/MetaEvent'; +import { MetaNumberEvent } from '@src/midi/MetaNumberEvent'; +import { MidiEventType } from '@src/midi/MidiEvent'; +import { MidiFile } from '@src/midi/MidiFile'; +import { PlaybackRange } from '@src/synth/PlaybackRange'; +import { SynthEvent } from '@src/synth/synthesis/SynthEvent'; +import { TinySoundFont } from '@src/synth/synthesis/TinySoundFont'; +import { EventEmitter, IEventEmitter } from '@src/EventEmitter'; +import { Logger } from '@src/Logger'; + +export class MidiFileSequencerTempoChange { + public bpm: number; + public ticks: number; + public time: number; + + public constructor(bpm: number, ticks: number, time: number) { + this.bpm = bpm; + this.ticks = ticks; + this.time = time; + } +} + +/** + * This sequencer dispatches midi events to the synthesizer based on the current + * synthesize position. The sequencer does not consider the playback speed. + */ +export class MidiFileSequencer { + private _synthesizer: TinySoundFont; + private _tempoChanges: MidiFileSequencerTempoChange[] = []; + private _firstProgramEventPerChannel: Map = new Map(); + private _synthData: SynthEvent[] = []; + private _division: number = 0; + private _eventIndex: number = 0; + private _currentTime: number = 0; + private _playbackRange: PlaybackRange | null = null; + private _playbackRangeStartTime: number = 0; + private _playbackRangeEndTime: number = 0; + private _endTime: number = 0; + + public constructor(synthesizer: TinySoundFont) { + this._synthesizer = synthesizer; + } + + public get playbackRange(): PlaybackRange | null { + return this._playbackRange; + } + + public set playbackRange(value: PlaybackRange | null) { + this._playbackRange = value; + if (value) { + this._playbackRangeStartTime = this.tickPositionToTimePositionWithSpeed(value.startTick, 1); + this._playbackRangeEndTime = this.tickPositionToTimePositionWithSpeed(value.endTick, 1); + } + } + + public isLooping: boolean = false; + + /** + * Gets the duration of the song in ticks. + */ + public endTick: number = 0; + + public get endTime(): number { + return this._endTime / this.playbackSpeed; + } + + /** + * Gets or sets the playback speed. + */ + public playbackSpeed: number = 1; + + public seek(timePosition: number): void { + // map to speed=1 + timePosition *= this.playbackSpeed; + + // ensure playback range + if (this.playbackRange) { + if (timePosition < this._playbackRangeStartTime) { + timePosition = this._playbackRangeStartTime; + } else if (timePosition > this._playbackRangeEndTime) { + timePosition = this._playbackRangeEndTime; + } + } + + // move back some ticks to ensure the on-time events are played + timePosition -= 25; + if (timePosition < 0) { + timePosition = 0; + } + + if (timePosition > this._currentTime) { + this.silentProcess(timePosition - this._currentTime); + } else if (timePosition < this._currentTime) { + // we have to restart the midi to make sure we get the right state: instruments, volume, pan, etc + this._currentTime = 0; + this._eventIndex = 0; + let metronomeVolume: number = this._synthesizer.metronomeVolume; + this._synthesizer.noteOffAll(true); + this._synthesizer.resetSoft(); + this._synthesizer.setupMetronomeChannel(metronomeVolume); + this.silentProcess(timePosition); + } + } + + private silentProcess(milliseconds: number): void { + if (milliseconds <= 0) { + return; + } + + let start: number = Date.now(); + let finalTime: number = this._currentTime + milliseconds; + + while (this._currentTime < finalTime) { + if (this.fillMidiEventQueueLimited(finalTime - this._currentTime)) { + this._synthesizer.synthesizeSilent(); + } + } + + let duration: number = Date.now() - start; + Logger.debug('Sequencer', 'Silent seek finished in ' + duration + 'ms'); + } + + public loadMidi(midiFile: MidiFile): void { + this._tempoChanges = []; + + this._division = midiFile.division; + this._eventIndex = 0; + this._currentTime = 0; + + // build synth events. + this._synthData = []; + + // Converts midi to milliseconds for easy sequencing + let bpm: number = 120; + let absTick: number = 0; + let absTime: number = 0.0; + + let metronomeLength: number = 0; + let metronomeTick: number = 0; + let metronomeTime: number = 0.0; + + let previousTick: number = 0; + for (let mEvent of midiFile.events) { + let synthData: SynthEvent = new SynthEvent(this._synthData.length, mEvent); + this._synthData.push(synthData); + + let deltaTick: number = mEvent.tick - previousTick; + absTick += deltaTick; + absTime += deltaTick * (60000.0 / (bpm * midiFile.division)); + synthData.time = absTime; + previousTick = mEvent.tick; + + if (mEvent.command === MidiEventType.Meta && mEvent.data1 === MetaEventType.Tempo) { + let meta: MetaNumberEvent = mEvent as MetaNumberEvent; + bpm = 60000000 / meta.value; + this._tempoChanges.push(new MidiFileSequencerTempoChange(bpm, absTick, absTime)); + } else if (mEvent.command === MidiEventType.Meta && mEvent.data1 === MetaEventType.TimeSignature) { + let meta: MetaDataEvent = mEvent as MetaDataEvent; + let timeSignatureDenominator: number = Math.pow(2, meta.data[1]); + metronomeLength = (this._division * (4.0 / timeSignatureDenominator)) | 0; + } else if (mEvent.command === MidiEventType.ProgramChange) { + let channel: number = mEvent.channel; + if (!this._firstProgramEventPerChannel.has(channel)) { + this._firstProgramEventPerChannel.set(channel, synthData); + } + } + + if (metronomeLength > 0) { + while (metronomeTick < absTick) { + let metronome: SynthEvent = SynthEvent.newMetronomeEvent(this._synthData.length); + this._synthData.push(metronome); + metronome.time = metronomeTime; + metronomeTick += metronomeLength; + metronomeTime += metronomeLength * (60000.0 / (bpm * midiFile.division)); + } + } + } + + this._synthData.sort((a, b) => { + if (a.time > b.time) { + return 1; + } + if (a.time < b.time) { + return -1; + } + return a.eventIndex - b.eventIndex; + }); + this._endTime = absTime; + this.endTick = absTick; + } + + public fillMidiEventQueue(): boolean { + return this.fillMidiEventQueueLimited(-1); + } + + private fillMidiEventQueueLimited(maxMilliseconds: number): boolean { + let millisecondsPerBuffer: number = + (TinySoundFont.MicroBufferSize / this._synthesizer.outSampleRate) * 1000 * this.playbackSpeed; + let endTime: number = this.internalEndTime; + if (maxMilliseconds > 0) { + // ensure that first microbuffer does not already exceed max time + if( maxMilliseconds < millisecondsPerBuffer) { + millisecondsPerBuffer = maxMilliseconds; + } + endTime = Math.min(this.internalEndTime, this._currentTime + maxMilliseconds); + } + + let anyEventsDispatched: boolean = false; + for (let i: number = 0; i < TinySoundFont.MicroBufferCount; i++) { + this._currentTime += millisecondsPerBuffer; + while ( + this._eventIndex < this._synthData.length && + this._synthData[this._eventIndex].time < this._currentTime && + this._currentTime < endTime + ) { + this._synthesizer.dispatchEvent(i, this._synthData[this._eventIndex]); + this._eventIndex++; + anyEventsDispatched = true; + } + if(this._currentTime >= endTime) { + break; + } + } + + return anyEventsDispatched; + } + + public tickPositionToTimePosition(tickPosition: number): number { + return this.tickPositionToTimePositionWithSpeed(tickPosition, this.playbackSpeed); + } + + public timePositionToTickPosition(timePosition: number): number { + return this.timePositionToTickPositionWithSpeed(timePosition, this.playbackSpeed); + } + + private tickPositionToTimePositionWithSpeed(tickPosition: number, playbackSpeed: number): number { + let timePosition: number = 0.0; + let bpm: number = 120.0; + let lastChange: number = 0; + + // find start and bpm of last tempo change before time + for (const c of this._tempoChanges) { + if (tickPosition < c.ticks) { + break; + } + + timePosition = c.time; + bpm = c.bpm; + lastChange = c.ticks; + } + + // add the missing millis + tickPosition -= lastChange; + timePosition += tickPosition * (60000.0 / (bpm * this._division)); + + return timePosition / playbackSpeed; + } + + private timePositionToTickPositionWithSpeed(timePosition: number, playbackSpeed: number): number { + timePosition *= playbackSpeed; + + let ticks: number = 0; + let bpm: number = 120.0; + let lastChange: number = 0; + + // find start and bpm of last tempo change before time + for (const c of this._tempoChanges) { + if (timePosition < c.time) { + break; + } + ticks = c.ticks; + bpm = c.bpm; + lastChange = c.time; + } + + // add the missing ticks + timePosition -= lastChange; + ticks += (timePosition / (60000.0 / (bpm * this._division))) | 0; + // we add 1 for possible rounding errors.(floating point issuses) + return ticks + 1; + } + + public finished: IEventEmitter = new EventEmitter(); + + private get internalEndTime(): number { + return !this.playbackRange ? this._endTime : this._playbackRangeEndTime; + } + + public checkForStop(): void { + if (this._currentTime >= this.internalEndTime) { + let metronomeVolume: number = this._synthesizer.metronomeVolume; + this._synthesizer.noteOffAll(true); + this._synthesizer.resetSoft(); + this._synthesizer.setupMetronomeChannel(metronomeVolume); + (this.finished as EventEmitter).trigger(); + } + } + + public stop(): void { + if (!this.playbackRange) { + this._currentTime = 0; + this._eventIndex = 0; + } else if (this.playbackRange) { + this._currentTime = this.playbackRange.startTick; + this._eventIndex = 0; + } + } +} diff --git a/src/synth/PlaybackRange.ts b/src/synth/PlaybackRange.ts new file mode 100644 index 000000000..6c838a392 --- /dev/null +++ b/src/synth/PlaybackRange.ts @@ -0,0 +1,14 @@ +/** + * Represents a range of the song that should be played. + */ +export class PlaybackRange { + /** + * The position in midi ticks from where the song should start. + */ + public startTick: number = 0; + + /** + * The position in midi ticks to where the song should be played. + */ + public endTick: number = 0; +} diff --git a/src/synth/PlayerState.ts b/src/synth/PlayerState.ts new file mode 100644 index 000000000..002c98630 --- /dev/null +++ b/src/synth/PlayerState.ts @@ -0,0 +1,13 @@ +/** + * Lists the different states of the player + */ +export enum PlayerState { + /** + * Player is paused + */ + Paused, + /** + * Player is playing + */ + Playing +} diff --git a/src/synth/PlayerStateChangedEventArgs.ts b/src/synth/PlayerStateChangedEventArgs.ts new file mode 100644 index 000000000..23c000287 --- /dev/null +++ b/src/synth/PlayerStateChangedEventArgs.ts @@ -0,0 +1,26 @@ +import { PlayerState } from '@src/synth/PlayerState'; + +/** + * Represents the info when the player state changes. + */ +export class PlayerStateChangedEventArgs { + /** + * The new state of the player. + */ + public readonly state: PlayerState; + + /** + * Gets a value indicating whether the playback was stopped or only paused. + * @returns true if the playback was stopped, false if the playback was started or paused + */ + public readonly stopped: boolean; + + /** + * Initializes a new instance of the {@link PlayerStateChangedEventArgs} class. + * @param state The state. + */ + public constructor(state: PlayerState, stopped: boolean) { + this.state = state; + this.stopped = stopped; + } +} diff --git a/src/synth/PositionChangedEventArgs.ts b/src/synth/PositionChangedEventArgs.ts new file mode 100644 index 000000000..14e9dff30 --- /dev/null +++ b/src/synth/PositionChangedEventArgs.ts @@ -0,0 +1,38 @@ +/** + * Represents the info when the time in the synthesizer changes. + */ +export class PositionChangedEventArgs { + /** + * Gets the current time in milliseconds. + */ + public readonly currentTime: number; + + /** + * Gets the length of the played song in milliseconds. + */ + public readonly endTime: number; + + /** + * Gets the current time in midi ticks. + */ + public readonly currentTick: number; + + /** + * Gets the length of the played song in midi ticks. + */ + public readonly endTick: number; + + /** + * Initializes a new instance of the {@link PositionChangedEventArgs} class. + * @param currentTime The current time. + * @param endTime The end time. + * @param currentTick The current tick. + * @param endTick The end tick. + */ + public constructor(currentTime: number, endTime: number, currentTick: number, endTick: number) { + this.currentTime = currentTime; + this.endTime = endTime; + this.currentTick = currentTick; + this.endTick = endTick; + } +} diff --git a/src/synth/SynthConstants.ts b/src/synth/SynthConstants.ts new file mode 100644 index 000000000..69f07cdaa --- /dev/null +++ b/src/synth/SynthConstants.ts @@ -0,0 +1,24 @@ +// The SoundFont loading and Audio Synthesis is based on TinySoundFont, licensed under MIT, +// developed by Bernhard Schelling (https://github.com/schellingb/TinySoundFont) +// TypeScript port for alphaTab: (C) 2020 by Daniel Kuschny +// Licensed under: MPL-2.0 +export class SynthConstants { + public static readonly DefaultChannelCount: number = 16 + 1; + public static readonly MetronomeChannel: number = SynthConstants.DefaultChannelCount - 1; + public static readonly AudioChannels: number = 2; + public static readonly MinVolume: number = 0; + public static readonly MaxVolume: number = 1; + public static readonly MinProgram: number = 0; + public static readonly MaxProgram: number = 127; + public static readonly MinPlaybackSpeed: number = 0.125; + public static readonly MaxPlaybackSpeed: number = 8; + + /** + * The Midi Pitch bend message is a 15-bit value + */ + public static readonly MaxPitchWheel: number = 16384; + /** + * The pitch wheel value for no pitch change at all. + */ + public static readonly DefaultPitchWheel: number = SynthConstants.MaxPitchWheel / 2; +} diff --git a/src/synth/SynthHelper.ts b/src/synth/SynthHelper.ts new file mode 100644 index 000000000..3711efa92 --- /dev/null +++ b/src/synth/SynthHelper.ts @@ -0,0 +1,32 @@ +// The SoundFont loading and Audio Synthesis is based on TinySoundFont, licensed under MIT, +// developed by Bernhard Schelling (https://github.com/schellingb/TinySoundFont) +// TypeScript port for alphaTab: (C) 2020 by Daniel Kuschny +// Licensed under: MPL-2.0 + +export class SynthHelper { + public static timecents2Secs(timecents: number): number { + return Math.pow(2, timecents / 1200.0); + } + + public static decibelsToGain(db: number): number { + return db > -100 ? Math.pow(10.0, db * 0.05) : 0; + } + + public static gainToDecibels(gain: number): number { + return gain <= 0.00001 ? -100 : 20.0 * Math.log10(gain); + } + + public static cents2Hertz(cents: number): number { + return 8.176 * Math.pow(2.0, cents / 1200.0); + } + + public static clamp(value: number, min: number, max: number): number { + if (value <= min) { + return min; + } + if (value >= max) { + return max; + } + return value; + } +} diff --git a/src/synth/ds/CircularSampleBuffer.ts b/src/synth/ds/CircularSampleBuffer.ts new file mode 100644 index 000000000..765237bbe --- /dev/null +++ b/src/synth/ds/CircularSampleBuffer.ts @@ -0,0 +1,94 @@ +/** + * Represents a fixed size circular sample buffer that can be written to and read from. + * @csharp_public + */ +export class CircularSampleBuffer { + private _buffer: Float32Array; + private _writePosition: number = 0; + private _readPosition: number = 0; + + /** + * Gets the number of samples written to the buffer. + */ + public count: number = 0; + + /** + * Initializes a new instance of the {@link CircularSampleBuffer} class. + * @param size The size. + */ + public constructor(size: number) { + this._buffer = new Float32Array(size); + } + + /** + * Clears all samples written to this buffer. + */ + public clear(): void { + this._readPosition = 0; + this._writePosition = 0; + this.count = 0; + this._buffer = new Float32Array(this._buffer.length); + } + + /** + * Writes the given samples to this buffer. + * @param data The sample array to read from. + * @param offset + * @param count + * @returns + */ + public write(data: Float32Array, offset: number, count: number): number { + let samplesWritten: number = 0; + if (count > this._buffer.length - this.count) { + count = this._buffer.length - this.count; + } + + const writeToEnd: number = Math.min(this._buffer.length - this._writePosition, count); + this._buffer.set(data.subarray(offset, offset + writeToEnd), this._writePosition); + this._writePosition += writeToEnd; + this._writePosition %= this._buffer.length; + samplesWritten += writeToEnd; + if (samplesWritten < count) { + this._buffer.set( + data.subarray(offset + samplesWritten, offset + samplesWritten + count - samplesWritten), + this._writePosition + ); + this._writePosition += count - samplesWritten; + samplesWritten = count; + } + this.count += samplesWritten; + return samplesWritten; + } + + /** + * Reads the requested amount of samples from the buffer. + * @param data The sample array to store the read elements. + * @param offset The offset within the destination buffer to put the items at. + * @param count The number of items to read from this buffer. + * @returns The number of items actually read from the buffer. + */ + public read(data: Float32Array, offset: number, count: number): number { + if (count > this.count) { + count = this.count; + } + + let samplesRead: number = 0; + const readToEnd: number = Math.min(this._buffer.length - this._readPosition, count); + data.set(this._buffer.subarray(this._readPosition, this._readPosition + readToEnd), offset); + samplesRead += readToEnd; + this._readPosition += readToEnd; + this._readPosition %= this._buffer.length; + + if (samplesRead < count) { + data.set( + this._buffer.subarray(this._readPosition, this._readPosition + count - samplesRead), + offset + samplesRead + ); + this._readPosition += count - samplesRead; + samplesRead = count; + } + + this.count -= samplesRead; + return samplesRead; + } +} diff --git a/src/synth/soundfont/Hydra.ts b/src/synth/soundfont/Hydra.ts new file mode 100644 index 000000000..4ac689686 --- /dev/null +++ b/src/synth/soundfont/Hydra.ts @@ -0,0 +1,332 @@ +// The SoundFont loading and Audio Synthesis is based on TinySoundFont, licensed under MIT, +// developed by Bernhard Schelling (https://github.com/schellingb/TinySoundFont) +// TypeScript port for alphaTab: (C) 2020 by Daniel Kuschny +// Licensed under: MPL-2.0 + +import { RiffChunk } from '@src/synth/soundfont/RiffChunk'; + +import { IOHelper } from '@src/io/IOHelper'; +import { IReadable } from '@src/io/IReadable'; +import { TypeConversions } from '@src/io/TypeConversions'; + +import { FormatError } from '@src/FormatError'; + +export class Hydra { + public phdrs: HydraPhdr[] = []; + public pbags: HydraPbag[] = []; + public pmods: HydraPmod[] = []; + public pgens: HydraPgen[] = []; + public insts: HydraInst[] = []; + public ibags: HydraIbag[] = []; + public imods: HydraImod[] = []; + public igens: HydraIgen[] = []; + public sHdrs: HydraShdr[] = []; + + public fontSamples: Float32Array = new Float32Array(0); + + public load(readable: IReadable): void { + const chunkHead: RiffChunk = new RiffChunk(); + const chunkFastList: RiffChunk = new RiffChunk(); + if (!RiffChunk.load(null, chunkHead, readable) || chunkHead.id !== 'sfbk') { + throw new FormatError("Soundfont is not a valid Soundfont2 file") + } + + while (RiffChunk.load(chunkHead, chunkFastList, readable)) { + + let chunk: RiffChunk = new RiffChunk(); + if (chunkFastList.id === 'pdta') { + while (RiffChunk.load(chunkFastList, chunk, readable)) { + switch (chunk.id) { + case 'phdr': + for ( + let i: number = 0, count: number = (chunk.size / HydraPhdr.SizeInFile) | 0; + i < count; + i++ + ) { + this.phdrs.push(new HydraPhdr(readable)); + } + break; + case 'pbag': + for ( + let i: number = 0, count: number = (chunk.size / HydraPbag.SizeInFile) | 0; + i < count; + i++ + ) { + this.pbags.push(new HydraPbag(readable)); + } + break; + case 'pmod': + for ( + let i: number = 0, count: number = (chunk.size / HydraPmod.SizeInFile) | 0; + i < count; + i++ + ) { + this.pmods.push(new HydraPmod(readable)); + } + break; + case 'pgen': + for ( + let i: number = 0, count: number = (chunk.size / HydraPgen.SizeInFile) | 0; + i < count; + i++ + ) { + this.pgens.push(new HydraPgen(readable)); + } + break; + case 'inst': + for ( + let i: number = 0, count: number = (chunk.size / HydraInst.SizeInFile) | 0; + i < count; + i++ + ) { + this.insts.push(new HydraInst(readable)); + } + break; + case 'ibag': + for ( + let i: number = 0, count: number = (chunk.size / HydraIbag.SizeInFile) | 0; + i < count; + i++ + ) { + this.ibags.push(new HydraIbag(readable)); + } + break; + case 'imod': + for ( + let i: number = 0, count: number = (chunk.size / HydraImod.SizeInFile) | 0; + i < count; + i++ + ) { + this.imods.push(new HydraImod(readable)); + } + break; + case 'igen': + for ( + let i: number = 0, count: number = (chunk.size / HydraIgen.SizeInFile) | 0; + i < count; + i++ + ) { + this.igens.push(new HydraIgen(readable)); + } + break; + case 'shdr': + for ( + let i: number = 0, count: number = (chunk.size / HydraShdr.SizeInFile) | 0; + i < count; + i++ + ) { + this.sHdrs.push(new HydraShdr(readable)); + } + break; + default: + readable.position += chunk.size; + break; + } + } + } else if (chunkFastList.id === 'sdta') { + while (RiffChunk.load(chunkFastList, chunk, readable)) { + switch (chunk.id) { + case 'smpl': + this.fontSamples = Hydra.loadSamples(chunk, readable); + break; + default: + readable.position += chunk.size; + break; + } + } + } else { + readable.position += chunkFastList.size; + } + } + } + + private static loadSamples(chunk: RiffChunk, reader: IReadable): Float32Array { + let samplesLeft: number = (chunk.size / 2) | 0; + const samples: Float32Array = new Float32Array(samplesLeft); + let samplesPos: number = 0; + + const sampleBuffer: Uint8Array = new Uint8Array(2048); + const testBuffer: Int16Array = new Int16Array((sampleBuffer.length / 2) | 0); + while (samplesLeft > 0) { + let samplesToRead: number = Math.min(samplesLeft, (sampleBuffer.length / 2) | 0); + reader.read(sampleBuffer, 0, samplesToRead * 2); + for (let i: number = 0; i < samplesToRead; i++) { + testBuffer[i] = (sampleBuffer[i * 2 + 1] << 8) | sampleBuffer[i * 2]; + samples[samplesPos + i] = testBuffer[i] / 32767; + } + samplesLeft -= samplesToRead; + samplesPos += samplesToRead; + } + return samples; + } +} + +export class HydraIbag { + public static readonly SizeInFile: number = 4; + + public instGenNdx: number; + public instModNdx: number; + + public constructor(reader: IReadable) { + this.instGenNdx = IOHelper.readUInt16LE(reader); + this.instModNdx = IOHelper.readUInt16LE(reader); + } +} + +export class HydraImod { + public static readonly SizeInFile: number = 10; + + public modSrcOper: number; + public modDestOper: number; + public modAmount: number; + public modAmtSrcOper: number; + public modTransOper: number; + + public constructor(reader: IReadable) { + this.modSrcOper = IOHelper.readUInt16LE(reader); + this.modDestOper = IOHelper.readUInt16LE(reader); + this.modAmount = IOHelper.readInt16LE(reader); + this.modAmtSrcOper = IOHelper.readUInt16LE(reader); + this.modTransOper = IOHelper.readUInt16LE(reader); + } +} + +export class HydraIgen { + public static readonly SizeInFile: number = 4; + + public genOper: number; + public genAmount: HydraGenAmount; + + public constructor(reader: IReadable) { + this.genOper = IOHelper.readUInt16LE(reader); + this.genAmount = new HydraGenAmount(reader); + } +} + +export class HydraInst { + public static readonly SizeInFile: number = 22; + + public instName: string; + public instBagNdx: number; + + public constructor(reader: IReadable) { + this.instName = IOHelper.read8BitStringLength(reader, 20); + this.instBagNdx = IOHelper.readUInt16LE(reader); + } +} + +export class HydraPbag { + public static readonly SizeInFile: number = 4; + + public genNdx: number; + public modNdx: number; + + public constructor(reader: IReadable) { + this.genNdx = IOHelper.readUInt16LE(reader); + this.modNdx = IOHelper.readUInt16LE(reader); + } +} + +export class HydraPgen { + public static readonly SizeInFile: number = 4; + public static readonly GenInstrument: number = 41; + public static readonly GenKeyRange: number = 43; + public static readonly GenVelRange: number = 44; + public static readonly GenSampleId: number = 53; + + public genOper: number; + public genAmount: HydraGenAmount; + + public constructor(reader: IReadable) { + this.genOper = IOHelper.readUInt16LE(reader); + this.genAmount = new HydraGenAmount(reader); + } +} + +export class HydraPhdr { + public static readonly SizeInFile: number = 38; + + public presetName: string; + public preset: number; + public bank: number; + public presetBagNdx: number; + public library: number; + public genre: number; + public morphology: number; + + public constructor(reader: IReadable) { + this.presetName = IOHelper.read8BitStringLength(reader, 20); + this.preset = IOHelper.readUInt16LE(reader); + this.bank = IOHelper.readUInt16LE(reader); + this.presetBagNdx = IOHelper.readUInt16LE(reader); + this.library = IOHelper.readUInt32LE(reader); + this.genre = IOHelper.readUInt32LE(reader); + this.morphology = IOHelper.readUInt32LE(reader); + } +} + +export class HydraPmod { + public static readonly SizeInFile: number = 10; + + public modSrcOper: number; + public modDestOper: number; + public modAmount: number; + public modAmtSrcOper: number; + public modTransOper: number; + + public constructor(reader: IReadable) { + this.modSrcOper = IOHelper.readUInt16LE(reader); + this.modDestOper = IOHelper.readUInt16LE(reader); + this.modAmount = IOHelper.readUInt16LE(reader); + this.modAmtSrcOper = IOHelper.readUInt16LE(reader); + this.modTransOper = IOHelper.readUInt16LE(reader); + } +} + +export class HydraShdr { + public static readonly SizeInFile: number = 46; + + public sampleName: string; + public start: number; + public end: number; + public startLoop: number; + public endLoop: number; + public sampleRate: number; + public originalPitch: number; + public pitchCorrection: number; + public sampleLink: number; + public sampleType: number; + + public constructor(reader: IReadable) { + this.sampleName = IOHelper.read8BitStringLength(reader, 20); + this.start = IOHelper.readUInt32LE(reader); + this.end = IOHelper.readUInt32LE(reader); + this.startLoop = IOHelper.readUInt32LE(reader); + this.endLoop = IOHelper.readUInt32LE(reader); + this.sampleRate = IOHelper.readUInt32LE(reader); + this.originalPitch = reader.readByte(); + this.pitchCorrection = IOHelper.readSInt8(reader); + this.sampleLink = IOHelper.readUInt16LE(reader); + this.sampleType = IOHelper.readUInt16LE(reader); + } +} + +export class HydraGenAmount { + public wordAmount: number; + + public get shortAmount(): number { + return TypeConversions.uint16ToInt16(this.wordAmount); + } + + public get lowByteAmount(): number { + return this.wordAmount & 0x00ff; + } + + public get highByteAmount(): number { + return ((this.wordAmount & 0xff00) >> 8) & 0xff; + } + + public constructor(reader: IReadable) { + this.wordAmount = IOHelper.readUInt16LE(reader); + } +} diff --git a/src/synth/soundfont/RiffChunk.ts b/src/synth/soundfont/RiffChunk.ts new file mode 100644 index 000000000..a4162f7e6 --- /dev/null +++ b/src/synth/soundfont/RiffChunk.ts @@ -0,0 +1,57 @@ +// The SoundFont loading and Audio Synthesis is based on TinySoundFont, licensed under MIT, +// developed by Bernhard Schelling (https://github.com/schellingb/TinySoundFont) +// TypeScript port for alphaTab: (C) 2020 by Daniel Kuschny +// Licensed under: MPL-2.0 +import { IOHelper } from '@src/io/IOHelper'; +import { IReadable } from '@src/io/IReadable'; + +export class RiffChunk { + public static readonly HeaderSize = 4 /*FourCC*/ + 4 /*Size*/; + + public id: string = ''; + public size: number = 0; + + public static load(parent: RiffChunk | null, chunk: RiffChunk, stream: IReadable): boolean { + if (parent && RiffChunk.HeaderSize > parent.size) { + return false; + } + + if (stream.position + RiffChunk.HeaderSize >= stream.length) { + return false; + } + + chunk.id = IOHelper.read8BitStringLength(stream, 4); + if (chunk.id.charCodeAt(0) <= 32 || chunk.id.charCodeAt(0) >= 122) { + return false; + } + chunk.size = IOHelper.readUInt32LE(stream); + + if (parent && RiffChunk.HeaderSize + chunk.size > parent.size) { + return false; + } + + if (parent) { + parent.size -= RiffChunk.HeaderSize + chunk.size; + } + + let isRiff: boolean = chunk.id === 'RIFF'; + let isList: boolean = chunk.id === 'LIST'; + if (isRiff && parent) { + // not allowed + return false; + } + + if (!isRiff && !isList) { + // custom type without sub type + return true; + } + + // for lists unwrap the list type + chunk.id = IOHelper.read8BitStringLength(stream, 4); + if (chunk.id.charCodeAt(0) <= 32 || chunk.id.charCodeAt(0) >= 122) { + return false; + } + chunk.size -= 4; + return true; + } +} diff --git a/src/synth/synthesis/Channel.ts b/src/synth/synthesis/Channel.ts new file mode 100644 index 000000000..80f6bbdc9 --- /dev/null +++ b/src/synth/synthesis/Channel.ts @@ -0,0 +1,22 @@ +// The SoundFont loading and Audio Synthesis is based on TinySoundFont, licensed under MIT, +// developed by Bernhard Schelling (https://github.com/schellingb/TinySoundFont) +// TypeScript port for alphaTab: (C) 2020 by Daniel Kuschny +// Licensed under: MPL-2.0 + +export class Channel { + public presetIndex: number = 0; + public bank: number = 0; + public pitchWheel: number = 0; + public midiPan: number = 0; + public midiVolume: number = 0; + public midiExpression: number = 0; + public midiRpn: number = 0; + public midiData: number = 0; + public panOffset: number = 0; + public gainDb: number = 0; + public pitchRange: number = 0; + public tuning: number = 0; + public mixVolume: number = 0; + public mute: boolean = false; + public solo: boolean = false; +} diff --git a/src/synth/synthesis/Channels.ts b/src/synth/synthesis/Channels.ts new file mode 100644 index 000000000..d30db48ff --- /dev/null +++ b/src/synth/synthesis/Channels.ts @@ -0,0 +1,36 @@ +// The SoundFont loading and Audio Synthesis is based on TinySoundFont, licensed under MIT, +// developed by Bernhard Schelling (https://github.com/schellingb/TinySoundFont) +// TypeScript port for alphaTab: (C) 2020 by Daniel Kuschny +// Licensed under: MPL-2.0 + +import { Channel } from '@src/synth/synthesis/Channel'; +import { TinySoundFont } from '@src/synth/synthesis/TinySoundFont'; +import { Voice } from '@src/synth/synthesis/Voice'; + +export class Channels { + public activeChannel: number = 0; + public channelList: Channel[] = []; + + public setupVoice(tinySoundFont: TinySoundFont, voice: Voice): void { + const c: Channel = this.channelList[this.activeChannel]; + const newpan: number = voice.region!.pan + c.panOffset; + voice.playingChannel = this.activeChannel; + voice.mixVolume = c.mixVolume; + voice.noteGainDb += c.gainDb; + voice.calcPitchRatio( + c.pitchWheel === 8192 ? c.tuning : (c.pitchWheel / 16383.0) * c.pitchRange * 2.0 - c.pitchRange + c.tuning, + tinySoundFont.outSampleRate + ); + + if (newpan <= -0.5) { + voice.panFactorLeft = 1.0; + voice.panFactorRight = 0.0; + } else if (newpan >= 0.5) { + voice.panFactorLeft = 0.0; + voice.panFactorRight = 1.0; + } else { + voice.panFactorLeft = Math.sqrt(0.5 - newpan); + voice.panFactorRight = Math.sqrt(0.5 + newpan); + } + } +} diff --git a/src/synth/synthesis/Envelope.ts b/src/synth/synthesis/Envelope.ts new file mode 100644 index 000000000..9ecea888c --- /dev/null +++ b/src/synth/synthesis/Envelope.ts @@ -0,0 +1,67 @@ +// The SoundFont loading and Audio Synthesis is based on TinySoundFont, licensed under MIT, +// developed by Bernhard Schelling (https://github.com/schellingb/TinySoundFont) +// TypeScript port for alphaTab: (C) 2020 by Daniel Kuschny +// Licensed under: MPL-2.0 +import { SynthHelper } from '@src/synth/SynthHelper'; + +export class Envelope { + public delay: number = 0; + public attack: number = 0; + public hold: number = 0; + public decay: number = 0; + public sustain: number = 0; + public release: number = 0; + public keynumToHold: number = 0; + public keynumToDecay: number = 0; + + public constructor(other?: Envelope) { + if (other) { + this.delay = other.delay; + this.attack = other.attack; + this.hold = other.hold; + this.decay = other.decay; + this.sustain = other.sustain; + this.release = other.release; + this.keynumToHold = other.keynumToHold; + this.keynumToDecay = other.keynumToDecay; + } + } + + public clear(): void { + this.delay = 0; + this.attack = 0; + this.hold = 0; + this.decay = 0; + this.sustain = 0; + this.release = 0; + this.keynumToHold = 0; + this.keynumToDecay = 0; + } + + public envToSecs(sustainIsGain: boolean): void { + // EG times need to be converted from timecents to seconds. + // Pin very short EG segments. Timecents don't get to zero, and our EG is + // happier with zero values. + this.delay = this.delay < -11950.0 ? 0.0 : SynthHelper.timecents2Secs(this.delay); + this.attack = this.attack < -11950.0 ? 0.0 : SynthHelper.timecents2Secs(this.attack); + this.release = this.release < -11950.0 ? 0.0 : SynthHelper.timecents2Secs(this.release); + + // If we have dynamic hold or decay times depending on key number we need + // to keep the values in timecents so we can calculate it during startNote + if (this.keynumToHold === 0) { + this.hold = this.hold < -11950.0 ? 0.0 : SynthHelper.timecents2Secs(this.hold); + } + + if (this.keynumToDecay === 0) { + this.decay = this.decay < -11950.0 ? 0.0 : SynthHelper.timecents2Secs(this.decay); + } + + if (this.sustain < 0.0) { + this.sustain = 0.0; + } else if (sustainIsGain) { + this.sustain = SynthHelper.decibelsToGain(-this.sustain / 10.0); + } else { + this.sustain = 1.0 - this.sustain / 1000.0; + } + } +} diff --git a/src/synth/synthesis/LoopMode.ts b/src/synth/synthesis/LoopMode.ts new file mode 100644 index 000000000..650421c65 --- /dev/null +++ b/src/synth/synthesis/LoopMode.ts @@ -0,0 +1,10 @@ +// The SoundFont loading and Audio Synthesis is based on TinySoundFont, licensed under MIT, +// developed by Bernhard Schelling (https://github.com/schellingb/TinySoundFont) +// TypeScript port for alphaTab: (C) 2020 by Daniel Kuschny +// Licensed under: MPL-2.0 + +export enum LoopMode { + None, + Continuous, + Sustain +} diff --git a/src/synth/synthesis/OutputMode.ts b/src/synth/synthesis/OutputMode.ts new file mode 100644 index 000000000..8aab4425a --- /dev/null +++ b/src/synth/synthesis/OutputMode.ts @@ -0,0 +1,22 @@ +// The SoundFont loading and Audio Synthesis is based on TinySoundFont, licensed under MIT, +// developed by Bernhard Schelling (https://github.com/schellingb/TinySoundFont) +// TypeScript port for alphaTab: (C) 2020 by Daniel Kuschny +// Licensed under: MPL-2.0 + +/** + * Supported output modes by the render methods + */ +export enum OutputMode { + /** + * Two channels with single left/right samples one after another + */ + StereoInterleaved, + /** + * Two channels with all samples for the left channel first then right + */ + StereoUnweaved, + /** + * A single channel (stereo instruments are mixed into center) + */ + Mono +} diff --git a/src/synth/synthesis/Preset.ts b/src/synth/synthesis/Preset.ts new file mode 100644 index 000000000..71d507608 --- /dev/null +++ b/src/synth/synthesis/Preset.ts @@ -0,0 +1,12 @@ +// The SoundFont loading and Audio Synthesis is based on TinySoundFont, licensed under MIT, +// developed by Bernhard Schelling (https://github.com/schellingb/TinySoundFont) +// TypeScript port for alphaTab: (C) 2020 by Daniel Kuschny +// Licensed under: MPL-2.0 +import { Region } from '@src/synth/synthesis/Region'; + +export class Preset { + public name: string = ""; + public presetNumber: number = 0; + public bank: number = 0; + public regions: Region[] | null = null; +} diff --git a/src/synth/synthesis/Region.ts b/src/synth/synthesis/Region.ts new file mode 100644 index 000000000..add235a54 --- /dev/null +++ b/src/synth/synthesis/Region.ts @@ -0,0 +1,346 @@ +// The SoundFont loading and Audio Synthesis is based on TinySoundFont, licensed under MIT, +// developed by Bernhard Schelling (https://github.com/schellingb/TinySoundFont) +// TypeScript port for alphaTab: (C) 2020 by Daniel Kuschny +// Licensed under: MPL-2.0 +import { HydraGenAmount } from '@src/synth/soundfont/Hydra'; +import { Envelope } from '@src/synth/synthesis/Envelope'; +import { LoopMode } from '@src/synth/synthesis/LoopMode'; +import { TypeConversions } from '@src/io/TypeConversions'; + +export enum GenOperators { + StartAddrsOffset, + EndAddrsOffset, + StartloopAddrsOffset, + EndloopAddrsOffset, + StartAddrsCoarseOffset, + ModLfoToPitch, + VibLfoToPitch, + ModEnvToPitch, + InitialFilterFc, + InitialFilterQ, + ModLfoToFilterFc, + ModEnvToFilterFc, + EndAddrsCoarseOffset, + ModLfoToVolume, + Unused1, + ChorusEffectsSend, + ReverbEffectsSend, + Pan, + Unused2, + Unused3, + Unused4, + DelayModLFO, + FreqModLFO, + DelayVibLFO, + FreqVibLFO, + DelayModEnv, + AttackModEnv, + HoldModEnv, + DecayModEnv, + SustainModEnv, + ReleaseModEnv, + KeynumToModEnvHold, + KeynumToModEnvDecay, + DelayVolEnv, + AttackVolEnv, + HoldVolEnv, + DecayVolEnv, + SustainVolEnv, + ReleaseVolEnv, + KeynumToVolEnvHold, + KeynumToVolEnvDecay, + Instrument, + Reserved1, + KeyRange, + VelRange, + StartloopAddrsCoarseOffset, + Keynum, + Velocity, + InitialAttenuation, + Reserved2, + EndloopAddrsCoarseOffset, + CoarseTune, + FineTune, + SampleID, + SampleModes, + Reserved3, + ScaleTuning, + ExclusiveClass, + OverridingRootKey, + Unused5, + EndOper +} + +export class Region { + public loopMode: LoopMode = LoopMode.None; + public sampleRate: number = 0; + public loKey: number = 0; + public hiKey: number = 0; + public loVel: number = 0; + public hiVel: number = 0; + public group: number = 0; + public offset: number = 0; + public end: number = 0; + public loopStart: number = 0; + public loopEnd: number = 0; + public transpose: number = 0; + public tune: number = 0; + public pitchKeyCenter: number = 0; + public pitchKeyTrack: number = 0; + public attenuation: number = 0; + public pan: number = 0; + public ampEnv: Envelope = new Envelope(); + public modEnv: Envelope = new Envelope(); + public initialFilterQ: number = 0; + public initialFilterFc: number = 0; + public modEnvToPitch: number = 0; + public modEnvToFilterFc: number = 0; + public modLfoToFilterFc: number = 0; + public modLfoToVolume: number = 0; + public delayModLFO: number = 0; + public freqModLFO: number = 0; + public modLfoToPitch: number = 0; + public delayVibLFO: number = 0; + public freqVibLFO: number = 0; + public vibLfoToPitch: number = 0; + + public constructor(other?: Region) { + if (other) { + this.loopMode = other.loopMode; + this.sampleRate = other.sampleRate; + this.loKey = other.loKey; + this.hiKey = other.hiKey; + this.loVel = other.loVel; + this.hiVel = other.hiVel; + this.group = other.group; + this.offset = other.offset; + this.end = other.end; + this.loopStart = other.loopStart; + this.loopEnd = other.loopEnd; + this.transpose = other.transpose; + this.tune = other.tune; + this.pitchKeyCenter = other.pitchKeyCenter; + this.pitchKeyTrack = other.pitchKeyTrack; + this.attenuation = other.attenuation; + this.pan = other.pan; + this.ampEnv = new Envelope(other.ampEnv); + this.modEnv = new Envelope(other.modEnv); + this.initialFilterQ = other.initialFilterQ; + this.initialFilterFc = other.initialFilterFc; + this.modEnvToPitch = other.modEnvToPitch; + this.modEnvToFilterFc = other.modEnvToFilterFc; + this.modLfoToFilterFc = other.modLfoToFilterFc; + this.modLfoToVolume = other.modLfoToVolume; + this.delayModLFO = other.delayModLFO; + this.freqModLFO = other.freqModLFO; + this.modLfoToPitch = other.modLfoToPitch; + this.delayVibLFO = other.delayVibLFO; + this.freqVibLFO = other.freqVibLFO; + this.vibLfoToPitch = other.vibLfoToPitch; + } + } + + public clear(forRelative: boolean): void { + this.loopMode = 0; + this.sampleRate = 0; + this.loKey = 0; + this.hiKey = 0; + this.loVel = 0; + this.hiVel = 0; + this.group = 0; + this.offset = 0; + this.end = 0; + this.loopStart = 0; + this.loopEnd = 0; + this.transpose = 0; + this.tune = 0; + this.pitchKeyCenter = 0; + this.pitchKeyTrack = 0; + this.attenuation = 0; + this.pan = 0; + this.ampEnv.clear(); + this.modEnv.clear(); + this.initialFilterQ = 0; + this.initialFilterFc = 0; + this.modEnvToPitch = 0; + this.modEnvToFilterFc = 0; + this.modLfoToFilterFc = 0; + this.modLfoToVolume = 0; + this.delayModLFO = 0; + this.freqModLFO = 0; + this.modLfoToPitch = 0; + this.delayVibLFO = 0; + this.freqVibLFO = 0; + this.vibLfoToPitch = 0; + + this.hiKey = this.hiVel = 127; + this.pitchKeyCenter = 60; // C4 + + if (forRelative) { + return; + } + + this.pitchKeyTrack = 100; + + this.pitchKeyCenter = -1; + + // SF2 defaults in timecents. + this.ampEnv.delay = this.ampEnv.attack = this.ampEnv.hold = this.ampEnv.decay = this.ampEnv.release = -12000.0; + this.modEnv.delay = this.modEnv.attack = this.modEnv.hold = this.modEnv.decay = this.modEnv.release = -12000.0; + + this.initialFilterFc = 13500; + + this.delayModLFO = -12000.0; + this.delayVibLFO = -12000.0; + } + + public operator(genOper: number, amount: HydraGenAmount): void { + switch (genOper as GenOperators) { + case GenOperators.StartAddrsOffset: + this.offset += TypeConversions.int16ToUint32(amount.shortAmount); + break; + case GenOperators.EndAddrsOffset: + this.end += TypeConversions.int16ToUint32(amount.shortAmount); + break; + case GenOperators.StartloopAddrsOffset: + this.loopStart += TypeConversions.int16ToUint32(amount.shortAmount); + break; + case GenOperators.EndloopAddrsOffset: + this.loopEnd += TypeConversions.int16ToUint32(amount.shortAmount); + break; + case GenOperators.StartAddrsCoarseOffset: + this.offset += TypeConversions.int16ToUint32(amount.shortAmount) * 32768; + break; + case GenOperators.ModLfoToPitch: + this.modLfoToPitch = amount.shortAmount; + break; + case GenOperators.VibLfoToPitch: + this.vibLfoToPitch = amount.shortAmount; + break; + case GenOperators.ModEnvToPitch: + this.modEnvToPitch = amount.shortAmount; + break; + case GenOperators.InitialFilterFc: + this.initialFilterFc = amount.shortAmount; + break; + case GenOperators.InitialFilterQ: + this.initialFilterQ = amount.shortAmount; + break; + case GenOperators.ModLfoToFilterFc: + this.modLfoToFilterFc = amount.shortAmount; + break; + case GenOperators.ModEnvToFilterFc: + this.modEnvToFilterFc = amount.shortAmount; + break; + case GenOperators.EndAddrsCoarseOffset: + this.end += TypeConversions.int16ToUint32(amount.shortAmount) * 32768; + break; + case GenOperators.ModLfoToVolume: + this.modLfoToVolume = amount.shortAmount; + break; + case GenOperators.Pan: + this.pan = amount.shortAmount / 1000.0; + break; + case GenOperators.DelayModLFO: + this.delayModLFO = amount.shortAmount; + break; + case GenOperators.FreqModLFO: + this.freqModLFO = amount.shortAmount; + break; + case GenOperators.DelayVibLFO: + this.delayVibLFO = amount.shortAmount; + break; + case GenOperators.FreqVibLFO: + this.freqVibLFO = amount.shortAmount; + break; + case GenOperators.DelayModEnv: + this.modEnv.delay = amount.shortAmount; + break; + case GenOperators.AttackModEnv: + this.modEnv.attack = amount.shortAmount; + break; + case GenOperators.HoldModEnv: + this.modEnv.hold = amount.shortAmount; + break; + case GenOperators.DecayModEnv: + this.modEnv.decay = amount.shortAmount; + break; + case GenOperators.SustainModEnv: + this.modEnv.sustain = amount.shortAmount; + break; + case GenOperators.ReleaseModEnv: + this.modEnv.release = amount.shortAmount; + break; + case GenOperators.KeynumToModEnvHold: + this.modEnv.keynumToHold = amount.shortAmount; + break; + case GenOperators.KeynumToModEnvDecay: + this.modEnv.keynumToDecay = amount.shortAmount; + break; + case GenOperators.DelayVolEnv: + this.ampEnv.delay = amount.shortAmount; + break; + case GenOperators.AttackVolEnv: + this.ampEnv.attack = amount.shortAmount; + break; + case GenOperators.HoldVolEnv: + this.ampEnv.hold = amount.shortAmount; + break; + case GenOperators.DecayVolEnv: + this.ampEnv.decay = amount.shortAmount; + break; + case GenOperators.SustainVolEnv: + this.ampEnv.sustain = amount.shortAmount; + break; + case GenOperators.ReleaseVolEnv: + this.ampEnv.release = amount.shortAmount; + break; + case GenOperators.KeynumToVolEnvHold: + this.ampEnv.keynumToHold = amount.shortAmount; + break; + case GenOperators.KeynumToVolEnvDecay: + this.ampEnv.keynumToDecay = amount.shortAmount; + break; + case GenOperators.KeyRange: + this.loKey = amount.lowByteAmount; + this.hiKey = amount.highByteAmount; + break; + case GenOperators.VelRange: + this.loVel = amount.lowByteAmount; + this.hiVel = amount.highByteAmount; + break; + case GenOperators.StartloopAddrsCoarseOffset: + this.loopStart += TypeConversions.int16ToUint32(amount.shortAmount) * 32768; + break; + case GenOperators.InitialAttenuation: + this.attenuation += amount.shortAmount * 0.1; + break; + case GenOperators.EndloopAddrsCoarseOffset: + this.loopEnd += TypeConversions.int16ToUint32(amount.shortAmount) * 32768; + break; + case GenOperators.CoarseTune: + this.transpose += amount.shortAmount; + break; + case GenOperators.FineTune: + this.tune += amount.shortAmount; + break; + case GenOperators.SampleModes: + this.loopMode = + (amount.wordAmount & 3) === 3 + ? LoopMode.Sustain + : (amount.wordAmount & 3) === 1 + ? LoopMode.Continuous + : LoopMode.None; + break; + case GenOperators.ScaleTuning: + this.pitchKeyTrack = amount.shortAmount; + break; + case GenOperators.ExclusiveClass: + this.group = amount.wordAmount; + break; + case GenOperators.OverridingRootKey: + this.pitchKeyCenter = amount.shortAmount; + break; + } + } +} diff --git a/src/synth/synthesis/SynthEvent.ts b/src/synth/synthesis/SynthEvent.ts new file mode 100644 index 000000000..dd83650c3 --- /dev/null +++ b/src/synth/synthesis/SynthEvent.ts @@ -0,0 +1,23 @@ +// The SoundFont loading and Audio Synthesis is based on TinySoundFont, licensed under MIT, +// developed by Bernhard Schelling (https://github.com/schellingb/TinySoundFont) +// TypeScript port for alphaTab: (C) 2020 by Daniel Kuschny +// Licensed under: MPL-2.0 +import { MidiEvent } from '@src/midi/MidiEvent'; + +export class SynthEvent { + public eventIndex: number; + public event: MidiEvent | null; + public isMetronome: boolean = false; + public time: number = 0; + + public constructor(eventIndex: number, e: MidiEvent | null) { + this.eventIndex = eventIndex; + this.event = e; + } + + public static newMetronomeEvent(eventIndex: number): SynthEvent { + const x: SynthEvent = new SynthEvent(eventIndex, null); + x.isMetronome = true; + return x; + } +} diff --git a/src/synth/synthesis/TinySoundFont.ts b/src/synth/synthesis/TinySoundFont.ts new file mode 100644 index 000000000..81670f57a --- /dev/null +++ b/src/synth/synthesis/TinySoundFont.ts @@ -0,0 +1,1278 @@ +// The SoundFont loading and Audio Synthesis is based on TinySoundFont, licensed under MIT, +// developed by Bernhard Schelling (https://github.com/schellingb/TinySoundFont) +// TypeScript port for alphaTab: (C) 2020 by Daniel Kuschny +// Licensed under: MPL-2.0 +import { MidiEvent, MidiEventType } from '@src/midi/MidiEvent'; +import { + Hydra, + HydraIbag, + HydraIgen, + HydraInst, + HydraPbag, + HydraPgen, + HydraPhdr, + HydraShdr +} from '@src/synth/soundfont/Hydra'; +import { Channel } from '@src/synth/synthesis/Channel'; +import { Channels } from '@src/synth/synthesis/Channels'; +import { LoopMode } from '@src/synth/synthesis/LoopMode'; +import { OutputMode } from '@src/synth/synthesis/OutputMode'; +import { Preset } from '@src/synth/synthesis/Preset'; +import { Region } from '@src/synth/synthesis/Region'; +import { SynthEvent } from '@src/synth/synthesis/SynthEvent'; +import { Voice } from '@src/synth/synthesis/Voice'; +import { VoiceEnvelopeSegment } from '@src/synth/synthesis/VoiceEnvelope'; +import { SynthHelper } from '@src/synth/SynthHelper'; +import { TypeConversions } from '@src/io/TypeConversions'; +import { Logger } from '@src/Logger'; +import { SynthConstants } from '@src/synth/SynthConstants'; + +/** + * This is a tiny soundfont based synthesizer. + * NOT YET IMPLEMENTED + * - Support for ChorusEffectsSend and ReverbEffectsSend generators + * - Better low-pass filter without lowering performance too much + * - Support for modulators + */ +export class TinySoundFont { + public static readonly MicroBufferCount: number = 32; // 4069 samples in total + public static readonly MicroBufferSize: number = 64; // 64 stereo samples + + private _midiEventQueue: SynthEvent[] = []; + private _midiEventCounts: Int32Array = new Int32Array(TinySoundFont.MicroBufferCount); + private _mutedChannels: Map = new Map(); + private _soloChannels: Map = new Map(); + private _isAnySolo: boolean = false; + + public constructor(sampleRate: number) { + this.outSampleRate = sampleRate; + } + + public synthesize(): Float32Array { + return this.fillWorkingBuffer(false); + } + + public synthesizeSilent(): void { + this.fillWorkingBuffer(true); + } + + public channelGetMixVolume(channel: number): number { + return this._channels && channel < this._channels.channelList.length + ? this._channels.channelList[channel].mixVolume + : 1.0; + } + + public channelSetMixVolume(channel: number, volume: number): void { + let c: Channel = this.channelInit(channel); + for (let v of this._voices) { + if (v.playingChannel === channel && v.playingPreset !== -1) { + v.mixVolume = volume; + } + } + c.mixVolume = volume; + } + + public channelSetMute(channel: number, mute: boolean): void { + if (mute) { + this._mutedChannels.set(channel, true); + } else { + this._mutedChannels.delete(channel); + } + } + + public channelSetSolo(channel: number, solo: boolean): void { + if (solo) { + this._soloChannels.set(channel, true); + } else { + this._soloChannels.delete(channel); + } + this._isAnySolo = this._soloChannels.size > 0; + } + + public resetChannelStates(): void { + this._mutedChannels = new Map(); + this._soloChannels = new Map(); + this._isAnySolo = false; + } + + public dispatchEvent(i: number, synthEvent: SynthEvent): void { + this._midiEventQueue.unshift(synthEvent); + this._midiEventCounts[i]++; + } + + private fillWorkingBuffer(silent: boolean): Float32Array { + // Break the process loop into sections representing the smallest timeframe before the midi controls need to be updated + // the bigger the timeframe the more efficent the process is, but playback quality will be reduced. + const buffer: Float32Array = new Float32Array(TinySoundFont.MicroBufferSize * TinySoundFont.MicroBufferCount * SynthConstants.AudioChannels); + let bufferPos: number = 0; + const anySolo: boolean = this._isAnySolo; + + // process in micro-buffers + for (let x: number = 0; x < TinySoundFont.MicroBufferCount; x++) { + + // process events for first microbuffer + if (this._midiEventQueue.length > 0) { + for (let i: number = 0; i < this._midiEventCounts[x]; i++) { + let m: SynthEvent | undefined = this._midiEventQueue.pop(); + if (m) { + if (m.isMetronome && this.metronomeVolume > 0) { + this.channelNoteOff(SynthConstants.MetronomeChannel, 33); + this.channelNoteOn(SynthConstants.MetronomeChannel, 33, 95 / 127); + } else if (m.event) { + this.processMidiMessage(m.event); + } + } + } + } + + // voice processing loop + for (const voice of this._voices) { + if (voice.playingPreset !== -1) { + const channel: number = voice.playingChannel; + // channel is muted if it is either explicitley muted, or another channel is set to solo but not this one. + const isChannelMuted: boolean = + this._mutedChannels.has(channel) || (anySolo && !this._soloChannels.has(channel)); + + if (silent) { + voice.kill(); + } else { + voice.render(this, buffer, bufferPos, 64, isChannelMuted); + } + } + } + bufferPos += TinySoundFont.MicroBufferSize * SynthConstants.AudioChannels; + } + + this._midiEventCounts.fill(0); + return buffer; + } + + private processMidiMessage(e: MidiEvent): void { + Logger.debug('Midi', 'Processing midi ' + e.command); + const command: MidiEventType = e.command; + const channel: number = e.channel; + const data1: number = e.data1; + const data2: number = e.data2; + switch (command) { + case MidiEventType.NoteOff: + this.channelNoteOff(channel, data1); + break; + case MidiEventType.NoteOn: + this.channelNoteOn(channel, data1, data2 / 127.0); + break; + case MidiEventType.NoteAftertouch: + break; + case MidiEventType.Controller: + this.channelMidiControl(channel, data1, data2); + break; + case MidiEventType.ProgramChange: + this.channelSetPresetNumber(channel, data1, channel === 9); + break; + case MidiEventType.ChannelAftertouch: + break; + case MidiEventType.PitchBend: + this.channelSetPitchWheel(channel, data1 | (data2 << 7)); + break; + } + } + + public get metronomeVolume(): number { + return this.channelGetMixVolume(SynthConstants.MetronomeChannel); + } + + public set metronomeVolume(value: number) { + this.channelSetMixVolume(SynthConstants.MetronomeChannel, value); + } + + public setupMetronomeChannel(volume: number): void { + this.channelSetMixVolume(SynthConstants.MetronomeChannel, volume); + if(volume > 0) { + this.channelSetVolume(SynthConstants.MetronomeChannel, 1); + this.channelSetPresetNumber(SynthConstants.MetronomeChannel, 0, true); + } + } + + /** + * Stop all playing notes immediatly and reset all channel parameters but keeps user + * defined settings + */ + public resetSoft(): void { + for (const v of this._voices) { + if ( + v.playingPreset !== -1 && + (v.ampEnv.segment < VoiceEnvelopeSegment.Release || v.ampEnv.parameters!.release !== 0) + ) { + v.endQuick(this.outSampleRate); + } + } + + if (this._channels) { + for (const c of this._channels.channelList) { + c.presetIndex = c.bank = 0; + c.pitchWheel = c.midiPan = 8192; + c.midiVolume = c.midiExpression = 16383; + c.midiRpn = 0xffff; + c.midiData = 0; + c.panOffset = 0.0; + c.gainDb = 0.0; + c.pitchRange = 2.0; + c.tuning = 0.0; + } + } + } + + private _presets: Preset[] | null = null; + private _voices: Voice[] = []; + private _channels: Channels | null = null; + private _voicePlayIndex: number = 0; + public fontSamples: Float32Array = new Float32Array(0); + + public get presetCount(): number { + return this._presets?.length ?? 0; + } + + /** + * Gets the currently configured output mode. + */ + public outputMode: OutputMode = OutputMode.StereoInterleaved; + + /** + * Gets the currently configured sample rate. + */ + public outSampleRate: number = 0; + + /** + * Gets the currently configured global gain in DB. + */ + public globalGainDb: number = 0; + + /** + * Stop all playing notes immediatly and reset all channel parameters + */ + public reset(): void { + for (let v of this._voices) { + if (v.playingPreset !== -1 && + (v.ampEnv.segment < VoiceEnvelopeSegment.Release || v.ampEnv.parameters!.release !== 0)) { + v.endQuick(this.outSampleRate); + } + } + this._channels = null; + } + + /** + * Setup the parameters for the voice render methods + * @param outputMode if mono or stereo and how stereo channel data is ordered + * @param sampleRate the number of samples per second (output frequency) + * @param globalGainDb volume gain in decibels (>0 means higher, <0 means lower) + */ + public setOutput(outputMode: OutputMode, sampleRate: number, globalGainDb: number): void { + this.outputMode = outputMode; + this.outSampleRate = sampleRate >= 1 ? sampleRate : 44100.0; + this.globalGainDb = globalGainDb; + } + + /** + * Start playing a note + * @param presetIndex preset index >= 0 and < {@link presetCount} + * @param key note value between 0 and 127 (60 being middle C) + * @param vel velocity as a float between 0.0 (equal to note off) and 1.0 (full) + */ + public noteOn(presetIndex: number, key: number, vel: number): void { + if (!this._presets) { + return; + } + + const midiVelocity: number = (vel * 127) | 0; + if (presetIndex < 0 || presetIndex >= this._presets.length) { + return; + } + + if (vel <= 0.0) { + this.noteOff(presetIndex, key); + return; + } + + // Play all matching regions. + const voicePlayIndex: number = this._voicePlayIndex++; + for (const region of this._presets[presetIndex].regions!) { + if (key < region.loKey || + key > region.hiKey || + midiVelocity < region.loVel || + midiVelocity > region.hiVel + ) { + continue; + } + let voice: Voice | null = null; + if (region.group !== 0) { + for (const v of this._voices) { + if (v.playingPreset === presetIndex && v.region!.group === region.group) { + v.endQuick(this.outSampleRate); + } else if (v.playingPreset === -1 && !voice) { + voice = v; + } + } + } else { + for (let v of this._voices) { + if (v.playingPreset === -1) { + voice = v; + } + } + } + + if (!voice) { + for (let i: number = 0; i < 4; i++) { + const newVoice: Voice = new Voice(); + newVoice.playingPreset = -1; + this._voices.push(newVoice); + } + voice = this._voices[this._voices.length - 4]; + } + + voice.region = region; + voice.playingPreset = presetIndex; + voice.playingKey = key; + voice.playIndex = voicePlayIndex; + voice.noteGainDb = this.globalGainDb - region.attenuation - SynthHelper.gainToDecibels(1.0 / vel); + + if (this._channels) { + this._channels.setupVoice(this, voice); + } else { + voice.calcPitchRatio(0, this.outSampleRate); + // The SFZ spec is silent about the pan curve, but a 3dB pan law seems common. This sqrt() curve matches what Dimension LE does; Alchemy Free seems closer to sin(adjustedPan * pi/2). + voice.panFactorLeft = Math.sqrt(0.5 - region.pan); + voice.panFactorRight = Math.sqrt(0.5 + region.pan); + } + + // Offset/end. + voice.sourceSamplePosition = region.offset; + + // Loop. + const doLoop: boolean = region.loopMode !== LoopMode.None && region.loopStart < region.loopEnd; + voice.loopStart = doLoop ? region.loopStart : 0; + voice.loopEnd = doLoop ? region.loopEnd : 0; + + // Setup envelopes. + voice.ampEnv.setup(region.ampEnv, key, midiVelocity, true, this.outSampleRate); + voice.modEnv.setup(region.modEnv, key, midiVelocity, false, this.outSampleRate); + + // Setup lowpass filter. + const filterQDB: number = region.initialFilterQ / 10.0; + voice.lowPass.qInv = 1.0 / Math.pow(10.0, filterQDB / 20.0); + voice.lowPass.z1 = voice.lowPass.z2 = 0; + voice.lowPass.active = region.initialFilterFc <= 13500; + if (voice.lowPass.active) { + voice.lowPass.setup(SynthHelper.cents2Hertz(region.initialFilterFc) / this.outSampleRate); + } + + // Setup LFO filters. + voice.modLfo.setup(region.delayModLFO, region.freqModLFO, this.outSampleRate); + voice.vibLfo.setup(region.delayVibLFO, region.freqVibLFO, this.outSampleRate); + } + } + + /** + * Start playing a note + * @param bank instrument bank number (alternative to preset_index) + * @param presetNumber preset number (alternative to preset_index) + * @param key note value between 0 and 127 (60 being middle C) + * @param vel velocity as a float between 0.0 (equal to note off) and 1.0 (full) + * @returns returns false if preset does not exist, otherwise true + */ + public bankNoteOn(bank: number, presetNumber: number, key: number, vel: number): boolean { + let presetIndex: number = this.getPresetIndex(bank, presetNumber); + if (presetIndex === -1) { + return false; + } + this.noteOn(presetIndex, key, vel); + return true; + } + + /** + * Stop playing a note + */ + public noteOff(presetIndex: number, key: number): void { + let matchFirst: Voice | null = null; + let matchLast: Voice | null = null; + let matches: Voice[] = []; + for (let v of this._voices) { + if (v.playingPreset !== presetIndex || + v.playingKey !== key || + v.ampEnv.segment >= VoiceEnvelopeSegment.Release + ) { + continue; + } else if (!matchFirst || v.playIndex < matchFirst.playIndex) { + matchFirst = v; + matchLast = v; + matches.push(v); + } else if (v.playIndex === matchFirst.playIndex) { + matchLast = v; + matches.push(v); + } + } + + if (!matchFirst) { + return; + + } + for (const v of matches) { + if (v !== matchFirst && + v !== matchLast && + (v.playIndex !== matchFirst.playIndex || + v.playingPreset !== presetIndex || + v.playingKey !== key || + v.ampEnv.segment >= VoiceEnvelopeSegment.Release) + ) { + continue; + } + v.end(this.outSampleRate); + } + } + + /** + * Stop playing a note + * @returns returns false if preset does not exist, otherwise true + */ + public bankNoteOff(bank: number, presetNumber: number, key: number): boolean { + const presetIndex: number = this.getPresetIndex(bank, presetNumber); + if (presetIndex === -1) { + return false; + } + + this.noteOff(presetIndex, key); + return true; + } + + /** + * Stop playing all notes (end with sustain and release) + */ + public noteOffAll(immediate: boolean): void { + for (const voice of this._voices) { + if (voice.playingPreset !== -1 && voice.ampEnv.segment < VoiceEnvelopeSegment.Release) { + if (immediate) { + voice.endQuick(this.outSampleRate); + } else { + voice.end(this.outSampleRate); + } + } + } + } + + public get activeVoiceCount(): number { + let count: number = 0; + for (const v of this._voices) { + if (v.playingPreset !== -1) { + count++; + } + } + return count; + } + + private channelInit(channel: number): Channel { + if (this._channels && channel < this._channels.channelList.length) { + return this._channels.channelList[channel]; + } + + if (!this._channels) { + this._channels = new Channels(); + } + + for (let i: number = this._channels.channelList.length; i <= channel; i++) { + let c: Channel = new Channel(); + c.presetIndex = c.bank = 0; + c.pitchWheel = c.midiPan = 8192; + c.midiVolume = c.midiExpression = 16383; + c.midiRpn = 0xffff; + c.midiData = 0; + c.panOffset = 0.0; + c.gainDb = 0.0; + c.pitchRange = 2.0; + c.tuning = 0.0; + c.mixVolume = 1; + this._channels.channelList.push(c); + } + + return this._channels.channelList[channel]; + } + + /** + * Returns the preset index from a bank and preset number, or -1 if it does not exist in the loaded SoundFont + */ + private getPresetIndex(bank: number, presetNumber: number): number { + if (!this._presets) { + return -1; + } + + for (let i: number = 0; i < this._presets.length; i++) { + let preset: Preset = this._presets[i]; + if (preset.presetNumber === presetNumber && preset.bank === bank) { + return i; + } + } + return -1; + } + + /** + * Returns the name of a preset index >= 0 and < GetPresetName() + * @param presetIndex + */ + public getPresetName(presetIndex: number): string | null { + if (!this._presets) { + return null; + } + return presetIndex < 0 || presetIndex >= this._presets.length ? null : this._presets[presetIndex].name; + } + + /** + * Returns the name of a preset by bank and preset number + */ + public bankGetPresetName(bank: number, presetNumber: number): string | null { + return this.getPresetName(this.getPresetIndex(bank, presetNumber)); + } + + /** + * Start playing a note on a channel + * @param channel channel number + * @param key note value between 0 and 127 (60 being middle C) + * @param vel velocity as a float between 0.0 (equal to note off) and 1.0 (full) + */ + public channelNoteOn(channel: number, key: number, vel: number): void { + if (!this._channels || channel > this._channels.channelList.length) { + return; + } + + this._channels.activeChannel = channel; + this.noteOn(this._channels.channelList[channel].presetIndex, key, vel); + } + + /** + * Stop playing notes on a channel + * @param channel channel number + * @param key note value between 0 and 127 (60 being middle C) + */ + public channelNoteOff(channel: number, key: number): void { + const matches: Voice[] = []; + let matchFirst: Voice | null = null; + let matchLast: Voice | null = null; + for (const v of this._voices) { + // Find the first and last entry in the voices list with matching channel, key and look up the smallest play index + if (v.playingPreset === -1 || + v.playingChannel !== channel || + v.playingKey !== key || + v.ampEnv.segment >= VoiceEnvelopeSegment.Release + ) { + continue; + } + + if (!matchFirst || v.playIndex < matchFirst.playIndex) { + matchFirst = matchLast = v; + matches.push(v); + } else if (v.playIndex === matchFirst.playIndex) { + matchLast = v; + matches.push(v); + } + } + + if (!matchFirst) { + return; + } + + for (const v of matches) { + // Stop all voices with matching channel, key and the smallest play index which was enumerated above + if (v !== matchFirst && + v !== matchLast && + (v.playIndex !== matchFirst.playIndex || + v.playingPreset === -1 || + v.playingChannel !== channel || + v.playingKey !== key || + v.ampEnv.segment >= VoiceEnvelopeSegment.Release) + ) { + continue; + } + + v.end(this.outSampleRate); + } + } + + /** + * Stop playing all notes on a channel with sustain and release. + * @param channel channel number + */ + public channelNoteOffAll(channel: number): void { + for (const v of this._voices) { + if ( + v.playingPreset !== -1 && + v.playingChannel === channel && + v.ampEnv.segment < VoiceEnvelopeSegment.Release + ) { + v.end(this.outSampleRate); + } + } + } + + /** + * Stop playing all notes on a channel immediately + * @param channel channel number + */ + public channelSoundsOffAll(channel: number): void { + for (let v of this._voices) { + if (v.playingPreset !== -1 && + v.playingChannel === channel && + (v.ampEnv.segment < VoiceEnvelopeSegment.Release || v.ampEnv.parameters!.release === 0) + ) { + v.endQuick(this.outSampleRate); + } + } + } + + /** + * + * @param channel channel number + * @param presetIndex preset index <= 0 and > {@link presetCount} + */ + public channelSetPresetIndex(channel: number, presetIndex: number): void { + this.channelInit(channel).presetIndex = TypeConversions.int32ToUint16(presetIndex); + } + + /** + * @param channel channel number + * @param presetNumber preset number (alternative to preset_index) + * @param midiDrums false for normal channels, otherwise apply MIDI drum channel rules + * @returns return false if preset does not exist, otherwise true + */ + public channelSetPresetNumber(channel: number, presetNumber: number, midiDrums: boolean = false): boolean { + const c: Channel = this.channelInit(channel); + let presetIndex: number = 0; + if (midiDrums) { + presetIndex = this.getPresetIndex(128 | (c.bank & 0x7fff), presetNumber); + if (presetIndex === -1) { + presetIndex = this.getPresetIndex(128, presetNumber); + } + if (presetIndex === -1) { + presetIndex = this.getPresetIndex(128, 0); + } + if (presetIndex === -1) { + presetIndex = this.getPresetIndex(c.bank & 0x7ff, presetNumber); + } + } else { + presetIndex = this.getPresetIndex(c.bank & 0x7ff, presetNumber); + } + + if (presetIndex === -1) { + presetIndex = this.getPresetIndex(0, presetNumber); + } + + if (presetIndex !== -1) { + c.presetIndex = presetIndex; + return true; + } + + return false; + } + + /** + * @param channel channel number + * @param bank instrument bank number (alternative to preset_index) + */ + public channelSetBank(channel: number, bank: number): void { + this.channelInit(channel).bank = TypeConversions.int32ToUint16(bank); + } + + /** + * @param channel channel number + * @param bank instrument bank number (alternative to preset_index) + * @param presetNumber preset number (alternative to preset_index) + * @returns return false if preset does not exist, otherwise true + */ + public channelSetBankPreset(channel: number, bank: number, presetNumber: number): boolean { + const c: Channel = this.channelInit(channel); + const presetIndex: number = this.getPresetIndex(bank, presetNumber); + if (presetIndex === -1) { + return false; + } + + c.presetIndex = TypeConversions.int32ToUint16(presetIndex); + c.bank = TypeConversions.int32ToUint16(bank); + return true; + } + + /** + * @param channel channel number + * @param pan stereo panning value from 0.0 (left) to 1.0 (right) (default 0.5 center) + */ + public channelSetPan(channel: number, pan: number): void { + for (const v of this._voices) { + if (v.playingChannel === channel && v.playingPreset !== -1) { + let newPan: number = v.region!.pan + pan - 0.5; + if (newPan <= -0.5) { + v.panFactorLeft = 1; + v.panFactorRight = 0; + } else if (newPan >= 0.5) { + v.panFactorLeft = 0; + v.panFactorRight = 1; + } else { + v.panFactorLeft = Math.sqrt(0.5 - newPan); + v.panFactorRight = Math.sqrt(0.5 + newPan); + } + } + } + this.channelInit(channel).panOffset = pan - 0.5; + } + + /** + * @param channel channel number + * @param volume linear volume scale factor (default 1.0 full) + */ + public channelSetVolume(channel: number, volume: number): void { + const c: Channel = this.channelInit(channel); + const gainDb: number = SynthHelper.gainToDecibels(volume); + const gainDBChange: number = gainDb - c.gainDb; + if (gainDBChange === 0) { + return; + } + + for (const v of this._voices) { + if (v.playingChannel === channel && v.playingPreset !== -1) { + v.noteGainDb += gainDBChange; + } + } + + c.gainDb = gainDb; + } + + /** + * @param channel channel number + * @param pitchWheel pitch wheel position 0 to 16383 (default 8192 unpitched) + */ + public channelSetPitchWheel(channel: number, pitchWheel: number): void { + const c: Channel = this.channelInit(channel); + if (c.pitchWheel === pitchWheel) { + return; + } + + c.pitchWheel = TypeConversions.int32ToUint16(pitchWheel); + this.channelApplyPitch(channel, c); + } + + private channelApplyPitch(channel: number, c: Channel): void { + const pitchShift: number = c.pitchWheel === 8192 + ? c.tuning + : (c.pitchWheel / 16383.0 * c.pitchRange * 2) - c.pitchRange + c.tuning; + for (const v of this._voices) { + if (v.playingChannel === channel && v.playingPreset !== -1) { + v.calcPitchRatio(pitchShift, this.outSampleRate); + } + } + } + + /** + * @param channel channel number + * @param pitchRange range of the pitch wheel in semitones (default 2.0, total +/- 2 semitones) + */ + public channelSetPitchRange(channel: number, pitchRange: number): void { + const c: Channel = this.channelInit(channel); + if (c.pitchRange === pitchRange) { + return; + } + + c.pitchRange = pitchRange; + if (c.pitchWheel !== 8192) { + this.channelApplyPitch(channel, c); + } + } + + /** + * @param channel channel number + * @param tuning tuning of all playing voices in semitones (default 0.0, standard (A440) tuning) + */ + public channelSetTuning(channel: number, tuning: number): void { + const c: Channel = this.channelInit(channel); + if (c.tuning === tuning) { + return; + } + + c.tuning = tuning; + this.channelApplyPitch(channel, c); + } + + /** + * Apply a MIDI control change to the channel (not all controllers are supported!) + */ + public channelMidiControl(channel: number, controller: number, controlValue: number): void { + let c: Channel = this.channelInit(channel); + switch (controller) { + case 5: /*Portamento_Time_MSB*/ + case 96: /*DATA_BUTTON_INCREMENT*/ + case 97: /*DATA_BUTTON_DECREMENT*/ + case 64: /*HOLD_PEDAL*/ + case 65: /*Portamento*/ + case 66: /*SostenutoPedal */ + case 122: /*LocalKeyboard */ + case 124: /*OmniModeOff */ + case 125: /*OmniModeon */ + case 126: /*MonoMode */ + case 127 /*PolyMode*/: + return; + + case 38 /*DATA_ENTRY_LSB*/: + c.midiData = TypeConversions.int32ToUint16((c.midiData & 0x3f80) | controlValue); + + if (c.midiRpn === 0) { + this.channelSetPitchRange(channel, (c.midiData >> 7) + 0.01 * (c.midiData & 0x7f)); + } else if (c.midiRpn === 1) { + this.channelSetTuning(channel, (c.tuning | 0) + (c.midiData - 8192.0) / 8192.0); // fine tune + } else if (c.midiRpn === 2) { + this.channelSetTuning(channel, controlValue - 64.0 + (c.tuning - (c.tuning | 0))); // coarse tune + } + return; + + case 7 /*VOLUME_MSB*/: + c.midiVolume = TypeConversions.int32ToUint16((c.midiVolume & 0x7f) | (controlValue << 7)); + // Raising to the power of 3 seems to result in a decent sounding volume curve for MIDI + this.channelSetVolume(channel, Math.pow((c.midiVolume / 16383.0) * (c.midiExpression / 16383.0), 3.0)); + return; + case 39 /*VOLUME_LSB*/: + c.midiVolume = TypeConversions.int32ToUint16((c.midiVolume & 0x3f80) | controlValue); + // Raising to the power of 3 seems to result in a decent sounding volume curve for MIDI + this.channelSetVolume(channel, Math.pow((c.midiVolume / 16383.0) * (c.midiExpression / 16383.0), 3.0)); + return; + case 11 /*EXPRESSION_MSB*/: + c.midiExpression = TypeConversions.int32ToUint16((c.midiExpression & 0x7f) | (controlValue << 7)); + // Raising to the power of 3 seems to result in a decent sounding volume curve for MIDI + this.channelSetVolume(channel, Math.pow((c.midiVolume / 16383.0) * (c.midiExpression / 16383.0), 3.0)); + return; + case 43 /*EXPRESSION_LSB*/: + c.midiExpression = TypeConversions.int32ToUint16((c.midiExpression & 0x3f80) | controlValue); + // Raising to the power of 3 seems to result in a decent sounding volume curve for MIDI + this.channelSetVolume(channel, Math.pow((c.midiVolume / 16383.0) * (c.midiExpression / 16383.0), 3.0)); + return; + case 10 /*PAN_MSB*/: + c.midiPan = TypeConversions.int32ToUint16((c.midiPan & 0x7f) | (controlValue << 7)); + this.channelSetPan(channel, c.midiPan / 16383.0); + return; + case 42 /*PAN_LSB*/: + c.midiPan = TypeConversions.int32ToUint16((c.midiPan & 0x3f80) | controlValue); + this.channelSetPan(channel, c.midiPan / 16383.0); + return; + case 6 /*DATA_ENTRY_MSB*/: + c.midiData = TypeConversions.int32ToUint16((c.midiData & 0x7f) | (controlValue << 7)); + if (c.midiRpn === 0) { + this.channelSetPitchRange(channel, (c.midiData >> 7) + 0.01 * (c.midiData & 0x7f)); + } else if (c.midiRpn === 1) { + this.channelSetTuning(channel, (c.tuning | 0) + (c.midiData - 8192.0) / 8192.0); // fine tune + } else if (c.midiRpn === 2 && controller === 6) { + this.channelSetTuning(channel, controlValue - 64.0 + (c.tuning - (c.tuning | 0))); // coarse tune + } + return; + case 0 /*BANK_SELECT_MSB*/: + c.bank = TypeConversions.int32ToUint16(0x8000 | controlValue); + return; + // bank select MSB alone acts like LSB + case 32 /*BANK_SELECT_LSB*/: + c.bank = TypeConversions.int32ToUint16( + ((c.bank & 0x8000) !== 0 ? (c.bank & 0x7f) << 7 : 0) | controlValue + ); + return; + case 101 /*RPN_MSB*/: + c.midiRpn = TypeConversions.int32ToUint16( + ((c.midiRpn === 0xffff ? 0 : c.midiRpn) & 0x7f) | (controlValue << 7) + ); + // TODO + return; + case 100 /*RPN_LSB*/: + c.midiRpn = TypeConversions.int32ToUint16( + ((c.midiRpn === 0xffff ? 0 : c.midiRpn) & 0x3f80) | controlValue + ); + // TODO + return; + case 98 /*NRPN_LSB*/: + c.midiRpn = 0xffff; + // TODO + return; + case 99 /*NRPN_MSB*/: + c.midiRpn = 0xffff; + // TODO + return; + case 120 /*ALL_SOUND_OFF*/: + this.channelSoundsOffAll(channel); + return; + case 123 /*ALL_NOTES_OFF*/: + this.channelNoteOffAll(channel); + return; + case 121 /*ALL_CTRL_OFF*/: + c.midiVolume = c.midiExpression = 16383; + c.midiPan = 8192; + c.bank = 0; + this.channelSetVolume(channel, 1); + this.channelSetPan(channel, 0.5); + this.channelSetPitchRange(channel, 2); + // TODO + return; + } + } + + /** + * Gets the current preset index of the given channel. + * @param channel The channel index + * @returns The current preset index of the given channel. + */ + public channelGetPresetIndex(channel: number): number { + return this._channels && channel < this._channels.channelList.length + ? this._channels.channelList[channel].presetIndex + : 0; + } + + /** + * Gets the current bank of the given channel. + * @param channel The channel index + * @returns The current bank of the given channel. + */ + public channelGetPresetBank(channel: number): number { + return this._channels && channel < this._channels.channelList.length + ? this._channels.channelList[channel].bank & 0x7fff + : 0; + } + + /** + * Gets the current pan of the given channel. + * @param channel The channel index + * @returns The current pan of the given channel. + */ + public channelGetPan(channel: number): number { + return this._channels && channel < this._channels.channelList.length + ? this._channels.channelList[channel].panOffset - 0.5 + : 0.5; + } + + /** + * Gets the current volume of the given channel. + * @param channel The channel index + * @returns The current volune of the given channel. + */ + public channelGetVolume(channel: number): number { + return this._channels && channel < this._channels.channelList.length + ? SynthHelper.decibelsToGain(this._channels.channelList[channel].gainDb) + : 1.0; + } + + /** + * Gets the current pitch wheel of the given channel. + * @param channel The channel index + * @returns The current pitch wheel of the given channel. + */ + public channelGetPitchWheel(channel: number): number { + return this._channels && channel < this._channels.channelList.length + ? this._channels.channelList[channel].pitchWheel + : 8192; + } + + /** + * Gets the current pitch range of the given channel. + * @param channel The channel index + * @returns The current pitch range of the given channel. + */ + public channelGetPitchRange(channel: number): number { + return this._channels && channel < this._channels.channelList.length + ? this._channels.channelList[channel].pitchRange + : 2.0; + } + + /** + * Gets the current tuning of the given channel. + * @param channel The channel index + * @returns The current tuning of the given channel. + */ + public channelGetTuning(channel: number): number { + return this._channels && channel < this._channels.channelList.length + ? this._channels.channelList[channel].tuning + : 0.0; + } + + public loadPresets(hydra: Hydra): void { + this._presets = new Array(hydra.phdrs.length - 1); + this.fontSamples = hydra.fontSamples; + + for (let phdrIndex: number = 0; phdrIndex < hydra.phdrs.length - 1; phdrIndex++) { + const phdr: HydraPhdr = hydra.phdrs[phdrIndex]; + let regionIndex: number = 0; + + const preset: Preset = (this._presets[phdrIndex] = new Preset()); + preset.name = phdr.presetName; + preset.bank = phdr.bank; + preset.presetNumber = phdr.preset; + let regionNum: number = 0; + + for ( + let pbagIndex: number = phdr.presetBagNdx; + pbagIndex < hydra.phdrs[phdrIndex + 1].presetBagNdx; + pbagIndex++ + ) { + const pbag: HydraPbag = hydra.pbags[pbagIndex]; + let plokey: number = 0; + let phikey: number = 127; + let plovel: number = 0; + let phivel: number = 127; + + for (let pgenIndex: number = pbag.genNdx; pgenIndex < hydra.pbags[pbagIndex + 1].genNdx; pgenIndex++) { + let pgen: HydraPgen = hydra.pgens[pgenIndex]; + + if (pgen.genOper === HydraPgen.GenKeyRange) { + plokey = pgen.genAmount.lowByteAmount; + phikey = pgen.genAmount.highByteAmount; + continue; + } + + if (pgen.genOper === HydraPgen.GenVelRange) { + plovel = pgen.genAmount.lowByteAmount; + phivel = pgen.genAmount.highByteAmount; + continue; + } + + if (pgen.genOper !== HydraPgen.GenInstrument) { + continue; + } + + if (pgen.genAmount.wordAmount >= hydra.insts.length) { + continue; + } + + let pinst: HydraInst = hydra.insts[pgen.genAmount.wordAmount]; + for ( + let ibagIndex: number = pinst.instBagNdx; + ibagIndex < hydra.insts[pgen.genAmount.wordAmount + 1].instBagNdx; + ibagIndex++ + ) { + let ibag: HydraIbag = hydra.ibags[ibagIndex]; + + let ilokey: number = 0; + let ihikey: number = 127; + let ilovel: number = 0; + let ihivel: number = 127; + + for ( + let igenIndex: number = ibag.instGenNdx; + igenIndex < hydra.ibags[ibagIndex + 1].instGenNdx; + igenIndex++ + ) { + let igen: HydraIgen = hydra.igens[igenIndex]; + if (igen.genOper === HydraPgen.GenKeyRange) { + ilokey = igen.genAmount.lowByteAmount; + ihikey = igen.genAmount.highByteAmount; + continue; + } + + if (igen.genOper === HydraPgen.GenVelRange) { + ilovel = igen.genAmount.lowByteAmount; + ihivel = igen.genAmount.highByteAmount; + continue; + } + + if ( + igen.genOper === 53 && + ihikey >= plokey && + ilokey <= phikey && + ihivel >= plovel && + ilovel <= phivel + ) { + regionNum++; + } + } + } + } + } + + preset.regions = new Array(regionNum); + + let globalRegion: Region = new Region(); + globalRegion.clear(true); + + // Zones. + for ( + let pbagIndex: number = phdr.presetBagNdx; + pbagIndex < hydra.phdrs[phdrIndex + 1].presetBagNdx; + pbagIndex++ + ) { + const pbag: HydraPbag = hydra.pbags[pbagIndex]; + + const presetRegion: Region = new Region(globalRegion); + let hadGenInstrument: boolean = false; + + // Generators. + for (let pgenIndex: number = pbag.genNdx; pgenIndex < hydra.pbags[pbagIndex + 1].genNdx; pgenIndex++) { + const pgen: HydraPgen = hydra.pgens[pgenIndex]; + + // Instrument. + if (pgen.genOper === HydraPgen.GenInstrument) { + let whichInst: number = pgen.genAmount.wordAmount; + if (whichInst >= hydra.insts.length) { + continue; + } + + let instRegion: Region = new Region(); + instRegion.clear(false); + + // Generators + let inst: HydraInst = hydra.insts[whichInst]; + for ( + let ibagIndex: number = inst.instBagNdx; + ibagIndex < hydra.insts[whichInst + 1].instBagNdx; + ibagIndex++ + ) { + let ibag: HydraIbag = hydra.ibags[ibagIndex]; + let zoneRegion: Region = new Region(instRegion); + let hadSampleId: boolean = false; + + for ( + let igenIndex: number = ibag.instGenNdx; + igenIndex < hydra.ibags[ibagIndex + 1].instGenNdx; + igenIndex++ + ) { + let igen: HydraIgen = hydra.igens[igenIndex]; + + if (igen.genOper === HydraPgen.GenSampleId) { + // preset region key and vel ranges are a filter for the zone regions + if ( + zoneRegion.hiKey < presetRegion.loKey || + zoneRegion.loKey > presetRegion.hiKey + ) { + continue; + } + + if ( + zoneRegion.hiVel < presetRegion.loVel || + zoneRegion.loVel > presetRegion.hiVel + ) { + continue; + } + + if (presetRegion.loKey > zoneRegion.loKey) { + zoneRegion.loKey = presetRegion.loKey; + } + + if (presetRegion.hiKey < zoneRegion.hiKey) { + zoneRegion.hiKey = presetRegion.hiKey; + } + + if (presetRegion.loVel > zoneRegion.loVel) { + zoneRegion.loVel = presetRegion.loVel; + } + + if (presetRegion.hiVel < zoneRegion.hiVel) { + zoneRegion.hiVel = presetRegion.hiVel; + } + + // sum regions + zoneRegion.offset += presetRegion.offset; + zoneRegion.end += presetRegion.end; + zoneRegion.loopStart += presetRegion.loopStart; + zoneRegion.loopEnd += presetRegion.loopEnd; + zoneRegion.transpose += presetRegion.transpose; + zoneRegion.tune += presetRegion.tune; + zoneRegion.pitchKeyTrack += presetRegion.pitchKeyTrack; + zoneRegion.attenuation += presetRegion.attenuation; + zoneRegion.pan += presetRegion.pan; + zoneRegion.ampEnv.delay += presetRegion.ampEnv.delay; + zoneRegion.ampEnv.attack += presetRegion.ampEnv.attack; + zoneRegion.ampEnv.hold += presetRegion.ampEnv.hold; + zoneRegion.ampEnv.decay += presetRegion.ampEnv.decay; + zoneRegion.ampEnv.sustain += presetRegion.ampEnv.sustain; + zoneRegion.ampEnv.release += presetRegion.ampEnv.release; + zoneRegion.modEnv.delay += presetRegion.modEnv.delay; + zoneRegion.modEnv.attack += presetRegion.modEnv.attack; + zoneRegion.modEnv.hold += presetRegion.modEnv.hold; + zoneRegion.modEnv.decay += presetRegion.modEnv.decay; + zoneRegion.modEnv.sustain += presetRegion.modEnv.sustain; + zoneRegion.modEnv.release += presetRegion.modEnv.release; + zoneRegion.initialFilterQ += presetRegion.initialFilterQ; + zoneRegion.initialFilterFc += presetRegion.initialFilterFc; + zoneRegion.modEnvToPitch += presetRegion.modEnvToPitch; + zoneRegion.modEnvToFilterFc += presetRegion.modEnvToFilterFc; + zoneRegion.delayModLFO += presetRegion.delayModLFO; + zoneRegion.freqModLFO += presetRegion.freqModLFO; + zoneRegion.modLfoToPitch += presetRegion.modLfoToPitch; + zoneRegion.modLfoToFilterFc += presetRegion.modLfoToFilterFc; + zoneRegion.modLfoToVolume += presetRegion.modLfoToVolume; + zoneRegion.delayVibLFO += presetRegion.delayVibLFO; + zoneRegion.freqVibLFO += presetRegion.freqVibLFO; + zoneRegion.vibLfoToPitch += presetRegion.vibLfoToPitch; + + // EG times need to be converted from timecents to seconds. + zoneRegion.ampEnv.envToSecs(true); + zoneRegion.modEnv.envToSecs(false); + + // LFO times need to be converted from timecents to seconds. + zoneRegion.delayModLFO = + zoneRegion.delayModLFO < -11950.0 + ? 0.0 + : SynthHelper.timecents2Secs(zoneRegion.delayModLFO); + zoneRegion.delayVibLFO = + zoneRegion.delayVibLFO < -11950.0 + ? 0.0 + : SynthHelper.timecents2Secs(zoneRegion.delayVibLFO); + + // Pin values to their ranges. + if (zoneRegion.pan < -0.5) { + zoneRegion.pan = -0.5; + } else if (zoneRegion.pan > 0.5) { + zoneRegion.pan = 0.5; + } + + if (zoneRegion.initialFilterQ < 1500 || zoneRegion.initialFilterQ > 13500) { + zoneRegion.initialFilterQ = 0; + } + + let shdr: HydraShdr = hydra.sHdrs[igen.genAmount.wordAmount]; + zoneRegion.offset += shdr.start; + zoneRegion.end += shdr.end; + zoneRegion.loopStart += shdr.startLoop; + zoneRegion.loopEnd += shdr.endLoop; + if (shdr.endLoop > 0) { + zoneRegion.loopEnd -= 1; + } + + if (zoneRegion.pitchKeyCenter === -1) { + zoneRegion.pitchKeyCenter = shdr.originalPitch; + } + + zoneRegion.tune += shdr.pitchCorrection; + zoneRegion.sampleRate = shdr.sampleRate; + if (zoneRegion.end !== 0 && zoneRegion.end < this.fontSamples.length) { + zoneRegion.end++; + } else { + zoneRegion.end = this.fontSamples.length; + } + + preset.regions[regionIndex] = new Region(zoneRegion); + regionIndex++; + + hadSampleId = true; + } else { + zoneRegion.operator(igen.genOper, igen.genAmount); + } + } + + // Handle instrument's global zone. + if (ibag === hydra.ibags[inst.instBagNdx] && !hadSampleId) { + instRegion = new Region(zoneRegion); + } + + // Modulators (TODO) + //if (ibag->instModNdx < ibag[1].instModNdx) addUnsupportedOpcode("any modulator"); + } + + hadGenInstrument = true; + } else { + presetRegion.operator(pgen.genOper, pgen.genAmount); + } + } + + // Modulators (TODO) + // if (pbag->modNdx < pbag[1].modNdx) addUnsupportedOpcode("any modulator"); + + // Handle preset's global zone. + if (pbag === hydra.pbags[phdr.presetBagNdx] && !hadGenInstrument) { + globalRegion = presetRegion; + } + } + } + } +} diff --git a/src/synth/synthesis/Voice.ts b/src/synth/synthesis/Voice.ts new file mode 100644 index 000000000..2df8fbc06 --- /dev/null +++ b/src/synth/synthesis/Voice.ts @@ -0,0 +1,312 @@ +// The SoundFont loading and Audio Synthesis is based on TinySoundFont, licensed under MIT, +// developed by Bernhard Schelling (https://github.com/schellingb/TinySoundFont) +// TypeScript port for alphaTab: (C) 2020 by Daniel Kuschny +// Licensed under: MPL-2.0 +import { LoopMode } from '@src/synth/synthesis/LoopMode'; +import { OutputMode } from '@src/synth/synthesis/OutputMode'; +import { Region } from '@src/synth/synthesis/Region'; +import { TinySoundFont } from '@src/synth/synthesis/TinySoundFont'; +import { VoiceEnvelope, VoiceEnvelopeSegment } from '@src/synth/synthesis/VoiceEnvelope'; +import { VoiceLfo } from '@src/synth/synthesis/VoiceLfo'; +import { VoiceLowPass } from '@src/synth/synthesis/VoiceLowPass'; +import { SynthHelper } from '@src/synth/SynthHelper'; + +export class Voice { + /** + * The lower this block size is the more accurate the effects are. + * Increasing the value significantly lowers the CPU usage of the voice rendering. + * If LFO affects the low-pass filter it can be hearable even as low as 8. + */ + private static readonly RenderEffectSampleBLock: number = 64; + + public playingPreset: number = 0; + public playingKey: number = 0; + public playingChannel: number = 0; + + public region: Region | null = null; + + public pitchInputTimecents: number = 0; + public pitchOutputFactor: number = 0; + public sourceSamplePosition: number = 0; + + public noteGainDb: number = 0; + public panFactorLeft: number = 0; + public panFactorRight: number = 0; + + public playIndex: number = 0; + public loopStart: number = 0; + public loopEnd: number = 0; + + public ampEnv: VoiceEnvelope = new VoiceEnvelope(); + public modEnv: VoiceEnvelope = new VoiceEnvelope(); + + public lowPass: VoiceLowPass = new VoiceLowPass(); + public modLfo: VoiceLfo = new VoiceLfo(); + public vibLfo: VoiceLfo = new VoiceLfo(); + + public mixVolume: number = 0; + public mute: boolean = false; + + public calcPitchRatio(pitchShift: number, outSampleRate: number): void { + if (!this.region) { + return; + } + + const note: number = this.playingKey + this.region.transpose + this.region.tune / 100.0; + let adjustedPitch: number = + this.region.pitchKeyCenter + (note - this.region.pitchKeyCenter) * (this.region.pitchKeyTrack / 100.0); + if (pitchShift !== 0) adjustedPitch += pitchShift; + this.pitchInputTimecents = adjustedPitch * 100.0; + this.pitchOutputFactor = + this.region.sampleRate / (SynthHelper.timecents2Secs(this.region.pitchKeyCenter * 100.0) * outSampleRate); + } + + public end(outSampleRate: number): void { + if (!this.region) { + return; + } + + this.ampEnv.nextSegment(VoiceEnvelopeSegment.Sustain, outSampleRate); + this.modEnv.nextSegment(VoiceEnvelopeSegment.Sustain, outSampleRate); + if (this.region.loopMode === LoopMode.Sustain) { + // Continue playing, but stop looping. + this.loopEnd = this.loopStart; + } + } + + public endQuick(outSampleRate: number): void { + this.ampEnv.parameters!.release = 0.0; + this.ampEnv.nextSegment(VoiceEnvelopeSegment.Sustain, outSampleRate); + this.modEnv.parameters!.release = 0.0; + this.modEnv.nextSegment(VoiceEnvelopeSegment.Sustain, outSampleRate); + } + + public render( + f: TinySoundFont, + outputBuffer: Float32Array, + offset: number, + numSamples: number, + isMuted: boolean + ): void { + if (!this.region) { + return; + } + + let region: Region = this.region; + let input: Float32Array = f.fontSamples; + let outL: number = 0; + let outR: number = f.outputMode === OutputMode.StereoUnweaved ? numSamples : -1; + + // Cache some values, to give them at least some chance of ending up in registers. + let updateModEnv: boolean = region.modEnvToPitch !== 0 || region.modEnvToFilterFc !== 0; + let updateModLFO: boolean = + this.modLfo.delta > 0 && + (region.modLfoToPitch !== 0 || region.modLfoToFilterFc !== 0 || region.modLfoToVolume !== 0); + let updateVibLFO: boolean = this.vibLfo.delta > 0 && region.vibLfoToPitch !== 0; + let isLooping: boolean = this.loopStart < this.loopEnd; + let tmpLoopStart: number = this.loopStart; + let tmpLoopEnd: number = this.loopEnd; + let tmpSampleEndDbl: number = region.end; + let tmpLoopEndDbl: number = tmpLoopEnd + 1.0; + let tmpSourceSamplePosition: number = this.sourceSamplePosition; + + let tmpLowpass: VoiceLowPass = new VoiceLowPass(this.lowPass); + + let dynamicLowpass: boolean = region.modLfoToFilterFc !== 0 || region.modEnvToFilterFc !== 0; + let tmpSampleRate: number = 0; + let tmpInitialFilterFc: number = 0; + let tmpModLfoToFilterFc: number = 0; + let tmpModEnvToFilterFc: number = 0; + + let dynamicPitchRatio: boolean = + region.modLfoToPitch !== 0 || region.modEnvToPitch !== 0 || region.vibLfoToPitch !== 0; + let pitchRatio: number = 0; + let tmpModLfoToPitch: number = 0; + let tmpVibLfoToPitch: number = 0; + let tmpModEnvToPitch: number = 0; + + let dynamicGain: boolean = region.modLfoToVolume !== 0; + let noteGain: number = 0; + let tmpModLfoToVolume: number = 0; + + if (dynamicLowpass) { + tmpSampleRate = f.outSampleRate; + tmpInitialFilterFc = region.initialFilterFc; + tmpModLfoToFilterFc = region.modLfoToFilterFc; + tmpModEnvToFilterFc = region.modEnvToFilterFc; + } else { + tmpSampleRate = 0; + tmpInitialFilterFc = 0; + tmpModLfoToFilterFc = 0; + tmpModEnvToFilterFc = 0; + } + + if (dynamicPitchRatio) { + pitchRatio = 0; + tmpModLfoToPitch = region.modLfoToPitch; + tmpVibLfoToPitch = region.vibLfoToPitch; + tmpModEnvToPitch = region.modEnvToPitch; + } else { + pitchRatio = SynthHelper.timecents2Secs(this.pitchInputTimecents) * this.pitchOutputFactor; + tmpModLfoToPitch = 0; + tmpVibLfoToPitch = 0; + tmpModEnvToPitch = 0; + } + + if (dynamicGain) { + tmpModLfoToVolume = region.modLfoToVolume * 0.1; + } else { + noteGain = SynthHelper.decibelsToGain(this.noteGainDb); + tmpModLfoToVolume = 0; + } + + while (numSamples > 0) { + let gainMono: number; + let gainLeft: number; + let gainRight: number = 0; + let blockSamples: number = numSamples > Voice.RenderEffectSampleBLock ? Voice.RenderEffectSampleBLock : numSamples; + numSamples -= blockSamples; + + if (dynamicLowpass) { + let fres: number = + tmpInitialFilterFc + + this.modLfo.level * tmpModLfoToFilterFc + + this.modEnv.level * tmpModEnvToFilterFc; + tmpLowpass.active = fres <= 13500.0; + if (tmpLowpass.active) { + tmpLowpass.setup(SynthHelper.cents2Hertz(fres) / tmpSampleRate); + } + } + + if (dynamicPitchRatio) { + pitchRatio = + SynthHelper.timecents2Secs( + this.pitchInputTimecents + + (this.modLfo.level * tmpModLfoToPitch + + this.vibLfo.level * tmpVibLfoToPitch + + this.modEnv.level * tmpModEnvToPitch) + ) * this.pitchOutputFactor; + } + + if (dynamicGain) { + noteGain = SynthHelper.decibelsToGain(this.noteGainDb + this.modLfo.level * tmpModLfoToVolume); + } + + gainMono = noteGain * this.ampEnv.level; + + if (isMuted) { + gainMono = 0; + } else { + gainMono *= this.mixVolume; + } + + // Update EG. + this.ampEnv.process(blockSamples, f.outSampleRate); + if (updateModEnv) { + this.modEnv.process(blockSamples, f.outSampleRate); + } + + // Update LFOs. + if (updateModLFO) { + this.modLfo.process(blockSamples); + } + + if (updateVibLFO) { + this.vibLfo.process(blockSamples); + } + + switch (f.outputMode) { + case OutputMode.StereoInterleaved: + gainLeft = gainMono * this.panFactorLeft; + gainRight = gainMono * this.panFactorRight; + while (blockSamples-- > 0 && tmpSourceSamplePosition < tmpSampleEndDbl) { + let pos: number = tmpSourceSamplePosition | 0; + let nextPos: number = pos >= tmpLoopEnd && isLooping ? tmpLoopStart : pos + 1; + + // Simple linear interpolation. + + // TODO: check for interpolation mode on voice + let alpha: number = tmpSourceSamplePosition - pos; + let val: number = input[pos] * (1.0 - alpha) + input[nextPos] * alpha; + + // Low-pass filter. + if (tmpLowpass.active) val = tmpLowpass.process(val); + + outputBuffer[offset + outL] += val * gainLeft; + outL++; + outputBuffer[offset + outL] += val * gainRight; + outL++; + + // Next sample. + tmpSourceSamplePosition += pitchRatio; + if (tmpSourceSamplePosition >= tmpLoopEndDbl && isLooping) { + tmpSourceSamplePosition -= tmpLoopEnd - tmpLoopStart + 1.0; + } + } + break; + case OutputMode.StereoUnweaved: + gainLeft = gainMono * this.panFactorLeft; + gainRight = gainMono * this.panFactorRight; + while (blockSamples-- > 0 && tmpSourceSamplePosition < tmpSampleEndDbl) { + let pos: number = tmpSourceSamplePosition | 0; + let nextPos: number = pos >= tmpLoopEnd && isLooping ? tmpLoopStart : pos + 1; + + // Simple linear interpolation. + let alpha: number = tmpSourceSamplePosition - pos; + let val: number = input[pos] * (1.0 - alpha) + input[nextPos] * alpha; + + // Low-pass filter. + if (tmpLowpass.active) val = tmpLowpass.process(val); + + outputBuffer[offset + outL] += val * gainLeft; + outL++; + outputBuffer[offset + outR] += val * gainRight; + outR++; + + // Next sample. + tmpSourceSamplePosition += pitchRatio; + if (tmpSourceSamplePosition >= tmpLoopEndDbl && isLooping) { + tmpSourceSamplePosition -= tmpLoopEnd - tmpLoopStart + 1.0; + } + } + break; + case OutputMode.Mono: + while (blockSamples-- > 0 && tmpSourceSamplePosition < tmpSampleEndDbl) { + let pos: number = tmpSourceSamplePosition | 0; + let nextPos: number = pos >= tmpLoopEnd && isLooping ? tmpLoopStart : pos + 1; + + // Simple linear interpolation. + let alpha: number = tmpSourceSamplePosition - pos; + let val: number = input[pos] * (1.0 - alpha) + input[nextPos] * alpha; + + // Low-pass filter. + if (tmpLowpass.active) val = tmpLowpass.process(val); + + outputBuffer[offset + outL] = val * gainMono; + outL++; + + // Next sample. + tmpSourceSamplePosition += pitchRatio; + if (tmpSourceSamplePosition >= tmpLoopEndDbl && isLooping) { + tmpSourceSamplePosition -= tmpLoopEnd - tmpLoopStart + 1.0; + } + } + break; + } + + if (tmpSourceSamplePosition >= tmpSampleEndDbl || this.ampEnv.segment === VoiceEnvelopeSegment.Done) { + this.kill(); + return; + } + } + + this.sourceSamplePosition = tmpSourceSamplePosition; + if (tmpLowpass.active || dynamicLowpass) { + this.lowPass = tmpLowpass; + } + } + + public kill(): void { + this.playingPreset = -1; + } +} diff --git a/src/synth/synthesis/VoiceEnvelope.ts b/src/synth/synthesis/VoiceEnvelope.ts new file mode 100644 index 000000000..eefca09a8 --- /dev/null +++ b/src/synth/synthesis/VoiceEnvelope.ts @@ -0,0 +1,196 @@ +// The SoundFont loading and Audio Synthesis is based on TinySoundFont, licensed under MIT, +// developed by Bernhard Schelling (https://github.com/schellingb/TinySoundFont) +// TypeScript port for alphaTab: (C) 2020 by Daniel Kuschny +// Licensed under: MPL-2.0 +import { Envelope } from '@src/synth/synthesis/Envelope'; +import { SynthHelper } from '@src/synth/SynthHelper'; + +export enum VoiceEnvelopeSegment { + None, + Delay, + Attack, + Hold, + Decay, + Sustain, + Release, + Done +} + +export class VoiceEnvelope { + private static readonly FastReleaseTime: number = 0.01; + + public level: number = 0; + public slope: number = 0; + + public samplesUntilNextSegment: number = 0; + + public segment: VoiceEnvelopeSegment = VoiceEnvelopeSegment.None; + public midiVelocity: number = 0; + + public parameters: Envelope | null = null; + + public segmentIsExponential: boolean = false; + public isAmpEnv: boolean = false; + + public nextSegment(activeSegment: VoiceEnvelopeSegment, outSampleRate: number): void { + if (!this.parameters) { + return; + } + + while (true) { + switch (activeSegment) { + case VoiceEnvelopeSegment.None: + this.samplesUntilNextSegment = (this.parameters.delay * outSampleRate) | 0; + + if (this.samplesUntilNextSegment > 0) { + this.segment = VoiceEnvelopeSegment.Delay; + this.segmentIsExponential = false; + this.level = 0.0; + this.slope = 0.0; + return; + } + + activeSegment = VoiceEnvelopeSegment.Delay; + break; + + case VoiceEnvelopeSegment.Delay: + this.samplesUntilNextSegment = (this.parameters.attack * outSampleRate) | 0; + + if (this.samplesUntilNextSegment > 0) { + + if (!this.isAmpEnv) { + // mod env attack duration scales with velocity (velocity of 1 is full duration, max velocity is 0.125 times duration) + this.samplesUntilNextSegment = + (this.parameters.attack * ((145 - this.midiVelocity) / 144.0) * outSampleRate) | 0; + } + + this.segment = VoiceEnvelopeSegment.Attack; + this.segmentIsExponential = false; + this.level = 0.0; + this.slope = 1.0 / this.samplesUntilNextSegment; + return; + } + activeSegment = VoiceEnvelopeSegment.Attack; + break; + + case VoiceEnvelopeSegment.Attack: + this.samplesUntilNextSegment = (this.parameters.hold * outSampleRate) | 0; + + if (this.samplesUntilNextSegment > 0) { + this.segment = VoiceEnvelopeSegment.Hold; + this.segmentIsExponential = false; + this.level = 1.0; + this.slope = 0.0; + return; + } + + activeSegment = VoiceEnvelopeSegment.Hold; + break; + + case VoiceEnvelopeSegment.Hold: + this.samplesUntilNextSegment = (this.parameters.decay * outSampleRate) | 0; + if (this.samplesUntilNextSegment > 0) { + this.segment = VoiceEnvelopeSegment.Decay; + this.level = 1.0; + + if (this.isAmpEnv) { + // I don't truly understand this; just following what LinuxSampler does. + let mysterySlope: number = -9.226 / this.samplesUntilNextSegment; + this.slope = Math.exp(mysterySlope); + this.segmentIsExponential = true; + if (this.parameters.sustain > 0.0) { + // Again, this is following LinuxSampler's example, which is similar to + // SF2-style decay, where "decay" specifies the time it would take to + // get to zero, not to the sustain level. The SFZ spec is not that + // specific about what "decay" means, so perhaps it's really supposed + // to specify the time to reach the sustain level. + this.samplesUntilNextSegment = (Math.log(this.parameters.sustain) / mysterySlope) | 0; + } + } else { + this.slope = -1.0 / this.samplesUntilNextSegment; + this.samplesUntilNextSegment = + (this.parameters.decay * (1.0 - this.parameters.sustain) * outSampleRate) | 0; + this.segmentIsExponential = false; + } + + return; + } + activeSegment = VoiceEnvelopeSegment.Decay; + break; + + case VoiceEnvelopeSegment.Decay: + this.segment = VoiceEnvelopeSegment.Sustain; + this.level = this.parameters.sustain; + this.slope = 0.0; + this.samplesUntilNextSegment = 0x7fffffff; + this.segmentIsExponential = false; + return; + + case VoiceEnvelopeSegment.Sustain: + this.segment = VoiceEnvelopeSegment.Release; + this.samplesUntilNextSegment = + ((this.parameters.release <= 0 ? VoiceEnvelope.FastReleaseTime : this.parameters.release) * + outSampleRate) | 0; + + if (this.isAmpEnv) { + // I don't truly understand this; just following what LinuxSampler does. + let mysterySlope: number = -9.226 / this.samplesUntilNextSegment; + this.slope = Math.exp(mysterySlope); + this.segmentIsExponential = true; + } else { + this.slope = -this.level / this.samplesUntilNextSegment; + this.segmentIsExponential = false; + } + + return; + case VoiceEnvelopeSegment.Release: + default: + this.segment = VoiceEnvelopeSegment.Done; + this.segmentIsExponential = false; + this.level = this.slope = 0.0; + this.samplesUntilNextSegment = 0x7ffffff; + return; + } + } + } + + public setup( + newParameters: Envelope, + midiNoteNumber: number, + midiVelocity: number, + isAmpEnv: boolean, + outSampleRate: number + ): void { + this.parameters = new Envelope(newParameters); + if (this.parameters.keynumToHold > 0) { + this.parameters.hold += this.parameters.keynumToHold * (60.0 - midiNoteNumber); + this.parameters.hold = + this.parameters.hold < -10000.0 ? 0.0 : SynthHelper.timecents2Secs(this.parameters.hold); + } + + if (this.parameters.keynumToDecay > 0) { + this.parameters.decay += this.parameters.keynumToDecay * (60.0 - midiNoteNumber); + this.parameters.decay = + this.parameters.decay < -10000.0 ? 0.0 : SynthHelper.timecents2Secs(this.parameters.decay); + } + + this.midiVelocity = midiVelocity | 0; + this.isAmpEnv = isAmpEnv; + this.nextSegment(VoiceEnvelopeSegment.None, outSampleRate); + } + + public process(numSamples: number, outSampleRate: number): void { + if (this.slope > 0) { + if (this.segmentIsExponential) { + this.level *= Math.pow(this.slope, numSamples); + } else { + this.level += this.slope * numSamples; + } + } + + // tslint:disable-next-line: no-conditional-assignment + if ((this.samplesUntilNextSegment -= numSamples) <= 0) { + this.nextSegment(this.segment, outSampleRate); + } + } +} diff --git a/src/synth/synthesis/VoiceLfo.ts b/src/synth/synthesis/VoiceLfo.ts new file mode 100644 index 000000000..1d4ccd353 --- /dev/null +++ b/src/synth/synthesis/VoiceLfo.ts @@ -0,0 +1,32 @@ +// The SoundFont loading and Audio Synthesis is based on TinySoundFont, licensed under MIT, +// developed by Bernhard Schelling (https://github.com/schellingb/TinySoundFont) +// TypeScript port for alphaTab: (C) 2020 by Daniel Kuschny +// Licensed under: MPL-2.0 +import { SynthHelper } from '@src/synth/SynthHelper'; + +export class VoiceLfo { + public samplesUntil: number = 0; + public level: number = 0; + public delta: number = 0; + + public setup(delay: number, freqCents: number, outSampleRate: number): void { + this.samplesUntil = (delay * outSampleRate) | 0; + this.delta = (4.0 * SynthHelper.cents2Hertz(freqCents)) / outSampleRate; + this.level = 0; + } + + public process(blockSamples: number): void { + if (this.samplesUntil > blockSamples) { + this.samplesUntil -= blockSamples; + return; + } + this.level += this.delta * blockSamples; + if (this.level > 1.0) { + this.delta = -this.delta; + this.level = 2.0 - this.level; + } else if (this.level < -1.0) { + this.delta = -this.delta; + this.level = -2.0 - this.level; + } + } +} diff --git a/src/synth/synthesis/VoiceLowPass.ts b/src/synth/synthesis/VoiceLowPass.ts new file mode 100644 index 000000000..ebf2277d8 --- /dev/null +++ b/src/synth/synthesis/VoiceLowPass.ts @@ -0,0 +1,45 @@ +// The SoundFont loading and Audio Synthesis is based on TinySoundFont, licensed under MIT, +// developed by Bernhard Schelling (https://github.com/schellingb/TinySoundFont) +// TypeScript port for alphaTab: (C) 2020 by Daniel Kuschny +// Licensed under: MPL-2.0 +export class VoiceLowPass { + public qInv: number = 0; + public a0: number = 0; + public a1: number = 0; + public b1: number = 0; + public b2: number = 0; + public z1: number = 0; + public z2: number = 0; + public active: boolean = false; + + public constructor(other?: VoiceLowPass) { + if (other) { + this.qInv = other.qInv; + this.a0 = other.a0; + this.a1 = other.a1; + this.b1 = other.b1; + this.b2 = other.b2; + this.z1 = other.z1; + this.z2 = other.z2; + this.active = other.active; + } + } + + public setup(fc: number): void { + // Lowpass filter from http://www.earlevel.com/main/2012/11/26/biquad-c-source-code/ + let k: number = Math.tan(Math.PI * fc); + let KK: number = k * k; + let norm: number = 1 / (1 + k * this.qInv + KK); + this.a0 = KK * norm; + this.a1 = 2 * this.a0; + this.b1 = 2 * (KK - 1) * norm; + this.b2 = (1 - k * this.qInv + KK) * norm; + } + + public process(input: number): number { + let output: number = input * this.a0 + this.z1; + this.z1 = input * this.a1 + this.z2 - this.b1 * output; + this.z2 = input * this.a0 - this.b2 * output; + return output; + } +} diff --git a/src/util/FontLoadingChecker.ts b/src/util/FontLoadingChecker.ts new file mode 100644 index 000000000..ff670d6c9 --- /dev/null +++ b/src/util/FontLoadingChecker.ts @@ -0,0 +1,146 @@ +import { IEventEmitterOfT, EventEmitterOfT } from '@src/EventEmitter'; +import { Logger } from '@src/Logger'; +import { Environment } from '@src/Environment'; + +/** + * This small utility helps to detect whether a particular font is already loaded. + * @target web + */ +export class FontLoadingChecker { + private _family: string; + private _fallbackText: string; + private _isStarted: boolean = false; + public isFontLoaded: boolean = false; + + public fontLoaded: IEventEmitterOfT = new EventEmitterOfT(); + + public constructor(family: string, fallbackText: string = 'BESbwy') { + this._family = family; + this._fallbackText = fallbackText; + } + + public checkForFontAvailability(): void { + if (Environment.isRunningInWorker) { + // no web fonts in web worker + this.isFontLoaded = false; + return; + } + + if (this._isStarted) { + return; + } + + this._isStarted = true; + let failCounter: number = 0; + let failCounterId: number = window.setInterval(() => { + failCounter++; + Logger.warning( + 'Rendering', + `Could not load font '${this._family}' within ${failCounter * 5} seconds`, + null + ); + }, 5000); + + Logger.debug('Font', `Start checking for font availablility: ${this._family}`); + + if (Environment.supportsFontsApi) { + Logger.debug('Font', `[${this._family}] Font API available`); + + let checkFont = () => { + (document as any).fonts.load(`1em ${this._family}`).then(() => { + Logger.debug('Font', `[${this._family}] Font API signaled loaded`); + + if ((document as any).fonts.check('1em ' + this._family)) { + Logger.debug('Rendering', `[${this._family}] Font API signaled available`); + this.isFontLoaded = true; + window.clearInterval(failCounterId); + (this.fontLoaded as EventEmitterOfT).trigger(this._family); + } else { + Logger.debug( + 'Font', + `[${this._family}] Font API loaded reported, but font not available, checking later again`, + null + ); + window.setTimeout(() => { + checkFont(); + }, 250); + } + return true; + }); + }; + checkFont(); + } else { + Logger.debug('Font', `[${this._family}] Font API not available, using resize trick`, null); + // based on the idea of https://www.bramstein.com/writing/detecting-system-fonts-without-flash.html + // simply create 3 elements with the 3 default font families and measure them + // then change to the desired font and expect a change on the width + let sans: HTMLElement; + let serif: HTMLElement; + let monospace: HTMLElement; + let initialSansWidth: number = -1; + let initialSerifWidth: number = -1; + let initialMonospaceWidth: number = -1; + let checkFont = () => { + if (!sans) { + sans = this.createCheckerElement('sans-serif'); + serif = this.createCheckerElement('serif'); + monospace = this.createCheckerElement('monospace'); + document.body.appendChild(sans); + document.body.appendChild(serif); + document.body.appendChild(monospace); + initialSansWidth = sans.offsetWidth; + initialSerifWidth = serif.offsetWidth; + initialMonospaceWidth = monospace.offsetWidth; + sans.style.fontFamily = `'${this._family}',sans-serif`; + serif.style.fontFamily = `'${this._family}',serif`; + monospace.style.fontFamily = `'${this._family}',monospace`; + } + let sansWidth: number = sans.offsetWidth; + let serifWidth: number = serif.offsetWidth; + let monospaceWidth: number = monospace.offsetWidth; + if ( + (sansWidth !== initialSansWidth && serifWidth !== initialSerifWidth) || + (sansWidth !== initialSansWidth && monospaceWidth !== initialMonospaceWidth) || + (serifWidth !== initialSerifWidth && monospaceWidth !== initialMonospaceWidth) + ) { + if (sansWidth === serifWidth || sansWidth === monospaceWidth || serifWidth === monospaceWidth) { + document.body.removeChild(sans); + document.body.removeChild(serif); + document.body.removeChild(monospace); + this.isFontLoaded = true; + window.clearInterval(failCounterId); + (this.fontLoaded as EventEmitterOfT).trigger(this._family); + } else { + window.setTimeout(checkFont, 250); + } + } else { + window.setTimeout(checkFont, 250); + } + }; + + let readyState:string = document.readyState; + if (readyState === "complete" + || readyState === "loaded" + || readyState === "interactive") { + checkFont(); + } else { + document.addEventListener('DOMContentLoaded', () => { + checkFont(); + }); + } + } + } + + private createCheckerElement(family: string): HTMLElement { + let checkerElement: HTMLElement = document.createElement('span'); + checkerElement.style.display = 'inline-block'; + checkerElement.style.position = 'absolute'; + checkerElement.style.overflow = 'hidden'; + checkerElement.style.top = '-1000px'; + checkerElement.style.fontSize = '100px'; + checkerElement.style.fontFamily = family; + checkerElement.innerHTML = this._fallbackText; + document.body.appendChild(checkerElement); + return checkerElement; + } +} diff --git a/src/util/Lazy.ts b/src/util/Lazy.ts new file mode 100644 index 000000000..29d30ca2a --- /dev/null +++ b/src/util/Lazy.ts @@ -0,0 +1,18 @@ +/** + * @target web + */ +export class Lazy { + private _factory: () => T; + private _value: T | undefined = undefined; + + public constructor(factory: () => T) { + this._factory = factory; + } + + public get value(): T { + if (this._value === undefined) { + this._value = this._factory(); + } + return this._value; + } +} diff --git a/src/xml/XmlDocument.ts b/src/xml/XmlDocument.ts new file mode 100644 index 000000000..1adc5bc55 --- /dev/null +++ b/src/xml/XmlDocument.ts @@ -0,0 +1,41 @@ +// This XML parser is based on the XML Parser of the Haxe Standard Library (MIT) +/* + * Copyright (C)2005-2019 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +import { XmlNode, XmlNodeType } from '@src/xml/XmlNode'; +import { XmlParser } from '@src/xml/XmlParser'; + +export class XmlDocument extends XmlNode { + public documentElement: XmlNode | null = null; + + public constructor(xml: string) { + super(); + this.nodeType = XmlNodeType.Document; + XmlParser.parse(xml, 0, this); + for (let child of this.childNodes) { + if (child.nodeType === XmlNodeType.Element) { + this.documentElement = child; + break; + } + } + } +} diff --git a/src/xml/XmlError.ts b/src/xml/XmlError.ts new file mode 100644 index 000000000..fcd4180b9 --- /dev/null +++ b/src/xml/XmlError.ts @@ -0,0 +1,36 @@ +// This XML parser is based on the XML Parser of the Haxe Standard Library (MIT) +/* + * Copyright (C)2005-2019 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +import { AlphaTabError, AlphaTabErrorType } from "@src/AlphaTabError"; + +export class XmlError extends AlphaTabError { + public xml: string; + public pos: number = 0; + + public constructor(message: string, xml: string, pos: number) { + super(AlphaTabErrorType.Format, message); + this.xml = xml; + this.pos = pos; + Object.setPrototypeOf(this, XmlError.prototype); + } +} diff --git a/src/xml/XmlNode.ts b/src/xml/XmlNode.ts new file mode 100644 index 000000000..bdb94a68c --- /dev/null +++ b/src/xml/XmlNode.ts @@ -0,0 +1,106 @@ +// This XML parser is based on the XML Parser of the Haxe Standard Library (MIT) +/* + * Copyright (C)2005-2019 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +export enum XmlNodeType { + None, + Element, + Attribute, + Text, + CDATA, + EntityReference, + Entity, + ProcessingInstruction, + Comment, + Document, + DocumentType, + DocumentFragment, + Notation, + Whitespace, + SignificantWhitespace, + EndElement, + EndEntity, + XmlDeclaration +} + +export class XmlNode { + public nodeType: XmlNodeType = XmlNodeType.None; + public localName: string | null = null; + public value: string | null = null; + public childNodes: XmlNode[] = []; + public attributes: Map = new Map(); + public firstChild: XmlNode | null = null; + public firstElement: XmlNode | null = null; + + public addChild(node: XmlNode): void { + this.childNodes.push(node); + this.firstChild = node; + if (node.nodeType === XmlNodeType.Element) { + this.firstElement = node; + } + } + + public getAttribute(name: string): string { + if (this.attributes.has(name)) { + return this.attributes.get(name)!; + } + return ''; + } + + public getElementsByTagName(name: string, recursive: boolean = false): XmlNode[] { + let tags: XmlNode[] = []; + this.searchElementsByTagName(this.childNodes, tags, name, recursive); + return tags; + } + + private searchElementsByTagName(all: XmlNode[], result: XmlNode[], name: string, recursive: boolean = false): void { + for (let c of all) { + if (c && c.nodeType === XmlNodeType.Element && c.localName === name) { + result.push(c); + } + if (recursive) { + this.searchElementsByTagName(c.childNodes, result, name, true); + } + } + } + + public findChildElement(name: string): XmlNode | null { + for (let c of this.childNodes) { + if (c && c.nodeType === XmlNodeType.Element && c.localName === name) { + return c; + } + } + return null; + } + + public get innerText(): string { + if (this.nodeType === XmlNodeType.Element || this.nodeType === XmlNodeType.Document) { + let txt: string = ''; + for (let c of this.childNodes) { + txt += c.innerText?.toString(); + } + let s: string = txt; + return s.trim(); + } + return this.value ?? ''; + } +} diff --git a/src/xml/XmlParser.ts b/src/xml/XmlParser.ts new file mode 100644 index 000000000..5592cd756 --- /dev/null +++ b/src/xml/XmlParser.ts @@ -0,0 +1,446 @@ +// This XML parser is based on the XML Parser of the Haxe Standard Library (MIT) +/* + * Copyright (C)2005-2019 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +import { XmlError } from '@src/xml/XmlError'; +import { XmlNode, XmlNodeType } from '@src/xml/XmlNode'; + +enum XmlState { + IgnoreSpaces, + Begin, + BeginNode, + TagName, + Body, + AttribName, + Equals, + AttvalBegin, + AttribVal, + Childs, + Close, + WaitEnd, + WaitEndRet, + Pcdata, + Header, + Comment, + Doctype, + Cdata, + Escape +} + +export class XmlParser { + public static readonly CharCodeLF: number = 10; + public static readonly CharCodeTab: number = 9; + public static readonly CharCodeCR: number = 13; + public static readonly CharCodeSpace: number = 32; + public static readonly CharCodeLowerThan: number = 60; + public static readonly CharCodeAmp: number = 38; + public static readonly CharCodeBrackedClose: number = 93; + public static readonly CharCodeBrackedOpen: number = 91; + public static readonly CharCodeGreaterThan: number = 62; + public static readonly CharCodeExclamation: number = 33; + public static readonly CharCodeUpperD: number = 68; + public static readonly CharCodeLowerD: number = 100; + public static readonly CharCodeMinus: number = 45; + public static readonly CharCodeQuestion: number = 63; + public static readonly CharCodeSlash: number = 47; + public static readonly CharCodeEquals: number = 61; + public static readonly CharCodeDoubleQuote: number = 34; + public static readonly CharCodeSingleQuote: number = 39; + public static readonly CharCodeSharp: number = 35; + public static readonly CharCodeLowerX: number = 120; + public static readonly CharCodeLowerA: number = 97; + public static readonly CharCodeLowerZ: number = 122; + public static readonly CharCodeUpperA: number = 65; + public static readonly CharCodeUpperZ: number = 90; + public static readonly CharCode0: number = 48; + public static readonly CharCode9: number = 57; + public static readonly CharCodeColon: number = 58; + public static readonly CharCodeDot: number = 46; + public static readonly CharCodeUnderscore: number = 95; + public static readonly CharCodeSemi: number = 59; + + private static Escapes: Map = new Map([ + ['lt', '<'], + ['gt', '>'], + ['amp', '&'], + ['quot', '"'], + ['apos', "'"] + ]); + + public static parse(str: string, p: number, parent: XmlNode): number { + let c: number = str.charCodeAt(p); + let state: XmlState = XmlState.Begin; + let next: XmlState = XmlState.Begin; + let start: number = 0; + let buf: string = ''; + let escapeNext: XmlState = XmlState.Begin; + let xml: XmlNode | null = null; + let aname: string | null = null; + + let nbrackets: number = 0; + + let attrValQuote: number = 0; + + while (p < str.length) { + c = str.charCodeAt(p); + switch (state) { + case XmlState.IgnoreSpaces: + switch (c) { + case XmlParser.CharCodeLF: + case XmlParser.CharCodeCR: + case XmlParser.CharCodeTab: + case XmlParser.CharCodeSpace: + break; + default: + state = next; + continue; + } + break; + + case XmlState.Begin: + switch (c) { + case XmlParser.CharCodeLowerThan: + state = XmlState.IgnoreSpaces; + next = XmlState.BeginNode; + break; + default: + start = p; + state = XmlState.Pcdata; + continue; + } + break; + + case XmlState.Pcdata: + if (c === XmlParser.CharCodeLowerThan) { + buf += str.substr(start, p - start); + let child: XmlNode = new XmlNode(); + child.nodeType = XmlNodeType.Text; + child.value = buf; + buf = ''; + parent.addChild(child); + state = XmlState.IgnoreSpaces; + next = XmlState.BeginNode; + } else if (c === XmlParser.CharCodeAmp) { + buf += str.substr(start, p - start); + state = XmlState.Escape; + escapeNext = XmlState.Pcdata; + start = p + 1; + } + break; + + case XmlState.Cdata: + if ( + c === XmlParser.CharCodeBrackedClose && + str.charCodeAt(p + 1) === XmlParser.CharCodeBrackedClose && + str.charCodeAt(p + 2) === XmlParser.CharCodeGreaterThan + ) { + // ]]> + let child: XmlNode = new XmlNode(); + child.nodeType = XmlNodeType.CDATA; + child.value = str.substr(start, p - start); + parent.addChild(child); + p += 2; + state = XmlState.Begin; + } + break; + + case XmlState.BeginNode: + switch (c) { + case XmlParser.CharCodeExclamation: + if (str.charCodeAt(p + 1) === XmlParser.CharCodeBrackedOpen) { + p += 2; + if (str.substr(p, 6).toUpperCase() !== 'CDATA[') { + throw new XmlError('Expected ', str, p); + } + break; + + case XmlState.WaitEndRet: + switch (c) { + case XmlParser.CharCodeGreaterThan: + return p; + default: + throw new XmlError('Expected >', str, p); + } + + case XmlState.Close: + if (!XmlParser.isValidChar(c)) { + if (start === p) { + throw new XmlError('Expected node name', str, p); + } + let v: string = str.substr(start, p - start); + if (v !== parent.localName) { + throw new XmlError('Expected ', str, p); + } + state = XmlState.IgnoreSpaces; + next = XmlState.WaitEndRet; + continue; + } + break; + + case XmlState.Comment: + if ( + c === XmlParser.CharCodeMinus && + str.charCodeAt(p + 1) === XmlParser.CharCodeMinus && + str.charCodeAt(p + 2) === XmlParser.CharCodeGreaterThan + ) { + p += 2; + state = XmlState.Begin; + } + break; + + case XmlState.Doctype: + if (c === XmlParser.CharCodeBrackedOpen) { + nbrackets++; + } else if (c === XmlParser.CharCodeBrackedClose) { + nbrackets--; + } else if (c === XmlParser.CharCodeGreaterThan && nbrackets === 0) { + // > + let node: XmlNode = new XmlNode(); + node.nodeType = XmlNodeType.DocumentType; + node.value = str.substr(start, p - start); + parent.addChild(node); + state = XmlState.Begin; + } + break; + + case XmlState.Header: + if (c === XmlParser.CharCodeQuestion && str.charCodeAt(p + 1) === XmlParser.CharCodeGreaterThan) { + p++; + state = XmlState.Begin; + } + break; + + case XmlState.Escape: + if (c === XmlParser.CharCodeSemi) { + let s: string = str.substr(start, p - start); + if (s.charCodeAt(0) === XmlParser.CharCodeSharp) { + let code: number = + s.charCodeAt(1) === XmlParser.CharCodeLowerX + ? parseInt('0' + s.substr(1, s.length - 1)) + : parseInt(s.substr(1, s.length - 1)); + buf += String.fromCharCode(code); + } else if (XmlParser.Escapes.has(s)) { + buf += XmlParser.Escapes.get(s); + } else { + buf += ('&' + s + ';')?.toString(); + } + start = p + 1; + state = escapeNext; + } else if (!XmlParser.isValidChar(c) && c !== XmlParser.CharCodeSharp) { + buf += '&'; + buf += str.substr(start, p - start); + p--; + start = p + 1; + state = escapeNext; + } + break; + } + + p++; + } + + if (state === XmlState.Begin) { + start = p; + state = XmlState.Pcdata; + } + + if (state === XmlState.Pcdata) { + if (p !== start) { + buf += str.substr(start, p - start); + let node: XmlNode = new XmlNode(); + node.nodeType = XmlNodeType.Text; + node.value = buf; + parent.addChild(node); + } + return p; + } + if (state === XmlState.Escape && escapeNext === XmlState.Pcdata) { + buf += '&'; + buf += str.substr(start, p - start); + let node: XmlNode = new XmlNode(); + node.nodeType = XmlNodeType.Text; + node.value = buf; + parent.addChild(node); + return p; + } + throw new XmlError('Unexpected end', str, p); + } + + private static isValidChar(c: number): boolean { + return ( + (c >= XmlParser.CharCodeLowerA && c <= XmlParser.CharCodeLowerZ) || + (c >= XmlParser.CharCodeUpperA && c <= XmlParser.CharCodeUpperZ) || + (c >= XmlParser.CharCode0 && c <= XmlParser.CharCode9) || + c === XmlParser.CharCodeColon || + c === XmlParser.CharCodeDot || + c === XmlParser.CharCodeUnderscore || + c === XmlParser.CharCodeMinus + ); + } +} diff --git a/src/zip/HuffTools.ts b/src/zip/HuffTools.ts new file mode 100644 index 000000000..cbf091055 --- /dev/null +++ b/src/zip/HuffTools.ts @@ -0,0 +1,125 @@ +// This Inflate algorithm is based on the Inflate class of the Haxe Standard Library (MIT) +import { FormatError } from '@src/FormatError'; +/* + * Copyright (C)2005-2019 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +import { Found, Huffman, NeedBit, NeedBits } from '@src/zip/Huffman'; + +// This Inflater is based on the Zip Reader of the Haxe Standard Library (MIT) +export class HuffTools { + public static make(lengths: number[], pos: number, nlengths: number, maxbits: number): Huffman { + let counts: number[] = []; + let tmp: number[] = []; + if (maxbits > 32) { + throw new FormatError('Invalid huffman'); + } + for (let i: number = 0; i < maxbits; i++) { + counts.push(0); + tmp.push(0); + } + for (let i: number = 0; i < nlengths; i++) { + let p: number = lengths[i + pos]; + if (p >= maxbits) { + throw new FormatError('Invalid huffman'); + } + counts[p]++; + } + let code: number = 0; + for (let i: number = 1; i < maxbits - 1; i++) { + code = (code + counts[i]) << 1; + tmp[i] = code; + } + let bits: Map = new Map(); + for (let i: number = 0; i < nlengths; i++) { + let l: number = lengths[i + pos]; + if (l !== 0) { + let n: number = tmp[l - 1]; + tmp[l - 1] = n + 1; + bits.set((n << 5) | l, i); + } + } + return HuffTools.treeCompress( + new NeedBit(HuffTools.treeMake(bits, maxbits, 0, 1), HuffTools.treeMake(bits, maxbits, 1, 1)) + ); + } + + private static treeMake(bits: Map, maxbits: number, v: number, len: number): Huffman { + if (len > maxbits) { + throw new FormatError('Invalid huffman'); + } + let idx: number = (v << 5) | len; + if (bits.has(idx)) { + return new Found(bits.get(idx)!); + } + v = v << 1; + len += 1; + return new NeedBit(HuffTools.treeMake(bits, maxbits, v, len), HuffTools.treeMake(bits, maxbits, v | 1, len)); + } + + private static treeCompress(t: Huffman): Huffman { + let d: number = HuffTools.treeDepth(t); + if (d === 0) { + return t; + } + if (d === 1) { + if (t instanceof NeedBit) { + return new NeedBit(HuffTools.treeCompress(t.left), HuffTools.treeCompress(t.right)); + } else { + throw new FormatError('assert'); + } + } + let size: number = 1 << d; + let table: Huffman[] = []; + for (let i: number = 0; i < size; i++) { + table.push(new Found(-1)); + } + HuffTools.treeWalk(table, 0, 0, d, t); + return new NeedBits(d, table); + } + + private static treeWalk(table: Huffman[], p: number, cd: number, d: number, t: Huffman): void { + if (t instanceof NeedBit) { + if (d > 0) { + HuffTools.treeWalk(table, p, cd + 1, d - 1, t.left); + HuffTools.treeWalk(table, p | (1 << cd), cd + 1, d - 1, t.right); + } else { + table[p] = HuffTools.treeCompress(t); + } + } else { + table[p] = HuffTools.treeCompress(t); + } + } + + private static treeDepth(t: Huffman): number { + if (t instanceof Found) { + return 0; + } + if (t instanceof NeedBits) { + throw new FormatError('assert'); + } + if (t instanceof NeedBit) { + let da: number = HuffTools.treeDepth(t.left); + let db: number = HuffTools.treeDepth(t.right); + return 1 + (da < db ? da : db); + } + return 0; + } +} diff --git a/src/zip/Huffman.ts b/src/zip/Huffman.ts new file mode 100644 index 000000000..6ffc0894e --- /dev/null +++ b/src/zip/Huffman.ts @@ -0,0 +1,55 @@ +// This Inflate algorithm is based on the Inflate class of the Haxe Standard Library (MIT) +/* + * Copyright (C)2005-2019 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +export class Huffman {} + +export class Found extends Huffman { + public readonly n: number; + + public constructor(n: number) { + super(); + this.n = n; + } +} + +export class NeedBit extends Huffman { + public readonly left: Huffman; + public readonly right: Huffman; + + public constructor(left: Huffman, right: Huffman) { + super(); + this.left = left; + this.right = right; + } +} + +export class NeedBits extends Huffman { + public readonly n: number; + public readonly table: Huffman[]; + + public constructor(n: number, table: Huffman[]) { + super(); + this.n = n; + this.table = table; + } +} diff --git a/src/zip/Inflate.ts b/src/zip/Inflate.ts new file mode 100644 index 000000000..98a0149d9 --- /dev/null +++ b/src/zip/Inflate.ts @@ -0,0 +1,404 @@ +// This Inflate algorithm is based on the Inflate class of the Haxe Standard Library (MIT) +import { FormatError } from '@src/FormatError'; +import { IOHelper } from '@src/io/IOHelper'; +import { IReadable } from '@src/io/IReadable'; +import { + Found as HuffmanFound, + Huffman, + NeedBit as HuffmanNeedBit, + NeedBits as HuffmanNeedBits +} from '@src/zip/Huffman'; +/* + * Copyright (C)2005-2019 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +import { HuffTools } from '@src/zip/HuffTools'; + +enum InflateState { + Head, + Block, + CData, + Flat, + Crc, + Dist, + DistOne, + Done +} + +class InflateWindow { + private static readonly Size: number = 1 << 15; + private static readonly BufferSize: number = 1 << 16; + + public buffer: Uint8Array = new Uint8Array(InflateWindow.BufferSize); + public pos: number = 0; + + public slide(): void { + let b: Uint8Array = new Uint8Array(InflateWindow.BufferSize); + this.pos -= InflateWindow.Size; + b.set(this.buffer.subarray(InflateWindow.Size, InflateWindow.Size + this.pos), 0); + this.buffer = b; + } + + public addBytes(b: Uint8Array, p: number, len: number): void { + if (this.pos + len > InflateWindow.BufferSize) { + this.slide(); + } + this.buffer.set(b.subarray(p, p + len), this.pos); + this.pos += len; + } + + public addByte(c: number): void { + if (this.pos === InflateWindow.BufferSize) { + this.slide(); + } + this.buffer[this.pos] = c; + this.pos++; + } + + public getLastChar(): number { + return this.buffer[this.pos - 1]; + } + + public available(): number { + return this.pos; + } +} + +export class Inflate { + // prettier-ignore + private static LenExtraBitsTbl: number[] = [ + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, -1, + -1 + ]; + // prettier-ignore + private static LenBaseValTbl: number[] = [ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, + 131, 163, 195, 227, 258 + ]; + // prettier-ignore + private static DistExtraBitsTbl: number[] = [ + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, + 13, 13, -1, -1 + ]; + // prettier-ignore + private static DistBaseValTbl: number[] = [ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, + 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 + ]; + // prettier-ignore + private static CodeLengthsPos: number[] = [ + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 + ]; + + private static _fixedHuffman: Huffman = Inflate.buildFixedHuffman(); + + private static buildFixedHuffman(): Huffman { + let a: number[] = []; + for (let n: number = 0; n < 288; n++) { + a.push(n <= 143 ? 8 : n <= 255 ? 9 : n <= 279 ? 7 : 8); + } + return HuffTools.make(a, 0, 288, 10); + } + + private _nbits: number = 0; + private _bits: number = 0; + private _state: InflateState = InflateState.Block; + private _isFinal: boolean = false; + private _huffman: Huffman = Inflate._fixedHuffman; + private _huffdist: Huffman | null = null; + private _len: number = 0; + private _dist: number = 0; + private _needed: number = 0; + private _output: Uint8Array | null = null; + private _outpos: number = 0; + private _input: IReadable; + private _lengths: number[] = []; + private _window: InflateWindow = new InflateWindow(); + + public constructor(readable: IReadable) { + this._input = readable; + for (let i: number = 0; i < 19; i++) { + this._lengths.push(-1); + } + } + + public readBytes(b: Uint8Array, pos: number, len: number): number { + this._needed = len; + this._outpos = pos; + this._output = b; + if (len > 0) { + while (this.inflateLoop()) { + // inflating... + } + } + return len - this._needed; + } + + private inflateLoop(): boolean { + switch (this._state) { + case InflateState.Head: + let cmf: number = this._input.readByte(); + let cm: number = cmf & 15; + if (cm !== 8) { + throw new FormatError('Invalid data'); + } + let flg: number = this._input.readByte(); + // var fcheck = flg & 31; + let fdict: boolean = (flg & 32) !== 0; + // var flevel = flg >> 6; + if (((cmf << 8) + flg) % 31 !== 0) { + throw new FormatError('Invalid data'); + } + if (fdict) { + throw new FormatError('Unsupported dictionary'); + } + this._state = InflateState.Block; + return true; + case InflateState.Crc: + this._state = InflateState.Done; + return true; + case InflateState.Done: + // nothing + return false; + case InflateState.Block: + this._isFinal = this.getBit(); + switch (this.getBits(2)) { + case 0: + this._len = IOHelper.readUInt16LE(this._input); + let nlen: number = IOHelper.readUInt16LE(this._input); + if (nlen !== 0xffff - this._len) { + throw new FormatError('Invalid data'); + } + this._state = InflateState.Flat; + let r: boolean = this.inflateLoop(); + this.resetBits(); + return r; + case 1: + this._huffman = Inflate._fixedHuffman; + this._huffdist = null; + this._state = InflateState.CData; + return true; + case 2: + let hlit: number = this.getBits(5) + 257; + let hdist: number = this.getBits(5) + 1; + let hclen: number = this.getBits(4) + 4; + for (let i: number = 0; i < hclen; i++) { + this._lengths[Inflate.CodeLengthsPos[i]] = this.getBits(3); + } + for (let i: number = hclen; i < 19; i++) { + this._lengths[Inflate.CodeLengthsPos[i]] = 0; + } + this._huffman = HuffTools.make(this._lengths, 0, 19, 8); + let xlengths: number[] = []; + for (let i: number = 0; i < hlit + hdist; i++) { + xlengths.push(0); + } + this.inflateLengths(xlengths, hlit + hdist); + this._huffdist = HuffTools.make(xlengths, hlit, hdist, 16); + this._huffman = HuffTools.make(xlengths, 0, hlit, 16); + this._state = InflateState.CData; + return true; + default: + throw new FormatError('Invalid data'); + } + case InflateState.Flat: { + let rlen: number = this._len < this._needed ? this._len : this._needed; + let bytes: Uint8Array = IOHelper.readByteArray(this._input, rlen); + this._len -= rlen; + this.addBytes(bytes, 0, rlen); + if (this._len === 0) this._state = this._isFinal ? InflateState.Crc : InflateState.Block; + return this._needed > 0; + } + case InflateState.DistOne: { + let rlen: number = this._len < this._needed ? this._len : this._needed; + this.addDistOne(rlen); + this._len -= rlen; + if (this._len === 0) { + this._state = InflateState.CData; + } + return this._needed > 0; + } + case InflateState.Dist: + while (this._len > 0 && this._needed > 0) { + let rdist: number = this._len < this._dist ? this._len : this._dist; + let rlen: number = this._needed < rdist ? this._needed : rdist; + this.addDist(this._dist, rlen); + this._len -= rlen; + } + if (this._len === 0) { + this._state = InflateState.CData; + } + return this._needed > 0; + case InflateState.CData: + let n: number = this.applyHuffman(this._huffman); + if (n < 256) { + this.addByte(n); + return this._needed > 0; + } else if (n === 256) { + this._state = this._isFinal ? InflateState.Crc : InflateState.Block; + return true; + } else { + n = (n - 257) & 0xff; + let extraBits: number = Inflate.LenExtraBitsTbl[n]; + if (extraBits === -1) { + throw new FormatError('Invalid data'); + } + this._len = Inflate.LenBaseValTbl[n] + this.getBits(extraBits); + let huffdist: Huffman | null = this._huffdist; + let distCode: number = !huffdist ? this.getRevBits(5) : this.applyHuffman(huffdist); + extraBits = Inflate.DistExtraBitsTbl[distCode]; + if (extraBits === -1) { + throw new FormatError('Invalid data'); + } + this._dist = Inflate.DistBaseValTbl[distCode] + this.getBits(extraBits); + if (this._dist > this._window.available()) { + throw new FormatError('Invalid data'); + } + this._state = this._dist === 1 ? InflateState.DistOne : InflateState.Dist; + return true; + } + } + return false; + } + + private addDistOne(n: number): void { + let c: number = this._window.getLastChar(); + for (let i: number = 0; i < n; i++) { + this.addByte(c); + } + } + + private addByte(b: number): void { + this._window.addByte(b); + this._output![this._outpos] = b; + this._needed--; + this._outpos++; + } + + private addDist(d: number, len: number): void { + this.addBytes(this._window.buffer, this._window.pos - d, len); + } + + private getBit(): boolean { + if (this._nbits === 0) { + this._nbits = 8; + this._bits = this._input.readByte(); + } + let b: boolean = (this._bits & 1) === 1; + this._nbits--; + this._bits = this._bits >> 1; + return b; + } + + private getBits(n: number): number { + while (this._nbits < n) { + this._bits = this._bits | (this._input.readByte() << this._nbits); + this._nbits += 8; + } + let b: number = this._bits & ((1 << n) - 1); + this._nbits -= n; + this._bits = this._bits >> n; + return b; + } + + private getRevBits(n: number): number { + return n === 0 ? 0 : this.getBit() ? (1 << (n - 1)) | this.getRevBits(n - 1) : this.getRevBits(n - 1); + } + + private resetBits(): void { + this._bits = 0; + this._nbits = 0; + } + + private addBytes(b: Uint8Array, p: number, len: number): void { + this._window.addBytes(b, p, len); + this._output!.set(b.subarray(p, p + len), this._outpos); + this._needed -= len; + this._outpos += len; + } + + private inflateLengths(a: number[], max: number): void { + let i: number = 0; + let prev: number = 0; + while (i < max) { + let n: number = this.applyHuffman(this._huffman); + switch (n) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + prev = n; + a[i] = n; + i++; + break; + case 16: + let end: number = i + 3 + this.getBits(2); + if (end > max) { + throw new FormatError('Invalid data'); + } + while (i < end) { + a[i] = prev; + i++; + } + break; + case 17: + i += 3 + this.getBits(3); + if (i > max) { + throw new FormatError('Invalid data'); + } + break; + case 18: + i += 11 + this.getBits(7); + if (i > max) { + throw new FormatError('Invalid data'); + } + break; + default: { + throw new FormatError('Invalid data'); + } + } + } + } + + private applyHuffman(h: Huffman): number { + if (h instanceof HuffmanFound) { + return h.n; + } + if (h instanceof HuffmanNeedBit) { + return this.applyHuffman(this.getBit() ? h.right : h.left); + } + if (h instanceof HuffmanNeedBits) { + return this.applyHuffman(h.table[this.getBits(h.n)]); + } + throw new FormatError('Invalid data'); + } +} diff --git a/src/zip/ZipReader.ts b/src/zip/ZipReader.ts new file mode 100644 index 000000000..86fcbf41a --- /dev/null +++ b/src/zip/ZipReader.ts @@ -0,0 +1,101 @@ +import { ByteBuffer } from '@src/io/ByteBuffer'; +import { IOHelper } from '@src/io/IOHelper'; +import { IReadable } from '@src/io/IReadable'; +import { Inflate } from '@src/zip/Inflate'; + +export class ZipEntry { + public readonly fullName: string; + public readonly fileName: string; + public readonly data: Uint8Array; + + public constructor(fullName: string, data: Uint8Array) { + this.fullName = fullName; + let i: number = fullName.lastIndexOf('/'); + this.fileName = i === -1 || i === fullName.length - 1 ? this.fullName : fullName.substr(i + 1); + this.data = data; + } +} + +export class ZipReader { + private static readonly OptionalDataDescriptorSignature: number = 0x08074b50; + private static readonly CompressionMethodDeflate: number = 8; + private static readonly LocalFileHeaderSignature: number = 0x04034b50; + + private _readable: IReadable; + + public constructor(readable: IReadable) { + this._readable = readable; + } + + public read(): ZipEntry[] { + let entries: ZipEntry[] = []; + while (true) { + let e: ZipEntry | null = this.readEntry(); + if (!e) { + break; + } + entries.push(e); + } + return entries; + } + + private readEntry(): ZipEntry | null { + let readable: IReadable = this._readable; + let h: number = IOHelper.readInt32LE(readable); + if (h !== ZipReader.LocalFileHeaderSignature) { + return null; + } + // 4.3.7 local file header + IOHelper.readUInt16LE(readable); // version + + let flags: number = IOHelper.readUInt16LE(readable); + let compressionMethod: number = IOHelper.readUInt16LE(readable); + let compressed: boolean = compressionMethod !== 0; + if (compressed && compressionMethod !== ZipReader.CompressionMethodDeflate) { + return null; + } + + IOHelper.readInt16LE(this._readable); // lastModFileTime + IOHelper.readInt16LE(this._readable); // lastModFileDate + IOHelper.readInt32LE(readable); // crc32 + IOHelper.readInt32LE(readable); // compressed size + + let uncompressedSize: number = IOHelper.readInt32LE(readable); + let fileNameLength: number = IOHelper.readInt16LE(readable); + let extraFieldLength: number = IOHelper.readInt16LE(readable); + let fname: string = IOHelper.toString(IOHelper.readByteArray(readable, fileNameLength), 'utf-8'); + readable.skip(extraFieldLength); + + // 4.3.8 File Data + let data: Uint8Array; + if (compressed) { + let target: ByteBuffer = ByteBuffer.empty(); + let z: Inflate = new Inflate(this._readable); + let buffer: Uint8Array = new Uint8Array(65536); + while (true) { + let bytes: number = z.readBytes(buffer, 0, buffer.length); + target.write(buffer, 0, bytes); + if (bytes < buffer.length) { + break; + } + } + data = target.toArray(); + } else { + data = IOHelper.readByteArray(this._readable, uncompressedSize); + } + + // 4.3.9 Data Descriptor + // 4.3.9.1 + if ((flags & 8) !== 0) { + let crc32: number = IOHelper.readInt32LE(this._readable); + // 4.3.9.3 + if (crc32 === ZipReader.OptionalDataDescriptorSignature) { + IOHelper.readInt32LE(this._readable); // real crc + } + IOHelper.readInt32LE(this._readable); // compressed size + IOHelper.readInt32LE(this._readable); // uncompressed size + } + + return new ZipEntry(fname, data); + } +} diff --git a/test-data/audio/audio-generation.gp b/test-data/audio/audio-generation.gp new file mode 100644 index 000000000..1499e00df Binary files /dev/null and b/test-data/audio/audio-generation.gp differ diff --git a/test-data/audio/audio-generation.pcm b/test-data/audio/audio-generation.pcm new file mode 100644 index 000000000..acabfa1fe Binary files /dev/null and b/test-data/audio/audio-generation.pcm differ diff --git a/test-data/audio/default.sf2 b/test-data/audio/default.sf2 new file mode 100644 index 000000000..bd4cc714c Binary files /dev/null and b/test-data/audio/default.sf2 differ diff --git a/Documentation/input/assets/files/NightWish.gp5 b/test-data/audio/full-song.gp5 similarity index 100% rename from Documentation/input/assets/files/NightWish.gp5 rename to test-data/audio/full-song.gp5 diff --git a/Source/AlphaTab.Test/TestFiles/Audio/GraceBeats.gp b/test-data/audio/grace-beats.gp similarity index 100% rename from Source/AlphaTab.Test/TestFiles/Audio/GraceBeats.gp rename to test-data/audio/grace-beats.gp diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro5/RepeatCloseAlternateEndings.gp5 b/test-data/audio/repeat-close-alternate-endings.gp5 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro5/RepeatCloseAlternateEndings.gp5 rename to test-data/audio/repeat-close-alternate-endings.gp5 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro5/RepeatCloseMulti.gp5 b/test-data/audio/repeat-close-multi.gp5 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro5/RepeatCloseMulti.gp5 rename to test-data/audio/repeat-close-multi.gp5 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro5/RepeatCloseWithoutStartAtBeginning.gp5 b/test-data/audio/repeat-close-without-start-at-beginning.gp5 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro5/RepeatCloseWithoutStartAtBeginning.gp5 rename to test-data/audio/repeat-close-without-start-at-beginning.gp5 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro5/RepeatClose.gp5 b/test-data/audio/repeat-close.gp5 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro5/RepeatClose.gp5 rename to test-data/audio/repeat-close.gp5 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro3/TestAccentuations.gp3 b/test-data/guitarpro3/accentuations.gp3 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro3/TestAccentuations.gp3 rename to test-data/guitarpro3/accentuations.gp3 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro3/TestBends.gp3 b/test-data/guitarpro3/bends.gp3 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro3/TestBends.gp3 rename to test-data/guitarpro3/bends.gp3 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro3/TestDead.gp3 b/test-data/guitarpro3/dead.gp3 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro3/TestDead.gp3 rename to test-data/guitarpro3/dead.gp3 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro3/Effects.gp3 b/test-data/guitarpro3/effects.gp3 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro3/Effects.gp3 rename to test-data/guitarpro3/effects.gp3 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro3/TestGrace.gp3 b/test-data/guitarpro3/grace.gp3 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro3/TestGrace.gp3 rename to test-data/guitarpro3/grace.gp3 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro3/TestHammer.gp3 b/test-data/guitarpro3/hammer.gp3 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro3/TestHammer.gp3 rename to test-data/guitarpro3/hammer.gp3 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro3/TestHarmonics.gp3 b/test-data/guitarpro3/harmonics.gp3 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro3/TestHarmonics.gp3 rename to test-data/guitarpro3/harmonics.gp3 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro3/Test02.gp3 b/test-data/guitarpro3/notes.gp3 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro3/Test02.gp3 rename to test-data/guitarpro3/notes.gp3 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro3/TestOtherEffects.gp3 b/test-data/guitarpro3/other-effects.gp3 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro3/TestOtherEffects.gp3 rename to test-data/guitarpro3/other-effects.gp3 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro3/TestRanges.gp3 b/test-data/guitarpro3/ranges.gp3 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro3/TestRanges.gp3 rename to test-data/guitarpro3/ranges.gp3 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro3/Test01.gp3 b/test-data/guitarpro3/score-info.gp3 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro3/Test01.gp3 rename to test-data/guitarpro3/score-info.gp3 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro3/TestSlides.gp3 b/test-data/guitarpro3/slides.gp3 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro3/TestSlides.gp3 rename to test-data/guitarpro3/slides.gp3 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro3/TestStrings.gp3 b/test-data/guitarpro3/strings.gp3 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro3/TestStrings.gp3 rename to test-data/guitarpro3/strings.gp3 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro3/TestStrokes.gp3 b/test-data/guitarpro3/strokes.gp3 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro3/TestStrokes.gp3 rename to test-data/guitarpro3/strokes.gp3 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro3/Test03.gp3 b/test-data/guitarpro3/time-signatures.gp3 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro3/Test03.gp3 rename to test-data/guitarpro3/time-signatures.gp3 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro3/TestTuplets.gp3 b/test-data/guitarpro3/tuplets.gp3 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro3/TestTuplets.gp3 rename to test-data/guitarpro3/tuplets.gp3 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro3/TestVibrato.gp3 b/test-data/guitarpro3/vibrato.gp3 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro3/TestVibrato.gp3 rename to test-data/guitarpro3/vibrato.gp3 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro4/TestAccentuations.gp4 b/test-data/guitarpro4/accentuations.gp4 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro4/TestAccentuations.gp4 rename to test-data/guitarpro4/accentuations.gp4 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro4/TestBends.gp4 b/test-data/guitarpro4/bends.gp4 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro4/TestBends.gp4 rename to test-data/guitarpro4/bends.gp4 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro4/Colors.gp4 b/test-data/guitarpro4/colors.gp4 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro4/Colors.gp4 rename to test-data/guitarpro4/colors.gp4 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro4/TestDead.gp4 b/test-data/guitarpro4/dead.gp4 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro4/TestDead.gp4 rename to test-data/guitarpro4/dead.gp4 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro4/Effects.gp4 b/test-data/guitarpro4/effects.gp4 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro4/Effects.gp4 rename to test-data/guitarpro4/effects.gp4 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro4/FadeToBlack.gp4 b/test-data/guitarpro4/fade-to-black.gp4 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro4/FadeToBlack.gp4 rename to test-data/guitarpro4/fade-to-black.gp4 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro4/TestFingering.gp4 b/test-data/guitarpro4/fingering.gp4 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro4/TestFingering.gp4 rename to test-data/guitarpro4/fingering.gp4 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro4/TestGrace.gp4 b/test-data/guitarpro4/grace.gp4 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro4/TestGrace.gp4 rename to test-data/guitarpro4/grace.gp4 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro4/TestHammer.gp4 b/test-data/guitarpro4/hammer.gp4 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro4/TestHammer.gp4 rename to test-data/guitarpro4/hammer.gp4 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro4/TestHarmonics.gp4 b/test-data/guitarpro4/harmonics.gp4 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro4/TestHarmonics.gp4 rename to test-data/guitarpro4/harmonics.gp4 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro4/Test02.gp4 b/test-data/guitarpro4/notes.gp4 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro4/Test02.gp4 rename to test-data/guitarpro4/notes.gp4 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro4/TestOtherEffects.gp4 b/test-data/guitarpro4/other-effects.gp4 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro4/TestOtherEffects.gp4 rename to test-data/guitarpro4/other-effects.gp4 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro4/TestRanges.gp4 b/test-data/guitarpro4/ranges.gp4 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro4/TestRanges.gp4 rename to test-data/guitarpro4/ranges.gp4 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro4/Test01.gp4 b/test-data/guitarpro4/score-info.gp4 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro4/Test01.gp4 rename to test-data/guitarpro4/score-info.gp4 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro4/TestSlides.gp4 b/test-data/guitarpro4/slides.gp4 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro4/TestSlides.gp4 rename to test-data/guitarpro4/slides.gp4 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro4/TestStrings.gp4 b/test-data/guitarpro4/strings.gp4 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro4/TestStrings.gp4 rename to test-data/guitarpro4/strings.gp4 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro4/TestStrokes.gp4 b/test-data/guitarpro4/strokes.gp4 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro4/TestStrokes.gp4 rename to test-data/guitarpro4/strokes.gp4 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro4/Test03.gp4 b/test-data/guitarpro4/time-signatures.gp4 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro4/Test03.gp4 rename to test-data/guitarpro4/time-signatures.gp4 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro4/TestTremolo.gp4 b/test-data/guitarpro4/tremolo.gp4 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro4/TestTremolo.gp4 rename to test-data/guitarpro4/tremolo.gp4 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro4/TestTrills.gp4 b/test-data/guitarpro4/trills.gp4 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro4/TestTrills.gp4 rename to test-data/guitarpro4/trills.gp4 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro4/TestTuplets.gp4 b/test-data/guitarpro4/tuplets.gp4 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro4/TestTuplets.gp4 rename to test-data/guitarpro4/tuplets.gp4 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro4/TestVibrato.gp4 b/test-data/guitarpro4/vibrato.gp4 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro4/TestVibrato.gp4 rename to test-data/guitarpro4/vibrato.gp4 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro5/TestAccentuations.gp5 b/test-data/guitarpro5/accentuations.gp5 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro5/TestAccentuations.gp5 rename to test-data/guitarpro5/accentuations.gp5 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro5/TestBends.gp5 b/test-data/guitarpro5/bends.gp5 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro5/TestBends.gp5 rename to test-data/guitarpro5/bends.gp5 diff --git a/Documentation/input/assets/files/Canon.gp5 b/test-data/guitarpro5/canon.gp5 similarity index 100% rename from Documentation/input/assets/files/Canon.gp5 rename to test-data/guitarpro5/canon.gp5 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro5/TestChords.gp5 b/test-data/guitarpro5/chords.gp5 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro5/TestChords.gp5 rename to test-data/guitarpro5/chords.gp5 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro5/Colors.gp5 b/test-data/guitarpro5/colors.gp5 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro5/Colors.gp5 rename to test-data/guitarpro5/colors.gp5 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro5/TestDead.gp5 b/test-data/guitarpro5/dead.gp5 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro5/TestDead.gp5 rename to test-data/guitarpro5/dead.gp5 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro5/Effects.gp5 b/test-data/guitarpro5/effects.gp5 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro5/Effects.gp5 rename to test-data/guitarpro5/effects.gp5 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro5/TestFingering.gp5 b/test-data/guitarpro5/fingering.gp5 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro5/TestFingering.gp5 rename to test-data/guitarpro5/fingering.gp5 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro5/TestGrace.gp5 b/test-data/guitarpro5/grace.gp5 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro5/TestGrace.gp5 rename to test-data/guitarpro5/grace.gp5 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro5/TestHammer.gp5 b/test-data/guitarpro5/hammer.gp5 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro5/TestHammer.gp5 rename to test-data/guitarpro5/hammer.gp5 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro5/TestHarmonics.gp5 b/test-data/guitarpro5/harmonics.gp5 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro5/TestHarmonics.gp5 rename to test-data/guitarpro5/harmonics.gp5 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro5/TestKeySignatures.gp5 b/test-data/guitarpro5/key-signatures.gp5 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro5/TestKeySignatures.gp5 rename to test-data/guitarpro5/key-signatures.gp5 diff --git a/test-data/guitarpro5/nightwish.gp5 b/test-data/guitarpro5/nightwish.gp5 new file mode 100644 index 000000000..c4322cfcd Binary files /dev/null and b/test-data/guitarpro5/nightwish.gp5 differ diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro5/Test02.gp5 b/test-data/guitarpro5/notes.gp5 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro5/Test02.gp5 rename to test-data/guitarpro5/notes.gp5 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro5/TestOtherEffects.gp5 b/test-data/guitarpro5/other-effects.gp5 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro5/TestOtherEffects.gp5 rename to test-data/guitarpro5/other-effects.gp5 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro5/TestRanges.gp5 b/test-data/guitarpro5/ranges.gp5 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro5/TestRanges.gp5 rename to test-data/guitarpro5/ranges.gp5 diff --git a/test-data/guitarpro5/repeat-close-alternate-endings.gp5 b/test-data/guitarpro5/repeat-close-alternate-endings.gp5 new file mode 100644 index 000000000..67da62945 Binary files /dev/null and b/test-data/guitarpro5/repeat-close-alternate-endings.gp5 differ diff --git a/test-data/guitarpro5/repeat-close-multi.gp5 b/test-data/guitarpro5/repeat-close-multi.gp5 new file mode 100644 index 000000000..f9d5c6e71 Binary files /dev/null and b/test-data/guitarpro5/repeat-close-multi.gp5 differ diff --git a/test-data/guitarpro5/repeat-close-without-start-at-beginning.gp5 b/test-data/guitarpro5/repeat-close-without-start-at-beginning.gp5 new file mode 100644 index 000000000..956b98ab9 Binary files /dev/null and b/test-data/guitarpro5/repeat-close-without-start-at-beginning.gp5 differ diff --git a/test-data/guitarpro5/repeat-close.gp5 b/test-data/guitarpro5/repeat-close.gp5 new file mode 100644 index 000000000..0ec62e484 Binary files /dev/null and b/test-data/guitarpro5/repeat-close.gp5 differ diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro5/Test01.gp5 b/test-data/guitarpro5/score-info.gp5 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro5/Test01.gp5 rename to test-data/guitarpro5/score-info.gp5 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro5/Serenade.gp5 b/test-data/guitarpro5/serenade.gp5 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro5/Serenade.gp5 rename to test-data/guitarpro5/serenade.gp5 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro5/TestSlides.gp5 b/test-data/guitarpro5/slides.gp5 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro5/TestSlides.gp5 rename to test-data/guitarpro5/slides.gp5 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro5/TestStrings.gp5 b/test-data/guitarpro5/strings.gp5 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro5/TestStrings.gp5 rename to test-data/guitarpro5/strings.gp5 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro5/TestStrokes.gp5 b/test-data/guitarpro5/strokes.gp5 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro5/TestStrokes.gp5 rename to test-data/guitarpro5/strokes.gp5 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro5/Test03.gp5 b/test-data/guitarpro5/time-signatures.gp5 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro5/Test03.gp5 rename to test-data/guitarpro5/time-signatures.gp5 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro5/TestTremolo.gp5 b/test-data/guitarpro5/tremolo.gp5 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro5/TestTremolo.gp5 rename to test-data/guitarpro5/tremolo.gp5 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro5/TestTrills.gp5 b/test-data/guitarpro5/trills.gp5 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro5/TestTrills.gp5 rename to test-data/guitarpro5/trills.gp5 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro5/TestTuplets.gp5 b/test-data/guitarpro5/tuplets.gp5 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro5/TestTuplets.gp5 rename to test-data/guitarpro5/tuplets.gp5 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro5/TestVibrato.gp5 b/test-data/guitarpro5/vibrato.gp5 similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro5/TestVibrato.gp5 rename to test-data/guitarpro5/vibrato.gp5 diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro6/TestAccentuations.gpx b/test-data/guitarpro6/accentuations.gpx similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro6/TestAccentuations.gpx rename to test-data/guitarpro6/accentuations.gpx diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro6/TestBends.gpx b/test-data/guitarpro6/bends.gpx similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro6/TestBends.gpx rename to test-data/guitarpro6/bends.gpx diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro6/TestChords.gpx b/test-data/guitarpro6/chords.gpx similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro6/TestChords.gpx rename to test-data/guitarpro6/chords.gpx diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro6/Colors.gpx b/test-data/guitarpro6/colors.gpx similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro6/Colors.gpx rename to test-data/guitarpro6/colors.gpx diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro6/TestDead.gpx b/test-data/guitarpro6/dead.gpx similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro6/TestDead.gpx rename to test-data/guitarpro6/dead.gpx diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro6/Effects.gpx b/test-data/guitarpro6/effects.gpx similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro6/Effects.gpx rename to test-data/guitarpro6/effects.gpx diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro6/Compressed.gpx b/test-data/guitarpro6/file-system-compressed.gpx similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro6/Compressed.gpx rename to test-data/guitarpro6/file-system-compressed.gpx diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro6/TestFingering.gpx b/test-data/guitarpro6/fingering.gpx similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro6/TestFingering.gpx rename to test-data/guitarpro6/fingering.gpx diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro6/TestGrace.gpx b/test-data/guitarpro6/grace.gpx similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro6/TestGrace.gpx rename to test-data/guitarpro6/grace.gpx diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro6/TestHammer.gpx b/test-data/guitarpro6/hammer.gpx similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro6/TestHammer.gpx rename to test-data/guitarpro6/hammer.gpx diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro6/TestHarmonics.gpx b/test-data/guitarpro6/harmonics.gpx similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro6/TestHarmonics.gpx rename to test-data/guitarpro6/harmonics.gpx diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro6/TestKeySignatures.gpx b/test-data/guitarpro6/key-signatures.gpx similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro6/TestKeySignatures.gpx rename to test-data/guitarpro6/key-signatures.gpx diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro6/LyricsTemplate.gpx b/test-data/guitarpro6/lyrics-template.gpx similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro6/LyricsTemplate.gpx rename to test-data/guitarpro6/lyrics-template.gpx diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro6/Test02.gpx b/test-data/guitarpro6/notes.gpx similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro6/Test02.gpx rename to test-data/guitarpro6/notes.gpx diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro6/TestOtherEffects.gpx b/test-data/guitarpro6/other-effects.gpx similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro6/TestOtherEffects.gpx rename to test-data/guitarpro6/other-effects.gpx diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro6/TestRanges.gpx b/test-data/guitarpro6/ranges.gpx similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro6/TestRanges.gpx rename to test-data/guitarpro6/ranges.gpx diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro6/RepeatCloseAlternateEndings.gpx b/test-data/guitarpro6/repeat-close-alternate-endings.gpx similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro6/RepeatCloseAlternateEndings.gpx rename to test-data/guitarpro6/repeat-close-alternate-endings.gpx diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro6/RepeatCloseMulti.gpx b/test-data/guitarpro6/repeat-close-multi.gpx similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro6/RepeatCloseMulti.gpx rename to test-data/guitarpro6/repeat-close-multi.gpx diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro6/RepeatClose.gpx b/test-data/guitarpro6/repeat-close.gpx similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro6/RepeatClose.gpx rename to test-data/guitarpro6/repeat-close.gpx diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro6/Test01.gpx b/test-data/guitarpro6/score-info.gpx similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro6/Test01.gpx rename to test-data/guitarpro6/score-info.gpx diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro6/Serenade.gpx b/test-data/guitarpro6/serenade.gpx similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro6/Serenade.gpx rename to test-data/guitarpro6/serenade.gpx diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro6/TestSlides.gpx b/test-data/guitarpro6/slides.gpx similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro6/TestSlides.gpx rename to test-data/guitarpro6/slides.gpx diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro6/TestStrings.gpx b/test-data/guitarpro6/strings.gpx similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro6/TestStrings.gpx rename to test-data/guitarpro6/strings.gpx diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro6/TestStrokes.gpx b/test-data/guitarpro6/strokes.gpx similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro6/TestStrokes.gpx rename to test-data/guitarpro6/strokes.gpx diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro6/Test03.gpx b/test-data/guitarpro6/time-signatures.gpx similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro6/Test03.gpx rename to test-data/guitarpro6/time-signatures.gpx diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro6/TestTremolo.gpx b/test-data/guitarpro6/tremolo.gpx similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro6/TestTremolo.gpx rename to test-data/guitarpro6/tremolo.gpx diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro6/TestTrills.gpx b/test-data/guitarpro6/trills.gpx similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro6/TestTrills.gpx rename to test-data/guitarpro6/trills.gpx diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro6/TestTuplets.gpx b/test-data/guitarpro6/tuplets.gpx similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro6/TestTuplets.gpx rename to test-data/guitarpro6/tuplets.gpx diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro6/TestVibrato.gpx b/test-data/guitarpro6/vibrato.gpx similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro6/TestVibrato.gpx rename to test-data/guitarpro6/vibrato.gpx diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro7/BinaryStylesheet b/test-data/guitarpro7/BinaryStylesheet similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro7/BinaryStylesheet rename to test-data/guitarpro7/BinaryStylesheet diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro7/TestAccentuations.gp b/test-data/guitarpro7/accentuations.gp similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro7/TestAccentuations.gp rename to test-data/guitarpro7/accentuations.gp diff --git a/test-data/guitarpro7/anacrusis.gp b/test-data/guitarpro7/anacrusis.gp new file mode 100644 index 000000000..bd18c1c24 Binary files /dev/null and b/test-data/guitarpro7/anacrusis.gp differ diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro7/BendsAdvanced.gp b/test-data/guitarpro7/bends-advanced.gp similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro7/BendsAdvanced.gp rename to test-data/guitarpro7/bends-advanced.gp diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro7/TestBends.gp b/test-data/guitarpro7/bends.gp similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro7/TestBends.gp rename to test-data/guitarpro7/bends.gp diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro7/TestChords.gp b/test-data/guitarpro7/chords.gp similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro7/TestChords.gp rename to test-data/guitarpro7/chords.gp diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro7/Colors.gp b/test-data/guitarpro7/colors.gp similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro7/Colors.gp rename to test-data/guitarpro7/colors.gp diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro7/Compressed.gp b/test-data/guitarpro7/compressed.gp similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro7/Compressed.gp rename to test-data/guitarpro7/compressed.gp diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro7/TestDead.gp b/test-data/guitarpro7/dead.gp similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro7/TestDead.gp rename to test-data/guitarpro7/dead.gp diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro7/Effects.gp b/test-data/guitarpro7/effects.gp similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro7/Effects.gp rename to test-data/guitarpro7/effects.gp diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro7/TestFermata.gp b/test-data/guitarpro7/fermata.gp similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro7/TestFermata.gp rename to test-data/guitarpro7/fermata.gp diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro7/TestFingering.gp b/test-data/guitarpro7/fingering.gp similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro7/TestFingering.gp rename to test-data/guitarpro7/fingering.gp diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro7/TestGrace.gp b/test-data/guitarpro7/grace.gp similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro7/TestGrace.gp rename to test-data/guitarpro7/grace.gp diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro7/TestHammer.gp b/test-data/guitarpro7/hammer.gp similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro7/TestHammer.gp rename to test-data/guitarpro7/hammer.gp diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro7/TestHarmonics.gp b/test-data/guitarpro7/harmonics.gp similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro7/TestHarmonics.gp rename to test-data/guitarpro7/harmonics.gp diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro7/TestKeySignatures.gp b/test-data/guitarpro7/key-signatures.gp similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro7/TestKeySignatures.gp rename to test-data/guitarpro7/key-signatures.gp diff --git a/test-data/guitarpro7/left-hand-tap.gp b/test-data/guitarpro7/left-hand-tap.gp new file mode 100644 index 000000000..0e8ecc042 Binary files /dev/null and b/test-data/guitarpro7/left-hand-tap.gp differ diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro7/LyricsTemplate.gp b/test-data/guitarpro7/lyrics-template.gp similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro7/LyricsTemplate.gp rename to test-data/guitarpro7/lyrics-template.gp diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro7/Test02.gp b/test-data/guitarpro7/notes.gp similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro7/Test02.gp rename to test-data/guitarpro7/notes.gp diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro7/TestOtherEffects.gp b/test-data/guitarpro7/other-effects.gp similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro7/TestOtherEffects.gp rename to test-data/guitarpro7/other-effects.gp diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro7/TestOttavia.gp b/test-data/guitarpro7/ottavia.gp similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro7/TestOttavia.gp rename to test-data/guitarpro7/ottavia.gp diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro7/TestPickSlide.gp b/test-data/guitarpro7/pick-slide.gp similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro7/TestPickSlide.gp rename to test-data/guitarpro7/pick-slide.gp diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro7/TestRanges.gp b/test-data/guitarpro7/ranges.gp similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro7/TestRanges.gp rename to test-data/guitarpro7/ranges.gp diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro7/RepeatCloseAlternateEndings.gp b/test-data/guitarpro7/repeat-close-alternate-endings.gp similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro7/RepeatCloseAlternateEndings.gp rename to test-data/guitarpro7/repeat-close-alternate-endings.gp diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro7/RepeatCloseMulti.gp b/test-data/guitarpro7/repeat-close-multi.gp similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro7/RepeatCloseMulti.gp rename to test-data/guitarpro7/repeat-close-multi.gp diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro7/RepeatClose.gp b/test-data/guitarpro7/repeat-close.gp similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro7/RepeatClose.gp rename to test-data/guitarpro7/repeat-close.gp diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro7/Test01.gp b/test-data/guitarpro7/score-info.gp similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro7/Test01.gp rename to test-data/guitarpro7/score-info.gp diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro7/Serenade.gp b/test-data/guitarpro7/serenade.gp similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro7/Serenade.gp rename to test-data/guitarpro7/serenade.gp diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro7/TestSimileMark.gp b/test-data/guitarpro7/simile-mark.gp similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro7/TestSimileMark.gp rename to test-data/guitarpro7/simile-mark.gp diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro7/TestSlides.gp b/test-data/guitarpro7/slides.gp similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro7/TestSlides.gp rename to test-data/guitarpro7/slides.gp diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro7/TestStrings.gp b/test-data/guitarpro7/strings.gp similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro7/TestStrings.gp rename to test-data/guitarpro7/strings.gp diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro7/TestStrokes.gp b/test-data/guitarpro7/strokes.gp similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro7/TestStrokes.gp rename to test-data/guitarpro7/strokes.gp diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro7/Test03.gp b/test-data/guitarpro7/time-signatures.gp similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro7/Test03.gp rename to test-data/guitarpro7/time-signatures.gp diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro7/TestTremoloVibrato.gp b/test-data/guitarpro7/tremolo-vibrato.gp similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro7/TestTremoloVibrato.gp rename to test-data/guitarpro7/tremolo-vibrato.gp diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro7/TestTremolo.gp b/test-data/guitarpro7/tremolo.gp similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro7/TestTremolo.gp rename to test-data/guitarpro7/tremolo.gp diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro7/TestTrills.gp b/test-data/guitarpro7/trills.gp similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro7/TestTrills.gp rename to test-data/guitarpro7/trills.gp diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro7/TestTuplets.gp b/test-data/guitarpro7/tuplets.gp similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro7/TestTuplets.gp rename to test-data/guitarpro7/tuplets.gp diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro7/TestVibrato.gp b/test-data/guitarpro7/vibrato.gp similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro7/TestVibrato.gp rename to test-data/guitarpro7/vibrato.gp diff --git a/Source/AlphaTab.Test/TestFiles/GuitarPro7/WhammyAdvanced.gp b/test-data/guitarpro7/whammy-advanced.gp similarity index 100% rename from Source/AlphaTab.Test/TestFiles/GuitarPro7/WhammyAdvanced.gp rename to test-data/guitarpro7/whammy-advanced.gp diff --git a/test-data/lyrics/template.gpx b/test-data/lyrics/template.gpx new file mode 100644 index 000000000..8825dabba Binary files /dev/null and b/test-data/lyrics/template.gpx differ diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/BeetAnGeSample.mxl b/test-data/musicxml-samples/BeetAnGeSample.mxl similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/BeetAnGeSample.mxl rename to test-data/musicxml-samples/BeetAnGeSample.mxl diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/BeetAnGeSample.pdf b/test-data/musicxml-samples/BeetAnGeSample.pdf similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/BeetAnGeSample.pdf rename to test-data/musicxml-samples/BeetAnGeSample.pdf diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/BeetAnGeSample.xml b/test-data/musicxml-samples/BeetAnGeSample.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/BeetAnGeSample.xml rename to test-data/musicxml-samples/BeetAnGeSample.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/Binchois.mxl b/test-data/musicxml-samples/Binchois.mxl similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/Binchois.mxl rename to test-data/musicxml-samples/Binchois.mxl diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/Binchois.png b/test-data/musicxml-samples/Binchois.png similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/Binchois.png rename to test-data/musicxml-samples/Binchois.png diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/Binchois.xml b/test-data/musicxml-samples/Binchois.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/Binchois.xml rename to test-data/musicxml-samples/Binchois.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/BrahWiMeSample.mxl b/test-data/musicxml-samples/BrahWiMeSample.mxl similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/BrahWiMeSample.mxl rename to test-data/musicxml-samples/BrahWiMeSample.mxl diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/BrahWiMeSample.pdf b/test-data/musicxml-samples/BrahWiMeSample.pdf similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/BrahWiMeSample.pdf rename to test-data/musicxml-samples/BrahWiMeSample.pdf diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/BrahWiMeSample.xml b/test-data/musicxml-samples/BrahWiMeSample.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/BrahWiMeSample.xml rename to test-data/musicxml-samples/BrahWiMeSample.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/BrookeWestSample.mxl b/test-data/musicxml-samples/BrookeWestSample.mxl similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/BrookeWestSample.mxl rename to test-data/musicxml-samples/BrookeWestSample.mxl diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/BrookeWestSample.pdf b/test-data/musicxml-samples/BrookeWestSample.pdf similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/BrookeWestSample.pdf rename to test-data/musicxml-samples/BrookeWestSample.pdf diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/BrookeWestSample.xml b/test-data/musicxml-samples/BrookeWestSample.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/BrookeWestSample.xml rename to test-data/musicxml-samples/BrookeWestSample.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/Chant.mxl b/test-data/musicxml-samples/Chant.mxl similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/Chant.mxl rename to test-data/musicxml-samples/Chant.mxl diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/Chant.png b/test-data/musicxml-samples/Chant.png similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/Chant.png rename to test-data/musicxml-samples/Chant.png diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/Chant.xml b/test-data/musicxml-samples/Chant.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/Chant.xml rename to test-data/musicxml-samples/Chant.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/DebuMandSample.mxl b/test-data/musicxml-samples/DebuMandSample.mxl similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/DebuMandSample.mxl rename to test-data/musicxml-samples/DebuMandSample.mxl diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/DebuMandSample.pdf b/test-data/musicxml-samples/DebuMandSample.pdf similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/DebuMandSample.pdf rename to test-data/musicxml-samples/DebuMandSample.pdf diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/DebuMandSample.xml b/test-data/musicxml-samples/DebuMandSample.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/DebuMandSample.xml rename to test-data/musicxml-samples/DebuMandSample.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/Dichterliebe01.mxl b/test-data/musicxml-samples/Dichterliebe01.mxl similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/Dichterliebe01.mxl rename to test-data/musicxml-samples/Dichterliebe01.mxl diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/Dichterliebe01.pdf b/test-data/musicxml-samples/Dichterliebe01.pdf similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/Dichterliebe01.pdf rename to test-data/musicxml-samples/Dichterliebe01.pdf diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/Dichterliebe01.xml b/test-data/musicxml-samples/Dichterliebe01.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/Dichterliebe01.xml rename to test-data/musicxml-samples/Dichterliebe01.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/Echigo-Jishi.mxl b/test-data/musicxml-samples/Echigo.mxl similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/Echigo-Jishi.mxl rename to test-data/musicxml-samples/Echigo.mxl diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/Echigo-Jishi.pdf b/test-data/musicxml-samples/Echigo.pdf similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/Echigo-Jishi.pdf rename to test-data/musicxml-samples/Echigo.pdf diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/Echigo-Jishi.xml b/test-data/musicxml-samples/Echigo.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/Echigo-Jishi.xml rename to test-data/musicxml-samples/Echigo.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/FaurReveSample.mxl b/test-data/musicxml-samples/FaurReveSample.mxl similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/FaurReveSample.mxl rename to test-data/musicxml-samples/FaurReveSample.mxl diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/FaurReveSample.pdf b/test-data/musicxml-samples/FaurReveSample.pdf similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/FaurReveSample.pdf rename to test-data/musicxml-samples/FaurReveSample.pdf diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/FaurReveSample.xml b/test-data/musicxml-samples/FaurReveSample.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/FaurReveSample.xml rename to test-data/musicxml-samples/FaurReveSample.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/MahlFaGe4Sample.mxl b/test-data/musicxml-samples/MahlFaGe4Sample.mxl similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/MahlFaGe4Sample.mxl rename to test-data/musicxml-samples/MahlFaGe4Sample.mxl diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/MahlFaGe4Sample.pdf b/test-data/musicxml-samples/MahlFaGe4Sample.pdf similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/MahlFaGe4Sample.pdf rename to test-data/musicxml-samples/MahlFaGe4Sample.pdf diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/MahlFaGe4Sample.xml b/test-data/musicxml-samples/MahlFaGe4Sample.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/MahlFaGe4Sample.xml rename to test-data/musicxml-samples/MahlFaGe4Sample.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/MozaChloSample.mxl b/test-data/musicxml-samples/MozaChloSample.mxl similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/MozaChloSample.mxl rename to test-data/musicxml-samples/MozaChloSample.mxl diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/MozaChloSample.pdf b/test-data/musicxml-samples/MozaChloSample.pdf similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/MozaChloSample.pdf rename to test-data/musicxml-samples/MozaChloSample.pdf diff --git a/test-data/musicxml-samples/MozaChloSample.xml b/test-data/musicxml-samples/MozaChloSample.xml new file mode 100644 index 000000000..bc37a66a6 --- /dev/null +++ b/test-data/musicxml-samples/MozaChloSample.xml @@ -0,0 +1,3994 @@ + + + + + K. 524 + An Chloe (Page 1) + + + Wolfgang Amadeus Mozart + Johann Georg Jacobi + Copyright © 2002 Recordare LLC + + Finale 2011 for Windows + Dolet 6.0 for Finale + 2011-08-08 + + + + + + + 6.35 + 40 + + + 1760 + 1360 + + 80 + 80 + 80 + 80 + + + + + 0 + 0 + + 130 + 70 + + + 80 + + + 0.8333 + 5 + 1.25 + 1.875 + 5 + 1.875 + 0.8333 + 0.8333 + 1.25 + 0.8333 + 60 + 60 + 60 + 8 + + + + + + + page number + 2 + + + rights + Copyright © 2002 Recordare LLC + + + title + An Chloe + + + K. 524 + + + lyricist + Johann Georg Jacobi + + + composer + Wolfgang Amadeus Mozart + + + + Voice + + Voice + + + 1 + 53 + 80 + 0 + + + + Piano + + Acoustic Grand Piano + + + 2 + 1 + 80 + 0 + + + + + + + + system + + + 4 + + -3 + major + + + + G + 2 + + + + + + 4 + 1 + + + + + + + + 16 + 1 + + + + + + + + 16 + 1 + + + + + + + + 16 + 1 + + + + + + + + 16 + 1 + + + + + + + 110 + + + + + + + + 16 + 1 + + + + + + + + 8 + 1 + half + + + + 4 + 1 + quarter + + + + G + 4 + + 2 + 1 + eighth + up + begin + + single + Wenn + + + + + A + -1 + 4 + + 2 + 1 + eighth + up + end + + single + die + + + + + + + + + B + -1 + 4 + + 4 + 1 + quarter + down + + single + Lieb’ + + + + + B + -1 + 4 + + 4 + 1 + quarter + down + + single + aus + + + + + B + -1 + 4 + + 4 + 1 + quarter + down + + begin + dei + + + + + B + -1 + 4 + + 4 + 1 + quarter + down + + end + nen + + + + + + + + + B + -1 + 4 + + 6 + 1 + quarter + + down + + + + + begin + blau + + + + + E + -1 + 5 + + 2 + 1 + eighth + down + + + + + + + E + -1 + 5 + + 4 + 1 + quarter + down + + end + en, + + + + + 4 + 1 + quarter + + + + + + + + G + 4 + + 6 + 1 + quarter + + up + + + + + begin + hel + + + + + B + -1 + 4 + + 2 + 1 + eighth + down + + + + + + + B + -1 + 4 + + 4 + 1 + quarter + down + + end + len, + + + + + 4 + 1 + quarter + + + + + + + 110 + + + + + E + -1 + 4 + + 8 + + 1 + half + up + + + + + begin + off + + + + + E + -1 + 4 + + 2 + + 1 + eighth + up + begin + + + + + + + F + 4 + + 2 + 1 + eighth + up + continue + + end + nen + + + + + G + 4 + + 2 + 1 + eighth + up + continue + + begin + Au + + + + + A + -1 + 4 + + 2 + 1 + eighth + up + end + + end + gen + + + + + + + + + G + 4 + + 4 + 1 + quarter + up + + + + + single + sieht, + + + + + + F + 4 + + 4 + 1 + quarter + up + + + + + + + 4 + 1 + quarter + + + + G + 4 + + 2 + 1 + eighth + up + begin + + single + und + + + + + A + -1 + 4 + + 2 + 1 + eighth + up + end + + single + vor + + + + + + + + + B + -1 + 4 + + 4 + 1 + quarter + down + + single + Lust + + + + + B + -1 + 4 + + 4 + 1 + quarter + down + + begin + hin + + + + + B + -1 + 4 + + 4 + 1 + quarter + down + + end + ein + + + + + B + -1 + 4 + + 4 + 1 + quarter + down + + single + zu + + + + + + + + + B + -1 + 4 + + 6 + 1 + quarter + + down + + + + + begin + schau + + + + + E + -1 + 5 + + 2 + 1 + eighth + down + + + + + + + E + -1 + 5 + + 4 + 1 + quarter + down + + end + en + + + + + 4 + 1 + quarter + + + + + + + 110 + + + + + G + 4 + + 8 + + 1 + half + up + + + + + single + mir’s + + + + + + G + 4 + + 2 + + 1 + eighth + up + begin + + + + + + + + A + -1 + 4 + + 2 + 1 + eighth + up + continue + + + + + + + A + 4 + + 2 + 1 + eighth + natural + up + continue + + + + + single + im + + + + + + B + -1 + 4 + + 2 + 1 + eighth + up + end + + + + + + + + + + + E + -1 + 4 + + 4 + 1 + quarter + up + + begin + Her + + + + + E + -1 + 4 + + 4 + 1 + quarter + up + + end + zen + + + + + G + 4 + + 3 + 1 + eighth + + up + begin + + + + + single + klopft + + + + + + A + -1 + 4 + + 1 + 1 + 16th + flat + up + continue + backward hook + + + + + + + F + 4 + + 3 + 1 + eighth + + up + continue + + + + + single + und + + + + + + G + 4 + + 1 + 1 + 16th + up + end + backward hook + + + + + + + + + + + E + -1 + 4 + + 4 + 1 + quarter + up + + single + glüht; + + + + + 4 + 1 + quarter + + + + 4 + 1 + quarter + + + + G + 4 + + 2 + 1 + eighth + up + begin + + single + und + + + + + A + 4 + + 2 + 1 + eighth + natural + up + end + + single + ich + + + + + + + + + B + -1 + 4 + + 4 + 1 + quarter + down + + begin + hal + + + + + F + 4 + + 4 + 1 + quarter + up + + end + te + + + + + C + 5 + + 4 + 1 + quarter + down + + single + dich + + + + + F + 4 + + 4 + 1 + quarter + up + + single + und + + + + + + + + + + + 120 + 0 + + 230 + + + 60 + + none + + + 24 + + -3 + major + + + 2 + + G + 2 + + + F + 4 + + + + + Allegretto + + 1 + + + + + +

    + + + 1 + + + + + G + 4 + + 12 + 1 + eighth + up + 1 + begin + + + + + + + A + -1 + 4 + + 12 + 1 + eighth + up + 1 + end + + + + + + 24 + + + + 24 + 3 + quarter + 2 + + + + + + + + B + -1 + 4 + + 24 + 1 + quarter + down + 1 + + + + B + -1 + 4 + + 24 + 1 + quarter + down + 1 + + + + B + -1 + 4 + + 24 + 1 + quarter + down + 1 + + + + B + -1 + 4 + + 24 + 1 + quarter + down + 1 + + + 96 + + + + +

    + + + 2 + + + + + E + -1 + 3 + + 12 + 3 + eighth + down + 2 + begin + + + + + + + B + -1 + 3 + + 12 + 3 + eighth + down + 2 + continue + + + + G + 3 + + 12 + 3 + eighth + down + 2 + continue + + + + B + -1 + 3 + + 12 + 3 + eighth + down + 2 + end + + + + D + 3 + + 12 + 3 + eighth + down + 2 + begin + + + + A + -1 + 3 + + 12 + 3 + eighth + down + 2 + continue + + + + F + 3 + + 12 + 3 + eighth + down + 2 + continue + + + + A + -1 + 3 + + 12 + 3 + eighth + down + 2 + end + + + + + + + + + + + B + -1 + 4 + + 24 + 1 + quarter + down + 1 + + + + + natural + + + + + + E + -1 + 5 + + 24 + 1 + quarter + down + 1 + + + + + + + 48 + 1 + half + 1 + + + 96 + + + + E + -1 + 3 + + 12 + 3 + eighth + down + 2 + begin + + + + + + + B + -1 + 3 + + 12 + 3 + eighth + down + 2 + continue + + + + G + 3 + + 12 + 3 + eighth + down + 2 + continue + + + + B + -1 + 3 + + 12 + 3 + eighth + down + 2 + end + + + + E + -1 + 3 + + 12 + 3 + eighth + down + 2 + begin + + + + B + -1 + 3 + + 12 + 3 + eighth + down + 2 + continue + + + + G + 3 + + 12 + 3 + eighth + down + 2 + continue + + + + B + -1 + 3 + + 12 + 3 + eighth + down + 2 + end + + + + + + + + + + + G + 4 + + 24 + 1 + quarter + up + 1 + + + + + sharp + + + + + + B + -1 + 4 + + 24 + 1 + quarter + down + 1 + + + + + + + 48 + 1 + half + 1 + + + 96 + + + + E + -1 + 3 + + 12 + 3 + eighth + down + 2 + begin + + + + + + + B + -1 + 3 + + 12 + 3 + eighth + down + 2 + continue + + + + G + 3 + + 12 + 3 + eighth + down + 2 + continue + + + + B + -1 + 3 + + 12 + 3 + eighth + down + 2 + end + + + + E + -1 + 3 + + 12 + 3 + eighth + down + 2 + begin + + + + B + -1 + 3 + + 12 + 3 + eighth + down + 2 + continue + + + + G + 3 + + 12 + 3 + eighth + down + 2 + continue + + + + B + -1 + 3 + + 12 + 3 + eighth + down + 2 + end + + + + + + + + + + + + + + + 1 + + + + + E + -1 + 4 + + 36 + 1 + quarter + + up + 1 + + + + + + + + + F + 4 + + 6 + 1 + 16th + up + 1 + begin + begin + + + + G + 4 + + 6 + 1 + 16th + up + 1 + end + end + + + + A + -1 + 4 + + 6 + 1 + 16th + down + 1 + begin + begin + + + + B + -1 + 4 + + 6 + 1 + 16th + down + 1 + continue + continue + + + + C + 5 + + 6 + 1 + 16th + down + 1 + continue + continue + + + + D + 5 + + 6 + 1 + 16th + down + 1 + end + end + + + + E + -1 + 5 + + 6 + 1 + 16th + down + 1 + begin + begin + + + + F + 5 + + 6 + 1 + 16th + down + 1 + continue + continue + + + + G + 5 + + 6 + 1 + 16th + down + 1 + continue + continue + + + + A + -1 + 5 + + 6 + 1 + 16th + down + 1 + end + end + + + 96 + + + + B + -1 + 2 + + 12 + 3 + eighth + down + 2 + begin + + + + + + + G + 3 + + 12 + 3 + eighth + down + 2 + continue + + + + E + -1 + 3 + + 12 + 3 + eighth + down + 2 + continue + + + + G + 3 + + 12 + 3 + eighth + down + 2 + end + + + + B + -1 + 2 + + 12 + 3 + eighth + down + 2 + begin + + + + G + 3 + + 12 + 3 + eighth + down + 2 + continue + + + + E + -1 + 3 + + 12 + 3 + eighth + down + 2 + continue + + + + G + 3 + + 12 + 3 + eighth + down + 2 + end + + + + + + + + + + 70 + + + + + B + -1 + 5 + + 8 + 1 + eighth + + 3 + 2 + + down + 1 + begin + + + + + + + + G + 5 + + 8 + 1 + eighth + + 3 + 2 + + down + 1 + continue + + + + E + -1 + 5 + + 8 + 1 + eighth + + 3 + 2 + + down + 1 + end + + + + + + + + B + -1 + 4 + + 8 + 1 + eighth + + 3 + 2 + + up + 1 + begin + + + + + + + + G + 4 + + 8 + 1 + eighth + + 3 + 2 + + up + 1 + continue + + + + E + -1 + 4 + + 8 + 1 + eighth + + 3 + 2 + + up + 1 + end + + + + + + + + G + 4 + + 12 + 1 + eighth + up + 1 + + + + 12 + 1 + eighth + 1 + + + + +

    + + + -2 + 1 + + + + + A + -1 + 3 + + 12 + 1 + eighth + up + 1 + + + + + B + -1 + 3 + + 12 + 1 + eighth + up + 1 + + + + + D + 4 + + 12 + 1 + eighth + up + 1 + + + + + F + 4 + + 12 + 1 + eighth + up + 1 + + + + 12 + 1 + eighth + 1 + + + 96 + + + + B + -1 + 2 + + 24 + 3 + quarter + up + 2 + + + + 24 + 3 + quarter + 2 + + + + 24 + 3 + quarter + 2 + + + + B + -1 + 2 + + 12 + 3 + eighth + up + 2 + + + + 12 + 3 + eighth + 2 + + + + + + + + A + -1 + 3 + + 48 + 1 + half + up + 1 + + + + + + + + B + -1 + 3 + + 48 + + 1 + half + up + 1 + + + + + + + + D + 4 + + 48 + 1 + half + up + 1 + + + + + F + 4 + + 48 + 1 + half + up + 1 + + + + G + 3 + + 24 + 1 + quarter + up + 1 + + + + + + + + B + -1 + 3 + + 24 + + 1 + quarter + up + 1 + + + + + + + + E + -1 + 4 + + 24 + 1 + quarter + up + 1 + + + + 24 + 1 + quarter + 1 + + + 96 + + + + E + -1 + 2 + + 48 + + 3 + half + up + 2 + + + + + + + + E + -1 + 3 + + 48 + + 3 + half + up + 2 + + + + + + + E + -1 + 2 + + 24 + + 3 + quarter + up + 2 + + + + + + + + E + -1 + 3 + + 24 + + 3 + quarter + up + 2 + + + + + + + 24 + 3 + quarter + 2 + + + + + + + + G + 3 + + 12 + 1 + eighth + up + 1 + begin + + + + + + + B + -1 + 3 + + 12 + 1 + eighth + up + 1 + continue + + + + E + -1 + 4 + + 12 + 1 + eighth + up + 1 + continue + + + + B + -1 + 3 + + 12 + 1 + eighth + up + 1 + end + + + + A + -1 + 3 + + 12 + 1 + eighth + up + 1 + begin + + + + B + -1 + 3 + + 12 + 1 + eighth + up + 1 + continue + + + + F + 4 + + 12 + 1 + eighth + up + 1 + continue + + + + B + -1 + 3 + + 12 + 1 + eighth + up + 1 + end + + + + + + 96 + + + + E + -1 + 2 + + 48 + 3 + half + up + 2 + + + + + E + -1 + 3 + + 48 + 3 + half + up + 2 + + + + D + 2 + + 48 + 3 + half + up + 2 + + + + + D + 3 + + 48 + 3 + half + up + 2 + + + + + + + + G + 3 + + 12 + 1 + eighth + up + 1 + begin + + + + + + + B + -1 + 3 + + 12 + 1 + eighth + up + 1 + continue + + + + E + -1 + 4 + + 12 + 1 + eighth + up + 1 + continue + + + + B + -1 + 3 + + 12 + 1 + eighth + up + 1 + end + + + + G + 3 + + 12 + 1 + eighth + up + 1 + begin + + + + B + -1 + 3 + + 12 + 1 + eighth + up + 1 + continue + + + + E + -1 + 4 + + 12 + 1 + eighth + up + 1 + continue + + + + B + -1 + 3 + + 12 + 1 + eighth + up + 1 + end + + + + + + 96 + + + + E + -1 + 2 + + 24 + 3 + quarter + up + 2 + + + + + E + -1 + 3 + + 24 + 3 + quarter + up + 2 + + + + 24 + 3 + quarter + 2 + + + + 48 + 3 + half + 2 + + + + + + + + G + 3 + + 12 + 1 + eighth + up + 1 + begin + + + + + + + B + -1 + 3 + + 12 + 1 + eighth + up + 1 + continue + + + + E + -1 + 4 + + 12 + 1 + eighth + up + 1 + continue + + + + B + -1 + 3 + + 12 + 1 + eighth + up + 1 + end + + + + G + 3 + + 12 + 1 + eighth + up + 1 + begin + + + + B + -1 + 3 + + 12 + 1 + eighth + up + 1 + continue + + + + E + -1 + 4 + + 12 + 1 + eighth + up + 1 + continue + + + + B + -1 + 3 + + 12 + 1 + eighth + up + 1 + end + + + + + + 96 + + + + E + -1 + 2 + + 24 + 3 + quarter + up + 2 + + + + + E + -1 + 3 + + 24 + 3 + quarter + up + 2 + + + + 24 + 3 + quarter + 2 + + + + 48 + 3 + half + 2 + + + + + + + 70 + + + + + E + -1 + 4 + + 12 + 1 + eighth + up + 1 + begin + + + + + + + B + -1 + 3 + + 12 + 1 + eighth + up + 1 + continue + + + + G + 3 + + 12 + 1 + eighth + up + 1 + continue + + + + B + -1 + 3 + + 12 + 1 + eighth + up + 1 + end + + + + E + -1 + 4 + + 12 + 1 + eighth + up + 1 + begin + + + + D + 4 + + 12 + 1 + eighth + up + 1 + continue + + + + + F + 4 + + 12 + 1 + eighth + up + 1 + + + + E + -1 + 4 + + 12 + 1 + eighth + up + 1 + continue + + + + + G + 4 + + 12 + 1 + eighth + up + 1 + + + + F + 4 + + 12 + 1 + eighth + up + 1 + end + + + + + + + + A + -1 + 4 + + 12 + 1 + eighth + up + 1 + + + 96 + + + + G + 2 + + 48 + 3 + half + up + 2 + + + + + + + E + -1 + 2 + + 48 + 3 + half + up + 2 + + + + + + + + + + + E + -1 + 4 + + 24 + 1 + quarter + up + 1 + + + + + + + + G + 4 + + 24 + 1 + quarter + up + 1 + + + + D + 4 + + 24 + 1 + quarter + up + 1 + + + + + + + + F + 4 + + 24 + 1 + quarter + up + 1 + + + + 48 + 1 + half + 1 + + + 96 + + + + B + -1 + 2 + + 24 + 3 + quarter + up + 2 + + + + B + -1 + 3 + + 24 + 3 + quarter + down + 2 + + + + B + -1 + 2 + + 24 + 3 + quarter + up + 2 + + + + 24 + 3 + quarter + 2 + + + + + + + + G + 3 + + 12 + 1 + eighth + up + 1 + begin + + + + + + + B + -1 + 3 + + 12 + 1 + eighth + up + 1 + continue + + + + E + -1 + 4 + + 12 + 1 + eighth + up + 1 + continue + + + + B + -1 + 3 + + 12 + 1 + eighth + up + 1 + end + + + + A + -1 + 3 + + 12 + 1 + eighth + up + 1 + begin + + + + B + -1 + 3 + + 12 + 1 + eighth + up + 1 + continue + + + + F + 4 + + 12 + 1 + eighth + up + 1 + continue + + + + B + -1 + 3 + + 12 + 1 + eighth + up + 1 + end + + + + + + 96 + + + + E + -1 + 2 + + 48 + 3 + half + up + 2 + + + + + E + -1 + 3 + + 48 + 3 + half + up + 2 + + + + D + 2 + + 48 + 3 + half + up + 2 + + + + + D + 3 + + 48 + 3 + half + up + 2 + + + + + + + + G + 3 + + 12 + 1 + eighth + up + 1 + begin + + + + + + + B + -1 + 3 + + 12 + 1 + eighth + up + 1 + continue + + + + E + -1 + 4 + + 12 + 1 + eighth + up + 1 + continue + + + + B + -1 + 3 + + 12 + 1 + eighth + up + 1 + end + + + + G + 3 + + 12 + 1 + eighth + up + 1 + begin + + + + B + -1 + 3 + + 12 + 1 + eighth + up + 1 + continue + + + + E + -1 + 4 + + 12 + 1 + eighth + up + 1 + continue + + + + B + -1 + 3 + + 12 + 1 + eighth + up + 1 + end + + + + + + 96 + + + + E + -1 + 2 + + 24 + 3 + quarter + up + 2 + + + + + E + -1 + 3 + + 24 + 3 + quarter + up + 2 + + + + 24 + 3 + quarter + 2 + + + + 48 + 3 + half + 2 + + + + + + + 60 + + + + + G + 3 + + 12 + 1 + eighth + up + 1 + begin + + + + + + + B + -1 + 3 + + 12 + 1 + eighth + up + 1 + continue + + + + E + -1 + 4 + + 12 + 1 + eighth + up + 1 + continue + + + + B + -1 + 3 + + 12 + 1 + eighth + up + 1 + end + + + + G + 3 + + 12 + 1 + eighth + up + 1 + begin + + + + B + -1 + 3 + + 12 + 1 + eighth + up + 1 + continue + + + + E + -1 + 4 + + 12 + 1 + eighth + up + 1 + continue + + + + B + -1 + 3 + + 12 + 1 + eighth + up + 1 + end + + + + + + 96 + + + + E + -1 + 2 + + 24 + 3 + quarter + up + 2 + + + + + E + -1 + 3 + + 24 + 3 + quarter + up + 2 + + + + 24 + 3 + quarter + 2 + + + + 48 + 3 + half + 2 + + + + + + + + G + 3 + + 12 + 1 + eighth + up + 1 + begin + + + + + + + B + -1 + 3 + + 12 + 1 + eighth + up + 1 + continue + + + + E + -1 + 4 + + 12 + 1 + eighth + up + 1 + continue + + + + B + -1 + 3 + + 12 + 1 + eighth + up + 1 + end + + + + + + + B + -1 + 3 + + 12 + 1 + eighth + up + 1 + + + + + E + -1 + 4 + + 12 + 1 + eighth + up + 1 + + + + 12 + 1 + eighth + 1 + + + + A + -1 + 3 + + 12 + 1 + eighth + up + 1 + + + + + D + 4 + + 12 + 1 + eighth + up + 1 + + + + 12 + 1 + eighth + 1 + + + 96 + + + + B + -1 + 1 + + 48 + 3 + half + up + 2 + + + + + B + -1 + 2 + + 48 + 3 + half + up + 2 + + + + 24 + 3 + quarter + 2 + + + + B + -1 + 2 + + 12 + 3 + eighth + up + 2 + + + + 12 + 3 + eighth + 2 + + + + + + + + G + 3 + + 24 + 1 + quarter + up + 1 + + + + + E + -1 + 4 + + 24 + 1 + quarter + up + 1 + + + + 24 + 1 + quarter + 1 + + + + 48 + 1 + half + 1 + + + 96 + + + + E + -1 + 3 + + 24 + 3 + quarter + down + 2 + + + + B + -1 + 2 + + 24 + 3 + quarter + up + 2 + + + + E + -1 + 2 + + 24 + 3 + quarter + up + 2 + + + + 24 + 3 + quarter + 2 + + + + + + + + B + -1 + 3 + + 12 + 1 + eighth + up + 1 + begin + + + + + + + D + 4 + + 12 + 1 + eighth + up + 1 + continue + + + + F + 4 + + 12 + 1 + eighth + up + 1 + continue + + + + D + 4 + + 12 + 1 + eighth + up + 1 + end + + + + + + + C + 4 + + 12 + 1 + eighth + up + 1 + begin + + + + + + + E + -1 + 4 + + 12 + 1 + eighth + up + 1 + continue + + + + F + 4 + + 12 + 1 + eighth + up + 1 + continue + + + + E + -1 + 4 + + 12 + 1 + eighth + up + 1 + end + + + + + + 96 + + + + D + 2 + + 48 + 3 + half + up + 2 + + + + + D + 3 + + 48 + 3 + half + up + 2 + + + + A + 1 + + 48 + 3 + half + natural + up + 2 + + + + + A + 2 + + 48 + 3 + half + natural + up + 2 + + + + + diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/MozaVeilSample.mxl b/test-data/musicxml-samples/MozaVeilSample.mxl similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/MozaVeilSample.mxl rename to test-data/musicxml-samples/MozaVeilSample.mxl diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/MozaVeilSample.pdf b/test-data/musicxml-samples/MozaVeilSample.pdf similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/MozaVeilSample.pdf rename to test-data/musicxml-samples/MozaVeilSample.pdf diff --git a/test-data/musicxml-samples/MozaVeilSample.xml b/test-data/musicxml-samples/MozaVeilSample.xml new file mode 100644 index 000000000..ff23c4ed5 --- /dev/null +++ b/test-data/musicxml-samples/MozaVeilSample.xml @@ -0,0 +1,4833 @@ + + + + + K. 476 + Das Veilchen (Page 1) + + + Wolfgang Amadeus Mozart + Johann Wolfgang von Goethe + Copyright © 2002 Recordare LLC + + Finale 2011 for Windows + Dolet 6.0 for Finale + 2011-08-08 + + + + + + + 6.35 + 40 + + + 1760 + 1360 + + 80 + 80 + 80 + 80 + + + + + 0 + 0 + + 130 + 70 + + + 80 + + + 0.8333 + 5 + 1.25 + 1.875 + 5 + 1.875 + 1.25 + 0.8333 + 1.25 + 0.8333 + 60 + 60 + 100 + 8 + + + + + + + page number + 2 + + + rights + Copyright © 2002 Recordare LLC + + + title + Das Veilchen + + + K. 476 + + + lyricist + Johann Wolfgang von Goethe + + + composer + Wolfgang Amadeus Mozart + + + + Voice + + Voice + + + 1 + 53 + 80 + 0 + + + + Piano + + Piano + + + 2 + 1 + 80 + 0 + + + + + + + + system + + + 4 + + 1 + major + + + + G + 2 + + + + + + 2 + 1 + + + + + + + + 8 + 1 + + + + + + + + 8 + 1 + + + + + + + + 8 + 1 + + + + + + + + 8 + 1 + + + + + + + + 8 + 1 + + + + + + + 100 + + + + + + + + 8 + 1 + + + + + + + + 4 + 1 + quarter + + + + 2 + 1 + eighth + + + + G + 4 + + 2 + 1 + eighth + up + + single + Ein + + + + + + + + + D + 5 + + 2 + 1 + eighth + down + begin + + begin + Veil + + + + + B + 4 + + 2 + 1 + eighth + down + continue + + end + chen + + + + + C + 5 + + 2 + 1 + eighth + down + continue + + single + auf + + + + + E + 5 + + 2 + 1 + eighth + down + end + + single + der + + + + + + + + + + E + 5 + + 1 + eighth + up + + + + + + + D + 5 + + 3 + 1 + eighth + + down + begin + + + + + begin + Wie + + + + + C + 5 + + 1 + 1 + 16th + down + end + backward hook + + end + se + + + + + B + 4 + + 2 + 1 + eighth + down + + single + stand, + + + + + 1 + 1 + 16th + + + + G + 5 + + 1 + 1 + 16th + down + + begin + ge + + + + + + + + + + G + 5 + + 1 + eighth + up + + + + + + + F + 1 + 5 + + 2 + 1 + eighth + down + begin + + + + + end + bückt + + + + + F + 5 + + 1 + 1 + 16th + + + + F + 1 + 5 + + 1 + 1 + 16th + down + end + backward hook + + single + in + + + + + + F + 1 + 5 + + 1 + eighth + up + + + + + + + E + 5 + + 2 + 1 + eighth + down + begin + + + + + single + sich + + + + + F + 5 + + 1 + 1 + 16th + + + + E + 5 + + 1 + 1 + 16th + down + end + backward hook + + single + und + + + + + + + + + + E + 5 + + 1 + eighth + up + + + + + + + D + 5 + + 3 + 1 + eighth + + down + begin + + + + + begin + un + + + + + C + 5 + + 1 + 1 + 16th + down + end + backward hook + + middle + be + + + + + B + 4 + + 2 + 1 + eighth + down + + end + kannt: + + + + + 2 + 1 + eighth + + + + + + + 100 + + + + + 2 + 1 + eighth + + + + A + 4 + + 2 + 1 + eighth + down + begin + + single + es + + + + + B + 4 + + 2 + 1 + eighth + down + continue + + single + war + + + + + C + 5 + + 2 + 1 + eighth + down + end + + single + ein + + + + + + + + + D + 5 + + 3 + 1 + eighth + + up + begin + + begin + her + + + + + G + 4 + + 1 + 1 + 16th + up + end + backward hook + + end + zig’s + + + + + B + 4 + + 2 + 1 + eighth + up + begin + + + + + begin + Veil + + + + + A + 4 + + 2 + 1 + eighth + up + end + + + + + + + + + + + G + 4 + + 4 + 1 + quarter + up + + end + chen. + + + + + 2 + 1 + eighth + + + + A + 4 + + 2 + 1 + eighth + up + + single + Da + + + + + + + + + D + 5 + + 2 + 1 + eighth + down + begin + + single + kam + + + + + C + 1 + 5 + + 2 + 1 + eighth + sharp + down + continue + + single + ein’ + + + + + B + 4 + + 2 + 1 + eighth + down + continue + + begin + jun + + + + + C + 1 + 5 + + 2 + 1 + eighth + down + end + + end + ge + + + + + + + + + D + 5 + + 3 + 1 + eighth + + down + begin + + begin + Schä + + + + + A + 4 + + 1 + 1 + 16th + down + end + backward hook + + middle + fer + + + + + A + 4 + + 2 + 1 + eighth + up + begin + + end + in + + + + + A + 4 + + 2 + 1 + eighth + up + end + + single + mit + + + + + + + + + D + 5 + + 2 + 1 + eighth + down + begin + + begin + leich + + + + + D + 5 + + 1 + 1 + 16th + + + + D + 5 + + 1 + 1 + 16th + down + end + backward hook + + end + tem + + + + + E + 5 + + 2 + 1 + eighth + down + begin + + single + Schritt + + + + + D + 5 + + 1 + 1 + 16th + + + + E + 5 + + 1 + 1 + 16th + down + end + backward hook + + single + und + + + + + + + + 100 + + + + + F + 1 + 5 + + 2 + 1 + eighth + down + begin + + begin + mun + + + + + D + 5 + + 1 + 1 + 16th + + + + F + 1 + 5 + + 1 + 1 + 16th + down + end + backward hook + + end + term + + + + + E + 5 + + 2 + 1 + eighth + down + begin + + single + Sinn + + + + + A + 4 + + 2 + 1 + eighth + down + end + + begin + da + + + + + + + + + G + 5 + + 6 + 1 + quarter + + down + + end + her, + + + + + E + 5 + + 2 + 1 + eighth + down + + begin + da + + + + + + + + + F + 1 + 5 + + 2 + 1 + eighth + down + begin + + end + her, + + + + + D + 5 + + 2 + 1 + eighth + down + end + + single + die + + + + + D + 5 + + 2 + 1 + eighth + down + begin + + begin + Wie + + + + + C + 1 + 5 + + 1 + 1 + 16th + sharp + down + continue + begin + + + + + end + se + + + + + + D + 5 + + 1 + 1 + 16th + down + end + end + + + + + + + + + + + E + 5 + + 2 + 1 + eighth + down + + single + her, + + + + + 2 + 1 + eighth + + + + C + 1 + 5 + + 3 + 1 + eighth + + sharp + down + begin + + + + + single + und + + + + + + D + 5 + + 1 + 1 + 16th + down + end + backward hook + + + + + + + + + + + D + 5 + + 4 + 1 + quarter + down + + single + sang. + + + + + 4 + 1 + quarter + + + + + + + + + + 120 + 0 + + 230 + + none + + + 8 + + 1 + major + + + 2 + + G + 2 + + + F + 4 + + + + + Allegretto + + 1 + + + + + +

    + + + 1 + + + + + G + 4 + + 4 + 1 + eighth + up + 1 + + + 4 + + + + G + 3 + + 4 + 5 + eighth + down + 2 + + + + + + + + D + 5 + + 4 + 1 + eighth + up + 1 + begin + + + + + + + B + 4 + + 4 + 1 + eighth + up + 1 + continue + + + + C + 5 + + 4 + 1 + eighth + up + 1 + continue + + + + E + 5 + + 4 + 1 + eighth + up + 1 + end + + + + + + 16 + + + + G + 4 + + 16 + 2 + half + down + 1 + + + 16 + + + + B + 3 + + 4 + 5 + eighth + down + 2 + begin + + + + + + + G + 3 + + 4 + 5 + eighth + down + 2 + continue + + + + A + 3 + + 4 + 5 + eighth + down + 2 + continue + + + + C + 4 + + 4 + 5 + eighth + down + 2 + end + + + + + + + + + + + + E + 5 + + 1 + eighth + up + 1 + + + + + + + D + 5 + + 6 + 1 + eighth + + up + 1 + begin + + + + + + + + C + 5 + + 2 + 1 + 16th + up + 1 + end + backward hook + + + + B + 4 + + 4 + 1 + eighth + up + 1 + begin + + + + + + + F + 5 + + 2 + 1 + 16th + 1 + + + + G + 5 + + 2 + 1 + 16th + up + 1 + end + backward hook + + + + + + 16 + + + + G + 4 + + 8 + 2 + quarter + down + 1 + + + 8 + + + + + C + 4 + + 5 + eighth + up + 2 + + + + + + + B + 3 + + 6 + 5 + eighth + + down + 2 + begin + + + + + + + + A + 3 + + 2 + 5 + 16th + down + 2 + end + backward hook + + + + G + 3 + + 4 + 5 + eighth + down + 2 + + + + + + + C + 4 + + 2 + 5 + 16th + 2 + + + + E + 4 + + 2 + 5 + 16th + up + 2 + + + + + + 16 + + + 8 + 6 + 2 + + + 4 + 6 + 2 + + + + B + 2 + + 4 + 6 + eighth + 2 + + + + + + + + F + 1 + 5 + + 4 + 1 + eighth + up + 1 + begin + + + + + + + F + 5 + + 2 + 1 + 16th + 1 + + + + F + 1 + 5 + + 2 + 1 + 16th + up + 1 + end + backward hook + + + + + + + E + 5 + + 4 + 1 + eighth + up + 1 + begin + + + + + + + F + 5 + + 2 + 1 + 16th + 1 + + + + E + 5 + + 2 + 1 + 16th + up + 1 + end + backward hook + + + 16 + + + + G + 4 + + 4 + 2 + eighth + down + 1 + + + + E + 4 + + 4 + 2 + eighth + 1 + + + + G + 4 + + 4 + 2 + eighth + down + 1 + + + + E + 4 + + 4 + 2 + eighth + 1 + + + 16 + + + + D + 4 + + 4 + 5 + eighth + up + 2 + begin + + + + + + + C + 4 + + 2 + 5 + 16th + 2 + + + + D + 4 + + 2 + 5 + 16th + up + 2 + end + backward hook + + + + + + + C + 4 + + 4 + 5 + eighth + up + 2 + begin + + + + + + + C + 4 + + 2 + 5 + 16th + 2 + + + + C + 4 + + 2 + 5 + 16th + up + 2 + end + backward hook + + + 16 + + + + G + 3 + + 4 + 6 + eighth + down + 2 + + + + B + 2 + + 4 + 6 + eighth + 2 + + + + G + 3 + + 4 + 6 + eighth + down + 2 + + + + B + 2 + + 4 + 6 + eighth + 2 + + + + + + + + + E + 5 + + 1 + eighth + up + 1 + + + + + + + D + 5 + + 6 + 1 + eighth + + up + 1 + begin + + + + + + + C + 5 + + 2 + 1 + 16th + up + 1 + end + backward hook + + + + B + 4 + + 4 + 1 + eighth + up + 1 + + + + B + 4 + + 4 + 1 + eighth + 1 + + + 16 + + + + G + 4 + + 8 + 2 + quarter + down + 1 + + + 8 + + + + + C + 4 + + 5 + eighth + up + 2 + + + + + + + B + 3 + + 6 + 5 + eighth + + up + 2 + begin + + + + + + + A + 3 + + 2 + 5 + 16th + up + 2 + end + backward hook + + + + G + 3 + + 4 + 5 + eighth + up + 2 + + + + D + 3 + + 4 + 5 + eighth + 2 + + + 16 + + + + G + 3 + + 8 + 6 + quarter + down + 2 + + + + + + + + + + + + 1 + 1 + + + + + B + 4 + + 4 + 1 + eighth + 1 + + + + F + 1 + 4 + + 4 + 1 + eighth + up + 1 + begin + + + + + + + + A + 4 + + 4 + 1 + eighth + up + 1 + + + + G + 4 + + 4 + 1 + eighth + up + 1 + continue + + + + + B + 4 + + 4 + 1 + eighth + up + 1 + + + + A + 4 + + 4 + 1 + eighth + up + 1 + end + + + + + + + + C + 5 + + 4 + 1 + eighth + up + 1 + + + 16 + + + 4 + 2 + 1 + + + + D + 4 + + 12 + 2 + quarter + + down + 1 + + + 16 + + + + 4 + 5 + eighth + 2 + + + + C + 3 + + 4 + 5 + eighth + down + 2 + begin + + + + + + + + C + 4 + + 4 + 5 + eighth + down + 2 + + + + B + 2 + + 4 + 5 + eighth + down + 2 + continue + + + + + B + 3 + + 4 + 5 + eighth + down + 2 + + + + A + 2 + + 4 + 5 + eighth + down + 2 + end + + + + + + + + A + 3 + + 4 + 5 + eighth + down + 2 + + + + + + + 100 + + + 70 + + + + + G + 4 + + 4 + 1 + eighth + up + 1 + begin + + + + + D + 5 + + 4 + 1 + eighth + up + 1 + + + + B + 4 + + 2 + 1 + 16th + 1 + + + + +

    + + + 1 + + + + + G + 4 + + 2 + 1 + 16th + up + 1 + end + backward hook + + + + B + 4 + + 4 + 1 + eighth + up + 1 + begin + + + + + + + A + 4 + + 4 + 1 + eighth + up + 1 + end + + + + + + 16 + + + + D + 4 + + 4 + 2 + eighth + down + 1 + + + 4 + 2 + 1 + + + + G + 4 + + 4 + 2 + eighth + down + 1 + begin + + + + F + 1 + 4 + + 4 + 2 + eighth + down + 1 + end + + + 16 + + + + B + 2 + + 4 + 5 + eighth + down + 2 + + + + + B + 3 + + 4 + 5 + eighth + down + 2 + + + + 4 + 5 + eighth + 2 + + + + D + 4 + + 4 + 5 + eighth + down + 2 + begin + + + + D + 3 + + 4 + 5 + eighth + down + 2 + end + + + + + + + + G + 4 + + 8 + 1 + quarter + up + 1 + + + + B + 4 + + 4 + 1 + eighth + 1 + + + + G + 4 + + 4 + 1 + eighth + up + 1 + + + 16 + + + 8 + 2 + 1 + + + 8 + + + + G + 3 + + 8 + 5 + quarter + down + 2 + + + + 8 + 5 + quarter + 2 + + + + + + + + D + 5 + + 4 + 1 + eighth + up + 1 + begin + + + + + + + B + 4 + + 4 + 1 + eighth + up + 1 + continue + + + + C + 5 + + 4 + 1 + eighth + up + 1 + continue + + + + E + 5 + + 4 + 1 + eighth + up + 1 + end + + + + + + 16 + + + + G + 4 + + 16 + 2 + half + down + 1 + + + 16 + + + + B + 3 + + 4 + 5 + eighth + down + 2 + begin + + + + + + + G + 3 + + 4 + 5 + eighth + down + 2 + continue + + + + A + 3 + + 4 + 5 + eighth + down + 2 + continue + + + + C + 4 + + 4 + 5 + eighth + down + 2 + end + + + + + + + + + + + + E + 5 + + 1 + eighth + up + 1 + + + + + + + D + 5 + + 6 + 1 + eighth + + up + 1 + begin + + + + + + + C + 5 + + 2 + 1 + 16th + up + 1 + end + backward hook + + + + B + 4 + + 4 + 1 + eighth + up + 1 + + + + B + 4 + + 2 + 1 + 16th + 1 + + + + G + 5 + + 2 + 1 + 16th + up + 1 + + + + + + 16 + + + + G + 4 + + 8 + 2 + quarter + down + 1 + + + 8 + + + + + C + 4 + + 5 + eighth + up + 2 + + + + + + + B + 3 + + 6 + 5 + eighth + + up + 2 + begin + + + + + + + A + 3 + + 2 + 5 + 16th + up + 2 + end + backward hook + + + + G + 3 + + 4 + 5 + eighth + up + 2 + + + + A + 3 + + 2 + 5 + 16th + 2 + + + + E + 4 + + 2 + 5 + 16th + up + 2 + + + + + + 16 + + + + G + 3 + + 8 + 6 + quarter + down + 2 + + + + + + + + F + 1 + 5 + + 4 + 1 + eighth + up + 1 + begin + + + + + + + F + 5 + + 2 + 1 + 16th + 1 + + + + F + 1 + 5 + + 2 + 1 + 16th + up + 1 + end + backward hook + + + + + + + E + 5 + + 4 + 1 + eighth + up + 1 + begin + + + + + + + F + 5 + + 2 + 1 + 16th + 1 + + + + E + 5 + + 2 + 1 + 16th + up + 1 + end + backward hook + + + 16 + + + + G + 4 + + 4 + 2 + eighth + down + 1 + + + + E + 4 + + 4 + 2 + eighth + 1 + + + + G + 4 + + 4 + 2 + eighth + down + 1 + + + + E + 4 + + 4 + 2 + eighth + 1 + + + 16 + + + + D + 4 + + 4 + 5 + eighth + up + 2 + begin + + + + + + + C + 4 + + 2 + 5 + 16th + 2 + + + + D + 4 + + 2 + 5 + 16th + up + 2 + end + backward hook + + + + + + + C + 4 + + 4 + 5 + eighth + up + 2 + begin + + + + + + + C + 4 + + 2 + 5 + 16th + 2 + + + + C + 4 + + 2 + 5 + 16th + up + 2 + end + backward hook + + + 16 + + + + G + 3 + + 4 + 6 + eighth + down + 2 + + + + B + 2 + + 4 + 6 + eighth + 2 + + + + G + 3 + + 4 + 6 + eighth + down + 2 + + + + B + 2 + + 4 + 6 + eighth + 2 + + + + + + + + + E + 5 + + 1 + eighth + up + 1 + + + + + + + D + 5 + + 6 + 1 + eighth + + up + 1 + begin + + + + + + + C + 5 + + 2 + 1 + 16th + up + 1 + end + backward hook + + + + B + 4 + + 4 + 1 + eighth + up + 1 + + + + B + 4 + + 4 + 1 + eighth + 1 + + + 16 + + + + G + 4 + + 8 + 2 + quarter + down + 1 + + + 8 + + + + + C + 4 + + 5 + eighth + up + 2 + + + + + + + B + 3 + + 6 + 5 + eighth + + up + 2 + begin + + + + + + + A + 3 + + 2 + 5 + 16th + up + 2 + end + backward hook + + + + G + 3 + + 4 + 5 + eighth + up + 2 + + + + D + 3 + + 4 + 5 + eighth + 2 + + + 16 + + + + G + 3 + + 8 + 6 + quarter + down + 2 + + + + + + + 60 + + + + + + + + + 1 + 1 + + + + + B + 4 + + 4 + 1 + eighth + 1 + + + + F + 1 + 4 + + 4 + 1 + eighth + up + 1 + begin + + + + + + + + A + 4 + + 4 + 1 + eighth + up + 1 + + + + G + 4 + + 4 + 1 + eighth + up + 1 + continue + + + + + B + 4 + + 4 + 1 + eighth + up + 1 + + + + A + 4 + + 4 + 1 + eighth + up + 1 + end + + + + + + + + C + 5 + + 4 + 1 + eighth + up + 1 + + + 16 + + + 4 + 2 + 1 + + + + D + 4 + + 12 + 2 + quarter + + down + 1 + + + 16 + + + + 4 + 5 + eighth + 2 + + + + C + 3 + + 4 + 5 + eighth + down + 2 + begin + + + + + + + + C + 4 + + 4 + 5 + eighth + down + 2 + + + + B + 2 + + 4 + 5 + eighth + down + 2 + continue + + + + + B + 3 + + 4 + 5 + eighth + down + 2 + + + + A + 2 + + 4 + 5 + eighth + down + 2 + end + + + + + + + + A + 3 + + 4 + 5 + eighth + down + 2 + + + + + + + + G + 4 + + 4 + 1 + eighth + up + 1 + + + + + D + 5 + + 4 + 1 + eighth + up + 1 + + + + B + 4 + + 4 + 1 + eighth + 1 + + + + +

    + + + 1 + + + + + D + 4 + + 4 + 1 + eighth + up + 1 + begin + + + + + + + + G + 4 + + 4 + 1 + eighth + up + 1 + + + + + B + 4 + + 4 + 1 + eighth + up + 1 + + + + C + 4 + + 4 + 1 + eighth + up + 1 + end + + + + + + + + F + 1 + 4 + + 4 + 1 + eighth + up + 1 + + + + + A + 4 + + 4 + 1 + eighth + up + 1 + + + 16 + + + + D + 4 + + 4 + 2 + eighth + down + 1 + + + 4 + 2 + 1 + + + 8 + 2 + 1 + + + 16 + + + + B + 2 + + 4 + 5 + eighth + down + 2 + + + + + B + 3 + + 4 + 5 + eighth + down + 2 + + + + 4 + 5 + eighth + 2 + + + + D + 3 + + 4 + 5 + eighth + up + 2 + begin + + + + D + 2 + + 4 + 5 + eighth + up + 2 + end + + + + + + + + B + 3 + + 8 + 1 + quarter + up + 1 + + + + + G + 4 + + 8 + 1 + quarter + up + 1 + + + + 8 + 1 + quarter + 1 + + + 16 + + + + G + 2 + + 8 + 5 + quarter + up + 2 + + + + 8 + 5 + quarter + 2 + + + + + + + + A + 4 + + 16 + 1 + half + up + 1 + + + 16 + + + + D + 4 + + 8 + 2 + quarter + down + 1 + + + + + + + + F + 1 + 4 + + 8 + 2 + quarter + down + 1 + + + + E + 4 + + 8 + 2 + quarter + down + 1 + + + + + + + + G + 4 + + 8 + 2 + quarter + down + 1 + + + 16 + + + + D + 3 + + 8 + 5 + quarter + down + 2 + + + + + + + A + 2 + + 8 + 5 + quarter + up + 2 + + + + + + + + + + + D + 4 + + 8 + 1 + quarter + up + 1 + + + + + F + 1 + 4 + + 8 + 1 + quarter + up + 1 + + + + + A + 4 + + 8 + 1 + quarter + up + 1 + + + + B + 4 + + 8 + 1 + quarter + 1 + + + 16 + + + + D + 2 + + 8 + 5 + quarter + up + 2 + + + + 8 + 5 + quarter + 2 + + + + + + + + F + 1 + 4 + + 2 + 1 + 16th + up + 1 + begin + begin + + + + + + + + + A + 4 + + 2 + 1 + 16th + up + 1 + continue + continue + + + + + + + + + F + 1 + 4 + + 2 + 1 + 16th + up + 1 + continue + continue + + + + + + + + + D + 4 + + 2 + 1 + 16th + up + 1 + end + end + + + + + + + + + C + 1 + 4 + + 2 + 1 + 16th + sharp + up + 1 + begin + begin + + + + + + + + + G + 4 + + 2 + 1 + 16th + up + 1 + continue + continue + + + + + + + + + E + 4 + + 2 + 1 + 16th + up + 1 + continue + continue + + + + + + + + + C + 1 + 4 + + 2 + 1 + 16th + up + 1 + end + end + + + + + + + + 16 + + + + D + 3 + + 4 + 5 + eighth + down + 2 + + + + 4 + 5 + eighth + 2 + + + + A + 3 + + 4 + 5 + eighth + down + 2 + + + + 4 + 5 + eighth + 2 + + + + + + + 60 + + + + + D + 4 + + 2 + 1 + 16th + up + 1 + begin + begin + + + + + + + + + F + 1 + 4 + + 2 + 1 + 16th + up + 1 + continue + continue + + + + + + + + + A + 4 + + 2 + 1 + 16th + up + 1 + continue + continue + + + + + + + + + D + 5 + + 2 + 1 + 16th + up + 1 + end + end + + + + + + + + + C + 1 + 5 + + 4 + 1 + eighth + sharp + down + 1 + + + + 4 + 1 + eighth + 1 + + + 16 + + + + D + 3 + + 4 + 5 + eighth + down + 2 + + + + 4 + 5 + eighth + 2 + + + + A + 2 + + 4 + 5 + eighth + up + 2 + + + + 4 + 5 + eighth + 2 + + + + + + + + A + 3 + + 2 + 1 + 16th + up + 1 + begin + begin + + + + + + + C + 1 + 4 + + 2 + 1 + 16th + sharp + up + 1 + continue + continue + + + + E + 4 + + 2 + 1 + 16th + up + 1 + continue + continue + + + + C + 1 + 4 + + 2 + 1 + 16th + up + 1 + end + end + + + + A + 3 + + 2 + 1 + 16th + up + 1 + begin + begin + + + + E + 4 + + 2 + 1 + 16th + up + 1 + continue + continue + + + + G + 4 + + 2 + 1 + 16th + up + 1 + continue + continue + + + + E + 4 + + 2 + 1 + 16th + up + 1 + end + end + + + + + + 16 + + + + 4 + 5 + eighth + 2 + + + + E + 3 + + 4 + 5 + eighth + down + 2 + begin + + + + + + + C + 1 + 3 + + 4 + 5 + eighth + sharp + down + 2 + continue + + + + A + 2 + + 4 + 5 + eighth + down + 2 + end + + + + + + + + + + + A + 3 + + 2 + 1 + 16th + up + 1 + begin + begin + + + + + + + D + 4 + + 2 + 1 + 16th + up + 1 + continue + continue + + + + F + 1 + 4 + + 2 + 1 + 16th + up + 1 + continue + continue + + + + D + 4 + + 2 + 1 + 16th + up + 1 + end + end + + + + + + + D + 4 + + 2 + 1 + 16th + up + 1 + begin + begin + + + + + + + F + 1 + 4 + + 2 + 1 + 16th + up + 1 + continue + continue + + + + A + 4 + + 2 + 1 + 16th + up + 1 + continue + continue + + + + F + 1 + 4 + + 2 + 1 + 16th + up + 1 + end + end + + + + + + 16 + + + + 4 + 5 + eighth + 2 + + + + D + 3 + + 4 + 5 + eighth + down + 2 + begin + + + + + + + F + 1 + 3 + + 4 + 5 + eighth + down + 2 + continue + + + + D + 3 + + 4 + 5 + eighth + down + 2 + end + + + + + + + + + + + B + 4 + + 4 + 1 + eighth + down + 1 + + + + 4 + 1 + eighth + 1 + + + + E + 4 + + 8 + 1 + quarter + up + 1 + + + + + + + + G + 4 + + 8 + 1 + quarter + up + 1 + + + + + C + 1 + 5 + + 8 + 1 + quarter + sharp + up + 1 + + + 16 + + + + G + 3 + + 4 + 5 + eighth + down + 2 + + + + 4 + 5 + eighth + 2 + + + + A + 3 + + 8 + 5 + quarter + down + 2 + + + + + + + + + + + F + 1 + 4 + + 4 + 1 + eighth + up + 1 + + + + + + + + D + 5 + + 4 + 1 + eighth + up + 1 + + + + 4 + 1 + eighth + 1 + + + + 4 + 1 + eighth + 1 + + + + A + 4 + + 2 + 1 + 16th + down + 1 + begin + begin + + + + + + + A + 5 + + 2 + 1 + 16th + down + 1 + end + end + + + + + + 16 + + + + D + 3 + + 4 + 5 + eighth + down + 2 + + + + + + + 4 + 5 + eighth + 2 + + + + 8 + 5 + quarter + 2 + + + + G + 2 + + + + + + diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/MozartPianoSonata.mxl b/test-data/musicxml-samples/MozartPianoSonata.mxl similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/MozartPianoSonata.mxl rename to test-data/musicxml-samples/MozartPianoSonata.mxl diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/MozartPianoSonata.png b/test-data/musicxml-samples/MozartPianoSonata.png similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/MozartPianoSonata.png rename to test-data/musicxml-samples/MozartPianoSonata.png diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/MozartPianoSonata.xml b/test-data/musicxml-samples/MozartPianoSonata.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/MozartPianoSonata.xml rename to test-data/musicxml-samples/MozartPianoSonata.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/MozartTrio.mxl b/test-data/musicxml-samples/MozartTrio.mxl similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/MozartTrio.mxl rename to test-data/musicxml-samples/MozartTrio.mxl diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/MozartTrio.png b/test-data/musicxml-samples/MozartTrio.png similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/MozartTrio.png rename to test-data/musicxml-samples/MozartTrio.png diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/MozartTrio.xml b/test-data/musicxml-samples/MozartTrio.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/MozartTrio.xml rename to test-data/musicxml-samples/MozartTrio.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/Saltarello.mxl b/test-data/musicxml-samples/Saltarello.mxl similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/Saltarello.mxl rename to test-data/musicxml-samples/Saltarello.mxl diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/Saltarello.png b/test-data/musicxml-samples/Saltarello.png similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/Saltarello.png rename to test-data/musicxml-samples/Saltarello.png diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/Saltarello.xml b/test-data/musicxml-samples/Saltarello.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/Saltarello.xml rename to test-data/musicxml-samples/Saltarello.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/SchbAvMaSample.mxl b/test-data/musicxml-samples/SchbAvMaSample.mxl similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/SchbAvMaSample.mxl rename to test-data/musicxml-samples/SchbAvMaSample.mxl diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/SchbAvMaSample.pdf b/test-data/musicxml-samples/SchbAvMaSample.pdf similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/SchbAvMaSample.pdf rename to test-data/musicxml-samples/SchbAvMaSample.pdf diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/SchbAvMaSample.xml b/test-data/musicxml-samples/SchbAvMaSample.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/SchbAvMaSample.xml rename to test-data/musicxml-samples/SchbAvMaSample.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/Telemann.mxl b/test-data/musicxml-samples/Telemann.mxl similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/Telemann.mxl rename to test-data/musicxml-samples/Telemann.mxl diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/Telemann.png b/test-data/musicxml-samples/Telemann.png similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/Telemann.png rename to test-data/musicxml-samples/Telemann.png diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlSamples/Telemann.xml b/test-data/musicxml-samples/Telemann.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlSamples/Telemann.xml rename to test-data/musicxml-samples/Telemann.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/01a-Pitches-Pitches.xml b/test-data/musicxml-testsuite/01a-Pitches-Pitches.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/01a-Pitches-Pitches.xml rename to test-data/musicxml-testsuite/01a-Pitches-Pitches.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/01b-Pitches-Intervals.xml b/test-data/musicxml-testsuite/01b-Pitches-Intervals.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/01b-Pitches-Intervals.xml rename to test-data/musicxml-testsuite/01b-Pitches-Intervals.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/01c-Pitches-NoVoiceElement.xml b/test-data/musicxml-testsuite/01c-Pitches-NoVoiceElement.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/01c-Pitches-NoVoiceElement.xml rename to test-data/musicxml-testsuite/01c-Pitches-NoVoiceElement.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/01d-Pitches-Microtones.xml b/test-data/musicxml-testsuite/01d-Pitches-Microtones.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/01d-Pitches-Microtones.xml rename to test-data/musicxml-testsuite/01d-Pitches-Microtones.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/01e-Pitches-ParenthesizedAccidentals.xml b/test-data/musicxml-testsuite/01e-Pitches-ParenthesizedAccidentals.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/01e-Pitches-ParenthesizedAccidentals.xml rename to test-data/musicxml-testsuite/01e-Pitches-ParenthesizedAccidentals.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/01f-Pitches-ParenthesizedMicrotoneAccidentals.xml b/test-data/musicxml-testsuite/01f-Pitches-ParenthesizedMicrotoneAccidentals.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/01f-Pitches-ParenthesizedMicrotoneAccidentals.xml rename to test-data/musicxml-testsuite/01f-Pitches-ParenthesizedMicrotoneAccidentals.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/02a-Rests-Durations.xml b/test-data/musicxml-testsuite/02a-Rests-Durations.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/02a-Rests-Durations.xml rename to test-data/musicxml-testsuite/02a-Rests-Durations.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/02b-Rests-PitchedRests.xml b/test-data/musicxml-testsuite/02b-Rests-PitchedRests.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/02b-Rests-PitchedRests.xml rename to test-data/musicxml-testsuite/02b-Rests-PitchedRests.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/02c-Rests-MultiMeasureRests.xml b/test-data/musicxml-testsuite/02c-Rests-MultiMeasureRests.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/02c-Rests-MultiMeasureRests.xml rename to test-data/musicxml-testsuite/02c-Rests-MultiMeasureRests.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/02d-Rests-Multimeasure-TimeSignatures.xml b/test-data/musicxml-testsuite/02d-Rests-Multimeasure-TimeSignatures.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/02d-Rests-Multimeasure-TimeSignatures.xml rename to test-data/musicxml-testsuite/02d-Rests-Multimeasure-TimeSignatures.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/02e-Rests-NoType.xml b/test-data/musicxml-testsuite/02e-Rests-NoType.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/02e-Rests-NoType.xml rename to test-data/musicxml-testsuite/02e-Rests-NoType.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/03a-Rhythm-Durations.xml b/test-data/musicxml-testsuite/03a-Rhythm-Durations.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/03a-Rhythm-Durations.xml rename to test-data/musicxml-testsuite/03a-Rhythm-Durations.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/03b-Rhythm-Backup.xml b/test-data/musicxml-testsuite/03b-Rhythm-Backup.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/03b-Rhythm-Backup.xml rename to test-data/musicxml-testsuite/03b-Rhythm-Backup.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/03c-Rhythm-DivisionChange.xml b/test-data/musicxml-testsuite/03c-Rhythm-DivisionChange.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/03c-Rhythm-DivisionChange.xml rename to test-data/musicxml-testsuite/03c-Rhythm-DivisionChange.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/03d-Rhythm-DottedDurations-Factors.xml b/test-data/musicxml-testsuite/03d-Rhythm-DottedDurations-Factors.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/03d-Rhythm-DottedDurations-Factors.xml rename to test-data/musicxml-testsuite/03d-Rhythm-DottedDurations-Factors.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/11a-TimeSignatures.xml b/test-data/musicxml-testsuite/11a-TimeSignatures.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/11a-TimeSignatures.xml rename to test-data/musicxml-testsuite/11a-TimeSignatures.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/11b-TimeSignatures-NoTime.xml b/test-data/musicxml-testsuite/11b-TimeSignatures-NoTime.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/11b-TimeSignatures-NoTime.xml rename to test-data/musicxml-testsuite/11b-TimeSignatures-NoTime.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/11c-TimeSignatures-CompoundSimple.xml b/test-data/musicxml-testsuite/11c-TimeSignatures-CompoundSimple.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/11c-TimeSignatures-CompoundSimple.xml rename to test-data/musicxml-testsuite/11c-TimeSignatures-CompoundSimple.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/11d-TimeSignatures-CompoundMultiple.xml b/test-data/musicxml-testsuite/11d-TimeSignatures-CompoundMultiple.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/11d-TimeSignatures-CompoundMultiple.xml rename to test-data/musicxml-testsuite/11d-TimeSignatures-CompoundMultiple.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/11e-TimeSignatures-CompoundMixed.xml b/test-data/musicxml-testsuite/11e-TimeSignatures-CompoundMixed.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/11e-TimeSignatures-CompoundMixed.xml rename to test-data/musicxml-testsuite/11e-TimeSignatures-CompoundMixed.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/11f-TimeSignatures-SymbolMeaning.xml b/test-data/musicxml-testsuite/11f-TimeSignatures-SymbolMeaning.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/11f-TimeSignatures-SymbolMeaning.xml rename to test-data/musicxml-testsuite/11f-TimeSignatures-SymbolMeaning.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/11g-TimeSignatures-SingleNumber.xml b/test-data/musicxml-testsuite/11g-TimeSignatures-SingleNumber.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/11g-TimeSignatures-SingleNumber.xml rename to test-data/musicxml-testsuite/11g-TimeSignatures-SingleNumber.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/11h-TimeSignatures-SenzaMisura.xml b/test-data/musicxml-testsuite/11h-TimeSignatures-SenzaMisura.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/11h-TimeSignatures-SenzaMisura.xml rename to test-data/musicxml-testsuite/11h-TimeSignatures-SenzaMisura.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/12a-Clefs.xml b/test-data/musicxml-testsuite/12a-Clefs.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/12a-Clefs.xml rename to test-data/musicxml-testsuite/12a-Clefs.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/12b-Clefs-NoKeyOrClef.xml b/test-data/musicxml-testsuite/12b-Clefs-NoKeyOrClef.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/12b-Clefs-NoKeyOrClef.xml rename to test-data/musicxml-testsuite/12b-Clefs-NoKeyOrClef.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/13a-KeySignatures.xml b/test-data/musicxml-testsuite/13a-KeySignatures.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/13a-KeySignatures.xml rename to test-data/musicxml-testsuite/13a-KeySignatures.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/13b-KeySignatures-ChurchModes.xml b/test-data/musicxml-testsuite/13b-KeySignatures-ChurchModes.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/13b-KeySignatures-ChurchModes.xml rename to test-data/musicxml-testsuite/13b-KeySignatures-ChurchModes.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/13c-KeySignatures-NonTraditional.xml b/test-data/musicxml-testsuite/13c-KeySignatures-NonTraditional.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/13c-KeySignatures-NonTraditional.xml rename to test-data/musicxml-testsuite/13c-KeySignatures-NonTraditional.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/13d-KeySignatures-Microtones.xml b/test-data/musicxml-testsuite/13d-KeySignatures-Microtones.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/13d-KeySignatures-Microtones.xml rename to test-data/musicxml-testsuite/13d-KeySignatures-Microtones.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/14a-StaffDetails-LineChanges.xml b/test-data/musicxml-testsuite/14a-StaffDetails-LineChanges.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/14a-StaffDetails-LineChanges.xml rename to test-data/musicxml-testsuite/14a-StaffDetails-LineChanges.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/21a-Chord-Basic.xml b/test-data/musicxml-testsuite/21a-Chord-Basic.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/21a-Chord-Basic.xml rename to test-data/musicxml-testsuite/21a-Chord-Basic.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/21b-Chords-TwoNotes.xml b/test-data/musicxml-testsuite/21b-Chords-TwoNotes.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/21b-Chords-TwoNotes.xml rename to test-data/musicxml-testsuite/21b-Chords-TwoNotes.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/21c-Chords-ThreeNotesDuration.xml b/test-data/musicxml-testsuite/21c-Chords-ThreeNotesDuration.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/21c-Chords-ThreeNotesDuration.xml rename to test-data/musicxml-testsuite/21c-Chords-ThreeNotesDuration.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/21d-Chords-SchubertStabatMater.xml b/test-data/musicxml-testsuite/21d-Chords-SchubertStabatMater.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/21d-Chords-SchubertStabatMater.xml rename to test-data/musicxml-testsuite/21d-Chords-SchubertStabatMater.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/21e-Chords-PickupMeasures.xml b/test-data/musicxml-testsuite/21e-Chords-PickupMeasures.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/21e-Chords-PickupMeasures.xml rename to test-data/musicxml-testsuite/21e-Chords-PickupMeasures.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/21f-Chord-ElementInBetween.xml b/test-data/musicxml-testsuite/21f-Chord-ElementInBetween.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/21f-Chord-ElementInBetween.xml rename to test-data/musicxml-testsuite/21f-Chord-ElementInBetween.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/22a-Noteheads.xml b/test-data/musicxml-testsuite/22a-Noteheads.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/22a-Noteheads.xml rename to test-data/musicxml-testsuite/22a-Noteheads.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/22b-Staff-Notestyles.xml b/test-data/musicxml-testsuite/22b-Staff-Notestyles.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/22b-Staff-Notestyles.xml rename to test-data/musicxml-testsuite/22b-Staff-Notestyles.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/22c-Noteheads-Chords.xml b/test-data/musicxml-testsuite/22c-Noteheads-Chords.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/22c-Noteheads-Chords.xml rename to test-data/musicxml-testsuite/22c-Noteheads-Chords.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/22d-Parenthesized-Noteheads.xml b/test-data/musicxml-testsuite/22d-Parenthesized-Noteheads.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/22d-Parenthesized-Noteheads.xml rename to test-data/musicxml-testsuite/22d-Parenthesized-Noteheads.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/23a-Tuplets.xml b/test-data/musicxml-testsuite/23a-Tuplets.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/23a-Tuplets.xml rename to test-data/musicxml-testsuite/23a-Tuplets.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/23b-Tuplets-Styles.xml b/test-data/musicxml-testsuite/23b-Tuplets-Styles.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/23b-Tuplets-Styles.xml rename to test-data/musicxml-testsuite/23b-Tuplets-Styles.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/23c-Tuplet-Display-NonStandard.xml b/test-data/musicxml-testsuite/23c-Tuplet-Display-NonStandard.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/23c-Tuplet-Display-NonStandard.xml rename to test-data/musicxml-testsuite/23c-Tuplet-Display-NonStandard.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/23d-Tuplets-Nested.xml b/test-data/musicxml-testsuite/23d-Tuplets-Nested.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/23d-Tuplets-Nested.xml rename to test-data/musicxml-testsuite/23d-Tuplets-Nested.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/23e-Tuplets-Tremolo.xml b/test-data/musicxml-testsuite/23e-Tuplets-Tremolo.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/23e-Tuplets-Tremolo.xml rename to test-data/musicxml-testsuite/23e-Tuplets-Tremolo.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/23f-Tuplets-DurationButNoBracket.xml b/test-data/musicxml-testsuite/23f-Tuplets-DurationButNoBracket.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/23f-Tuplets-DurationButNoBracket.xml rename to test-data/musicxml-testsuite/23f-Tuplets-DurationButNoBracket.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/24a-GraceNotes.xml b/test-data/musicxml-testsuite/24a-GraceNotes.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/24a-GraceNotes.xml rename to test-data/musicxml-testsuite/24a-GraceNotes.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/24b-ChordAsGraceNote.xml b/test-data/musicxml-testsuite/24b-ChordAsGraceNote.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/24b-ChordAsGraceNote.xml rename to test-data/musicxml-testsuite/24b-ChordAsGraceNote.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/24c-GraceNote-MeasureEnd.xml b/test-data/musicxml-testsuite/24c-GraceNote-MeasureEnd.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/24c-GraceNote-MeasureEnd.xml rename to test-data/musicxml-testsuite/24c-GraceNote-MeasureEnd.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/24d-AfterGrace.xml b/test-data/musicxml-testsuite/24d-AfterGrace.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/24d-AfterGrace.xml rename to test-data/musicxml-testsuite/24d-AfterGrace.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/24e-GraceNote-StaffChange.xml b/test-data/musicxml-testsuite/24e-GraceNote-StaffChange.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/24e-GraceNote-StaffChange.xml rename to test-data/musicxml-testsuite/24e-GraceNote-StaffChange.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/24f-GraceNote-Slur.xml b/test-data/musicxml-testsuite/24f-GraceNote-Slur.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/24f-GraceNote-Slur.xml rename to test-data/musicxml-testsuite/24f-GraceNote-Slur.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/31a-Directions.xml b/test-data/musicxml-testsuite/31a-Directions.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/31a-Directions.xml rename to test-data/musicxml-testsuite/31a-Directions.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/31c-MetronomeMarks.xml b/test-data/musicxml-testsuite/31c-MetronomeMarks.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/31c-MetronomeMarks.xml rename to test-data/musicxml-testsuite/31c-MetronomeMarks.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/32a-Notations.xml b/test-data/musicxml-testsuite/32a-Notations.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/32a-Notations.xml rename to test-data/musicxml-testsuite/32a-Notations.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/32b-Articulations-Texts.xml b/test-data/musicxml-testsuite/32b-Articulations-Texts.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/32b-Articulations-Texts.xml rename to test-data/musicxml-testsuite/32b-Articulations-Texts.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/32c-MultipleNotationChildren.xml b/test-data/musicxml-testsuite/32c-MultipleNotationChildren.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/32c-MultipleNotationChildren.xml rename to test-data/musicxml-testsuite/32c-MultipleNotationChildren.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/32d-Arpeggio.xml b/test-data/musicxml-testsuite/32d-Arpeggio.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/32d-Arpeggio.xml rename to test-data/musicxml-testsuite/32d-Arpeggio.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33a-Spanners.xml b/test-data/musicxml-testsuite/33a-Spanners.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33a-Spanners.xml rename to test-data/musicxml-testsuite/33a-Spanners.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33b-Spanners-Tie.xml b/test-data/musicxml-testsuite/33b-Spanners-Tie.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33b-Spanners-Tie.xml rename to test-data/musicxml-testsuite/33b-Spanners-Tie.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33c-Spanners-Slurs.xml b/test-data/musicxml-testsuite/33c-Spanners-Slurs.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33c-Spanners-Slurs.xml rename to test-data/musicxml-testsuite/33c-Spanners-Slurs.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33d-Spanners-OctaveShifts.xml b/test-data/musicxml-testsuite/33d-Spanners-OctaveShifts.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33d-Spanners-OctaveShifts.xml rename to test-data/musicxml-testsuite/33d-Spanners-OctaveShifts.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33e-Spanners-OctaveShifts-InvalidSize.xml b/test-data/musicxml-testsuite/33e-Spanners-OctaveShifts-InvalidSize.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33e-Spanners-OctaveShifts-InvalidSize.xml rename to test-data/musicxml-testsuite/33e-Spanners-OctaveShifts-InvalidSize.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33f-Trill-EndingOnGraceNote.xml b/test-data/musicxml-testsuite/33f-Trill-EndingOnGraceNote.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33f-Trill-EndingOnGraceNote.xml rename to test-data/musicxml-testsuite/33f-Trill-EndingOnGraceNote.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33g-Slur-ChordedNotes.xml b/test-data/musicxml-testsuite/33g-Slur-ChordedNotes.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33g-Slur-ChordedNotes.xml rename to test-data/musicxml-testsuite/33g-Slur-ChordedNotes.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33h-Spanners-Glissando.xml b/test-data/musicxml-testsuite/33h-Spanners-Glissando.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33h-Spanners-Glissando.xml rename to test-data/musicxml-testsuite/33h-Spanners-Glissando.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33i-Ties-NotEnded.xml b/test-data/musicxml-testsuite/33i-Ties-NotEnded.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/33i-Ties-NotEnded.xml rename to test-data/musicxml-testsuite/33i-Ties-NotEnded.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41a-MultiParts-Partorder.xml b/test-data/musicxml-testsuite/41a-MultiParts-Partorder.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41a-MultiParts-Partorder.xml rename to test-data/musicxml-testsuite/41a-MultiParts-Partorder.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41b-MultiParts-MoreThan10.xml b/test-data/musicxml-testsuite/41b-MultiParts-MoreThan10.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41b-MultiParts-MoreThan10.xml rename to test-data/musicxml-testsuite/41b-MultiParts-MoreThan10.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41c-StaffGroups.xml b/test-data/musicxml-testsuite/41c-StaffGroups.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41c-StaffGroups.xml rename to test-data/musicxml-testsuite/41c-StaffGroups.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41d-StaffGroups-Nested.xml b/test-data/musicxml-testsuite/41d-StaffGroups-Nested.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41d-StaffGroups-Nested.xml rename to test-data/musicxml-testsuite/41d-StaffGroups-Nested.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41e-StaffGroups-InstrumentNames-Linebroken.xml b/test-data/musicxml-testsuite/41e-StaffGroups-InstrumentNames-Linebroken.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41e-StaffGroups-InstrumentNames-Linebroken.xml rename to test-data/musicxml-testsuite/41e-StaffGroups-InstrumentNames-Linebroken.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41f-StaffGroups-Overlapping.xml b/test-data/musicxml-testsuite/41f-StaffGroups-Overlapping.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41f-StaffGroups-Overlapping.xml rename to test-data/musicxml-testsuite/41f-StaffGroups-Overlapping.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41g-PartNoId.xml b/test-data/musicxml-testsuite/41g-PartNoId.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41g-PartNoId.xml rename to test-data/musicxml-testsuite/41g-PartNoId.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41h-TooManyParts.xml b/test-data/musicxml-testsuite/41h-TooManyParts.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41h-TooManyParts.xml rename to test-data/musicxml-testsuite/41h-TooManyParts.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41i-PartNameDisplay-Override.xml b/test-data/musicxml-testsuite/41i-PartNameDisplay-Override.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/41i-PartNameDisplay-Override.xml rename to test-data/musicxml-testsuite/41i-PartNameDisplay-Override.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/42a-MultiVoice-TwoVoicesOnStaff-Lyrics.xml b/test-data/musicxml-testsuite/42a-MultiVoice-TwoVoicesOnStaff-Lyrics.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/42a-MultiVoice-TwoVoicesOnStaff-Lyrics.xml rename to test-data/musicxml-testsuite/42a-MultiVoice-TwoVoicesOnStaff-Lyrics.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/42b-MultiVoice-MidMeasureClefChange.xml b/test-data/musicxml-testsuite/42b-MultiVoice-MidMeasureClefChange.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/42b-MultiVoice-MidMeasureClefChange.xml rename to test-data/musicxml-testsuite/42b-MultiVoice-MidMeasureClefChange.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/43a-PianoStaff.xml b/test-data/musicxml-testsuite/43a-PianoStaff.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/43a-PianoStaff.xml rename to test-data/musicxml-testsuite/43a-PianoStaff.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/43b-MultiStaff-DifferentKeys.xml b/test-data/musicxml-testsuite/43b-MultiStaff-DifferentKeys.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/43b-MultiStaff-DifferentKeys.xml rename to test-data/musicxml-testsuite/43b-MultiStaff-DifferentKeys.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/43c-MultiStaff-DifferentKeysAfterBackup.xml b/test-data/musicxml-testsuite/43c-MultiStaff-DifferentKeysAfterBackup.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/43c-MultiStaff-DifferentKeysAfterBackup.xml rename to test-data/musicxml-testsuite/43c-MultiStaff-DifferentKeysAfterBackup.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/43d-MultiStaff-StaffChange.xml b/test-data/musicxml-testsuite/43d-MultiStaff-StaffChange.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/43d-MultiStaff-StaffChange.xml rename to test-data/musicxml-testsuite/43d-MultiStaff-StaffChange.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/43e-Multistaff-ClefDynamics.xml b/test-data/musicxml-testsuite/43e-Multistaff-ClefDynamics.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/43e-Multistaff-ClefDynamics.xml rename to test-data/musicxml-testsuite/43e-Multistaff-ClefDynamics.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/45a-SimpleRepeat.xml b/test-data/musicxml-testsuite/45a-SimpleRepeat.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/45a-SimpleRepeat.xml rename to test-data/musicxml-testsuite/45a-SimpleRepeat.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/45b-RepeatWithAlternatives.xml b/test-data/musicxml-testsuite/45b-RepeatWithAlternatives.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/45b-RepeatWithAlternatives.xml rename to test-data/musicxml-testsuite/45b-RepeatWithAlternatives.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/45c-RepeatMultipleTimes.xml b/test-data/musicxml-testsuite/45c-RepeatMultipleTimes.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/45c-RepeatMultipleTimes.xml rename to test-data/musicxml-testsuite/45c-RepeatMultipleTimes.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/45d-Repeats-Nested-Alternatives.xml b/test-data/musicxml-testsuite/45d-Repeats-Nested-Alternatives.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/45d-Repeats-Nested-Alternatives.xml rename to test-data/musicxml-testsuite/45d-Repeats-Nested-Alternatives.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/45e-Repeats-Nested-Alternatives.xml b/test-data/musicxml-testsuite/45e-Repeats-Nested-Alternatives.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/45e-Repeats-Nested-Alternatives.xml rename to test-data/musicxml-testsuite/45e-Repeats-Nested-Alternatives.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/45f-Repeats-InvalidEndings.xml b/test-data/musicxml-testsuite/45f-Repeats-InvalidEndings.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/45f-Repeats-InvalidEndings.xml rename to test-data/musicxml-testsuite/45f-Repeats-InvalidEndings.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/45g-Repeats-NotEnded.xml b/test-data/musicxml-testsuite/45g-Repeats-NotEnded.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/45g-Repeats-NotEnded.xml rename to test-data/musicxml-testsuite/45g-Repeats-NotEnded.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/46a-Barlines.xml b/test-data/musicxml-testsuite/46a-Barlines.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/46a-Barlines.xml rename to test-data/musicxml-testsuite/46a-Barlines.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/46b-MidmeasureBarline.xml b/test-data/musicxml-testsuite/46b-MidmeasureBarline.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/46b-MidmeasureBarline.xml rename to test-data/musicxml-testsuite/46b-MidmeasureBarline.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/46c-Midmeasure-Clef.xml b/test-data/musicxml-testsuite/46c-Midmeasure-Clef.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/46c-Midmeasure-Clef.xml rename to test-data/musicxml-testsuite/46c-Midmeasure-Clef.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/46d-PickupMeasure-ImplicitMeasures.xml b/test-data/musicxml-testsuite/46d-PickupMeasure-ImplicitMeasures.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/46d-PickupMeasure-ImplicitMeasures.xml rename to test-data/musicxml-testsuite/46d-PickupMeasure-ImplicitMeasures.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/46e-PickupMeasure-SecondVoiceStartsLater.xml b/test-data/musicxml-testsuite/46e-PickupMeasure-SecondVoiceStartsLater.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/46e-PickupMeasure-SecondVoiceStartsLater.xml rename to test-data/musicxml-testsuite/46e-PickupMeasure-SecondVoiceStartsLater.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/46f-IncompleteMeasures.xml b/test-data/musicxml-testsuite/46f-IncompleteMeasures.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/46f-IncompleteMeasures.xml rename to test-data/musicxml-testsuite/46f-IncompleteMeasures.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/46g-PickupMeasure-Chordnames-FiguredBass.xml b/test-data/musicxml-testsuite/46g-PickupMeasure-Chordnames-FiguredBass.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/46g-PickupMeasure-Chordnames-FiguredBass.xml rename to test-data/musicxml-testsuite/46g-PickupMeasure-Chordnames-FiguredBass.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/51b-Header-Quotes.xml b/test-data/musicxml-testsuite/51b-Header-Quotes.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/51b-Header-Quotes.xml rename to test-data/musicxml-testsuite/51b-Header-Quotes.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/51c-MultipleRights.xml b/test-data/musicxml-testsuite/51c-MultipleRights.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/51c-MultipleRights.xml rename to test-data/musicxml-testsuite/51c-MultipleRights.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/51d-EmptyTitle.xml b/test-data/musicxml-testsuite/51d-EmptyTitle.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/51d-EmptyTitle.xml rename to test-data/musicxml-testsuite/51d-EmptyTitle.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/52a-PageLayout.xml b/test-data/musicxml-testsuite/52a-PageLayout.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/52a-PageLayout.xml rename to test-data/musicxml-testsuite/52a-PageLayout.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/52b-Breaks.xml b/test-data/musicxml-testsuite/52b-Breaks.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/52b-Breaks.xml rename to test-data/musicxml-testsuite/52b-Breaks.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61a-Lyrics.xml b/test-data/musicxml-testsuite/61a-Lyrics.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61a-Lyrics.xml rename to test-data/musicxml-testsuite/61a-Lyrics.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61b-MultipleLyrics.xml b/test-data/musicxml-testsuite/61b-MultipleLyrics.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61b-MultipleLyrics.xml rename to test-data/musicxml-testsuite/61b-MultipleLyrics.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61c-Lyrics-Pianostaff.xml b/test-data/musicxml-testsuite/61c-Lyrics-Pianostaff.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61c-Lyrics-Pianostaff.xml rename to test-data/musicxml-testsuite/61c-Lyrics-Pianostaff.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61d-Lyrics-Melisma.xml b/test-data/musicxml-testsuite/61d-Lyrics-Melisma.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61d-Lyrics-Melisma.xml rename to test-data/musicxml-testsuite/61d-Lyrics-Melisma.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61e-Lyrics-Chords.xml b/test-data/musicxml-testsuite/61e-Lyrics-Chords.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61e-Lyrics-Chords.xml rename to test-data/musicxml-testsuite/61e-Lyrics-Chords.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61f-Lyrics-GracedNotes.xml b/test-data/musicxml-testsuite/61f-Lyrics-GracedNotes.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61f-Lyrics-GracedNotes.xml rename to test-data/musicxml-testsuite/61f-Lyrics-GracedNotes.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61g-Lyrics-NameNumber.xml b/test-data/musicxml-testsuite/61g-Lyrics-NameNumber.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61g-Lyrics-NameNumber.xml rename to test-data/musicxml-testsuite/61g-Lyrics-NameNumber.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61h-Lyrics-BeamsMelismata.xml b/test-data/musicxml-testsuite/61h-Lyrics-BeamsMelismata.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61h-Lyrics-BeamsMelismata.xml rename to test-data/musicxml-testsuite/61h-Lyrics-BeamsMelismata.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61i-Lyrics-Chords.xml b/test-data/musicxml-testsuite/61i-Lyrics-Chords.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61i-Lyrics-Chords.xml rename to test-data/musicxml-testsuite/61i-Lyrics-Chords.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61j-Lyrics-Elisions.xml b/test-data/musicxml-testsuite/61j-Lyrics-Elisions.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61j-Lyrics-Elisions.xml rename to test-data/musicxml-testsuite/61j-Lyrics-Elisions.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61k-Lyrics-SpannersExtenders.xml b/test-data/musicxml-testsuite/61k-Lyrics-SpannersExtenders.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/61k-Lyrics-SpannersExtenders.xml rename to test-data/musicxml-testsuite/61k-Lyrics-SpannersExtenders.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/71a-Chordnames.xml b/test-data/musicxml-testsuite/71a-Chordnames.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/71a-Chordnames.xml rename to test-data/musicxml-testsuite/71a-Chordnames.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/71c-ChordsFrets.xml b/test-data/musicxml-testsuite/71c-ChordsFrets.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/71c-ChordsFrets.xml rename to test-data/musicxml-testsuite/71c-ChordsFrets.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/71d-ChordsFrets-Multistaff.xml b/test-data/musicxml-testsuite/71d-ChordsFrets-Multistaff.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/71d-ChordsFrets-Multistaff.xml rename to test-data/musicxml-testsuite/71d-ChordsFrets-Multistaff.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/71e-TabStaves.xml b/test-data/musicxml-testsuite/71e-TabStaves.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/71e-TabStaves.xml rename to test-data/musicxml-testsuite/71e-TabStaves.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/71f-AllChordTypes.xml b/test-data/musicxml-testsuite/71f-AllChordTypes.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/71f-AllChordTypes.xml rename to test-data/musicxml-testsuite/71f-AllChordTypes.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/71g-MultipleChordnames.xml b/test-data/musicxml-testsuite/71g-MultipleChordnames.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/71g-MultipleChordnames.xml rename to test-data/musicxml-testsuite/71g-MultipleChordnames.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/72a-TransposingInstruments.xml b/test-data/musicxml-testsuite/72a-TransposingInstruments.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/72a-TransposingInstruments.xml rename to test-data/musicxml-testsuite/72a-TransposingInstruments.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/72b-TransposingInstruments-Full.xml b/test-data/musicxml-testsuite/72b-TransposingInstruments-Full.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/72b-TransposingInstruments-Full.xml rename to test-data/musicxml-testsuite/72b-TransposingInstruments-Full.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/72c-TransposingInstruments-Change.xml b/test-data/musicxml-testsuite/72c-TransposingInstruments-Change.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/72c-TransposingInstruments-Change.xml rename to test-data/musicxml-testsuite/72c-TransposingInstruments-Change.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/73a-Percussion.xml b/test-data/musicxml-testsuite/73a-Percussion.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/73a-Percussion.xml rename to test-data/musicxml-testsuite/73a-Percussion.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/74a-FiguredBass.xml b/test-data/musicxml-testsuite/74a-FiguredBass.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/74a-FiguredBass.xml rename to test-data/musicxml-testsuite/74a-FiguredBass.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/75a-AccordionRegistrations.xml b/test-data/musicxml-testsuite/75a-AccordionRegistrations.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/75a-AccordionRegistrations.xml rename to test-data/musicxml-testsuite/75a-AccordionRegistrations.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/99a-Sibelius5-IgnoreBeaming.xml b/test-data/musicxml-testsuite/99a-Sibelius5-IgnoreBeaming.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/99a-Sibelius5-IgnoreBeaming.xml rename to test-data/musicxml-testsuite/99a-Sibelius5-IgnoreBeaming.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/99b-Lyrics-BeamsMelismata-IgnoreBeams.xml b/test-data/musicxml-testsuite/99b-Lyrics-BeamsMelismata-IgnoreBeams.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/99b-Lyrics-BeamsMelismata-IgnoreBeams.xml rename to test-data/musicxml-testsuite/99b-Lyrics-BeamsMelismata-IgnoreBeams.xml diff --git a/Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/LICENSE b/test-data/musicxml-testsuite/LICENSE similarity index 100% rename from Source/AlphaTab.Test/TestFiles/MusicXmlTestSuite/LICENSE rename to test-data/musicxml-testsuite/LICENSE diff --git a/test-data/visual-tests/features/effects-and-annotations/bends.gp b/test-data/visual-tests/features/effects-and-annotations/bends.gp new file mode 100644 index 000000000..0c761be3b Binary files /dev/null and b/test-data/visual-tests/features/effects-and-annotations/bends.gp differ diff --git a/test-data/visual-tests/features/effects-and-annotations/bends.png b/test-data/visual-tests/features/effects-and-annotations/bends.png new file mode 100644 index 000000000..1aeaeb86f Binary files /dev/null and b/test-data/visual-tests/features/effects-and-annotations/bends.png differ diff --git a/Documentation/input/assets/files/features/Brush.gp5 b/test-data/visual-tests/features/effects-and-annotations/brush.gp5 similarity index 100% rename from Documentation/input/assets/files/features/Brush.gp5 rename to test-data/visual-tests/features/effects-and-annotations/brush.gp5 diff --git a/test-data/visual-tests/features/effects-and-annotations/brush.png b/test-data/visual-tests/features/effects-and-annotations/brush.png new file mode 100644 index 000000000..cca0dd777 Binary files /dev/null and b/test-data/visual-tests/features/effects-and-annotations/brush.png differ diff --git a/Documentation/input/assets/files/features/Chords.gp5 b/test-data/visual-tests/features/effects-and-annotations/chords.gp5 similarity index 100% rename from Documentation/input/assets/files/features/Chords.gp5 rename to test-data/visual-tests/features/effects-and-annotations/chords.gp5 diff --git a/test-data/visual-tests/features/effects-and-annotations/chords.png b/test-data/visual-tests/features/effects-and-annotations/chords.png new file mode 100644 index 000000000..5e77da1c8 Binary files /dev/null and b/test-data/visual-tests/features/effects-and-annotations/chords.png differ diff --git a/Documentation/input/assets/files/features/Dynamics.gp5 b/test-data/visual-tests/features/effects-and-annotations/dynamics.gp5 similarity index 100% rename from Documentation/input/assets/files/features/Dynamics.gp5 rename to test-data/visual-tests/features/effects-and-annotations/dynamics.gp5 diff --git a/test-data/visual-tests/features/effects-and-annotations/dynamics.png b/test-data/visual-tests/features/effects-and-annotations/dynamics.png new file mode 100644 index 000000000..20cc430c2 Binary files /dev/null and b/test-data/visual-tests/features/effects-and-annotations/dynamics.png differ diff --git a/Documentation/input/assets/files/features/FadeIn.gp5 b/test-data/visual-tests/features/effects-and-annotations/fade-in.gp5 similarity index 100% rename from Documentation/input/assets/files/features/FadeIn.gp5 rename to test-data/visual-tests/features/effects-and-annotations/fade-in.gp5 diff --git a/test-data/visual-tests/features/effects-and-annotations/fade-in.png b/test-data/visual-tests/features/effects-and-annotations/fade-in.png new file mode 100644 index 000000000..576afd175 Binary files /dev/null and b/test-data/visual-tests/features/effects-and-annotations/fade-in.png differ diff --git a/Documentation/input/assets/files/features/Fingering.gpx b/test-data/visual-tests/features/effects-and-annotations/fingering.gpx similarity index 100% rename from Documentation/input/assets/files/features/Fingering.gpx rename to test-data/visual-tests/features/effects-and-annotations/fingering.gpx diff --git a/test-data/visual-tests/features/effects-and-annotations/fingering.png b/test-data/visual-tests/features/effects-and-annotations/fingering.png new file mode 100644 index 000000000..ed036927f Binary files /dev/null and b/test-data/visual-tests/features/effects-and-annotations/fingering.png differ diff --git a/Documentation/input/assets/files/features/LetRing.gp5 b/test-data/visual-tests/features/effects-and-annotations/let-ring.gp5 similarity index 100% rename from Documentation/input/assets/files/features/LetRing.gp5 rename to test-data/visual-tests/features/effects-and-annotations/let-ring.gp5 diff --git a/test-data/visual-tests/features/effects-and-annotations/let-ring.png b/test-data/visual-tests/features/effects-and-annotations/let-ring.png new file mode 100644 index 000000000..6432f8867 Binary files /dev/null and b/test-data/visual-tests/features/effects-and-annotations/let-ring.png differ diff --git a/Documentation/input/assets/files/features/Markers.gp5 b/test-data/visual-tests/features/effects-and-annotations/markers.gp5 similarity index 100% rename from Documentation/input/assets/files/features/Markers.gp5 rename to test-data/visual-tests/features/effects-and-annotations/markers.gp5 diff --git a/test-data/visual-tests/features/effects-and-annotations/markers.png b/test-data/visual-tests/features/effects-and-annotations/markers.png new file mode 100644 index 000000000..d8cec7878 Binary files /dev/null and b/test-data/visual-tests/features/effects-and-annotations/markers.png differ diff --git a/Documentation/input/assets/files/features/PalmMute.gp5 b/test-data/visual-tests/features/effects-and-annotations/palm-mute.gp5 similarity index 100% rename from Documentation/input/assets/files/features/PalmMute.gp5 rename to test-data/visual-tests/features/effects-and-annotations/palm-mute.gp5 diff --git a/test-data/visual-tests/features/effects-and-annotations/palm-mute.png b/test-data/visual-tests/features/effects-and-annotations/palm-mute.png new file mode 100644 index 000000000..5264a1778 Binary files /dev/null and b/test-data/visual-tests/features/effects-and-annotations/palm-mute.png differ diff --git a/Documentation/input/assets/files/features/PickStroke.gp5 b/test-data/visual-tests/features/effects-and-annotations/pick-stroke.gp5 similarity index 100% rename from Documentation/input/assets/files/features/PickStroke.gp5 rename to test-data/visual-tests/features/effects-and-annotations/pick-stroke.gp5 diff --git a/test-data/visual-tests/features/effects-and-annotations/pick-stroke.png b/test-data/visual-tests/features/effects-and-annotations/pick-stroke.png new file mode 100644 index 000000000..fd4be4279 Binary files /dev/null and b/test-data/visual-tests/features/effects-and-annotations/pick-stroke.png differ diff --git a/Documentation/input/assets/files/features/Slides.gp5 b/test-data/visual-tests/features/effects-and-annotations/slides.gp5 similarity index 100% rename from Documentation/input/assets/files/features/Slides.gp5 rename to test-data/visual-tests/features/effects-and-annotations/slides.gp5 diff --git a/test-data/visual-tests/features/effects-and-annotations/slides.png b/test-data/visual-tests/features/effects-and-annotations/slides.png new file mode 100644 index 000000000..e33c221b5 Binary files /dev/null and b/test-data/visual-tests/features/effects-and-annotations/slides.png differ diff --git a/Documentation/input/assets/files/features/Tap.gp5 b/test-data/visual-tests/features/effects-and-annotations/tap.gp5 similarity index 100% rename from Documentation/input/assets/files/features/Tap.gp5 rename to test-data/visual-tests/features/effects-and-annotations/tap.gp5 diff --git a/test-data/visual-tests/features/effects-and-annotations/tap.png b/test-data/visual-tests/features/effects-and-annotations/tap.png new file mode 100644 index 000000000..2dcb49df2 Binary files /dev/null and b/test-data/visual-tests/features/effects-and-annotations/tap.png differ diff --git a/Documentation/input/assets/files/features/Tempo.gp5 b/test-data/visual-tests/features/effects-and-annotations/tempo.gp5 similarity index 100% rename from Documentation/input/assets/files/features/Tempo.gp5 rename to test-data/visual-tests/features/effects-and-annotations/tempo.gp5 diff --git a/test-data/visual-tests/features/effects-and-annotations/tempo.png b/test-data/visual-tests/features/effects-and-annotations/tempo.png new file mode 100644 index 000000000..d57ce3d73 Binary files /dev/null and b/test-data/visual-tests/features/effects-and-annotations/tempo.png differ diff --git a/Documentation/input/assets/files/features/Text.gp5 b/test-data/visual-tests/features/effects-and-annotations/text.gp5 similarity index 100% rename from Documentation/input/assets/files/features/Text.gp5 rename to test-data/visual-tests/features/effects-and-annotations/text.gp5 diff --git a/test-data/visual-tests/features/effects-and-annotations/text.png b/test-data/visual-tests/features/effects-and-annotations/text.png new file mode 100644 index 000000000..5a9e79b71 Binary files /dev/null and b/test-data/visual-tests/features/effects-and-annotations/text.png differ diff --git a/test-data/visual-tests/features/effects-and-annotations/tremolo-bar.gp b/test-data/visual-tests/features/effects-and-annotations/tremolo-bar.gp new file mode 100644 index 000000000..71834b2ee Binary files /dev/null and b/test-data/visual-tests/features/effects-and-annotations/tremolo-bar.gp differ diff --git a/test-data/visual-tests/features/effects-and-annotations/tremolo-bar.png b/test-data/visual-tests/features/effects-and-annotations/tremolo-bar.png new file mode 100644 index 000000000..36675a994 Binary files /dev/null and b/test-data/visual-tests/features/effects-and-annotations/tremolo-bar.png differ diff --git a/Documentation/input/assets/files/features/TremoloPicking.gp5 b/test-data/visual-tests/features/effects-and-annotations/tremolo-picking.gp5 similarity index 100% rename from Documentation/input/assets/files/features/TremoloPicking.gp5 rename to test-data/visual-tests/features/effects-and-annotations/tremolo-picking.gp5 diff --git a/test-data/visual-tests/features/effects-and-annotations/tremolo-picking.png b/test-data/visual-tests/features/effects-and-annotations/tremolo-picking.png new file mode 100644 index 000000000..9b5b7d0b8 Binary files /dev/null and b/test-data/visual-tests/features/effects-and-annotations/tremolo-picking.png differ diff --git a/Documentation/input/assets/files/features/Trill.gp5 b/test-data/visual-tests/features/effects-and-annotations/trill.gp5 similarity index 100% rename from Documentation/input/assets/files/features/Trill.gp5 rename to test-data/visual-tests/features/effects-and-annotations/trill.gp5 diff --git a/test-data/visual-tests/features/effects-and-annotations/trill.png b/test-data/visual-tests/features/effects-and-annotations/trill.png new file mode 100644 index 000000000..da47bbdc7 Binary files /dev/null and b/test-data/visual-tests/features/effects-and-annotations/trill.png differ diff --git a/Documentation/input/assets/files/features/TripletFeel.gpx b/test-data/visual-tests/features/effects-and-annotations/triplet-feel.gpx similarity index 100% rename from Documentation/input/assets/files/features/TripletFeel.gpx rename to test-data/visual-tests/features/effects-and-annotations/triplet-feel.gpx diff --git a/test-data/visual-tests/features/effects-and-annotations/triplet-feel.png b/test-data/visual-tests/features/effects-and-annotations/triplet-feel.png new file mode 100644 index 000000000..85451fad9 Binary files /dev/null and b/test-data/visual-tests/features/effects-and-annotations/triplet-feel.png differ diff --git a/test-data/visual-tests/features/effects-and-annotations/tuplets-advanced.gp b/test-data/visual-tests/features/effects-and-annotations/tuplets-advanced.gp new file mode 100644 index 000000000..3b41962f3 Binary files /dev/null and b/test-data/visual-tests/features/effects-and-annotations/tuplets-advanced.gp differ diff --git a/test-data/visual-tests/features/effects-and-annotations/tuplets-advanced.png b/test-data/visual-tests/features/effects-and-annotations/tuplets-advanced.png new file mode 100644 index 000000000..ef6e9c5ef Binary files /dev/null and b/test-data/visual-tests/features/effects-and-annotations/tuplets-advanced.png differ diff --git a/Documentation/input/assets/files/features/Tuplets.gp5 b/test-data/visual-tests/features/effects-and-annotations/tuplets.gp5 similarity index 100% rename from Documentation/input/assets/files/features/Tuplets.gp5 rename to test-data/visual-tests/features/effects-and-annotations/tuplets.gp5 diff --git a/test-data/visual-tests/features/effects-and-annotations/tuplets.png b/test-data/visual-tests/features/effects-and-annotations/tuplets.png new file mode 100644 index 000000000..038759d17 Binary files /dev/null and b/test-data/visual-tests/features/effects-and-annotations/tuplets.png differ diff --git a/Documentation/input/assets/files/features/Vibrato.gp5 b/test-data/visual-tests/features/effects-and-annotations/vibrato.gp5 similarity index 100% rename from Documentation/input/assets/files/features/Vibrato.gp5 rename to test-data/visual-tests/features/effects-and-annotations/vibrato.gp5 diff --git a/test-data/visual-tests/features/effects-and-annotations/vibrato.png b/test-data/visual-tests/features/effects-and-annotations/vibrato.png new file mode 100644 index 000000000..05acb99ba Binary files /dev/null and b/test-data/visual-tests/features/effects-and-annotations/vibrato.png differ diff --git a/Documentation/input/assets/files/features/AlternateEndings.gp5 b/test-data/visual-tests/features/general/alternate-endings.gp5 similarity index 100% rename from Documentation/input/assets/files/features/AlternateEndings.gp5 rename to test-data/visual-tests/features/general/alternate-endings.gp5 diff --git a/test-data/visual-tests/features/general/alternate-endings.png b/test-data/visual-tests/features/general/alternate-endings.png new file mode 100644 index 000000000..49847584d Binary files /dev/null and b/test-data/visual-tests/features/general/alternate-endings.png differ diff --git a/test-data/visual-tests/features/general/notation-legend-default.png b/test-data/visual-tests/features/general/notation-legend-default.png new file mode 100644 index 000000000..300531f29 Binary files /dev/null and b/test-data/visual-tests/features/general/notation-legend-default.png differ diff --git a/test-data/visual-tests/features/general/notation-legend-songbook.png b/test-data/visual-tests/features/general/notation-legend-songbook.png new file mode 100644 index 000000000..68606f4e9 Binary files /dev/null and b/test-data/visual-tests/features/general/notation-legend-songbook.png differ diff --git a/test-data/visual-tests/features/general/notation-legend.gp b/test-data/visual-tests/features/general/notation-legend.gp new file mode 100644 index 000000000..9980f8fc9 Binary files /dev/null and b/test-data/visual-tests/features/general/notation-legend.gp differ diff --git a/Documentation/input/assets/files/features/Repeats.gp5 b/test-data/visual-tests/features/general/repeats.gp5 similarity index 100% rename from Documentation/input/assets/files/features/Repeats.gp5 rename to test-data/visual-tests/features/general/repeats.gp5 diff --git a/test-data/visual-tests/features/general/repeats.png b/test-data/visual-tests/features/general/repeats.png new file mode 100644 index 000000000..4bd1669d6 Binary files /dev/null and b/test-data/visual-tests/features/general/repeats.png differ diff --git a/Documentation/input/assets/files/features/SongDetails.gp5 b/test-data/visual-tests/features/general/song-details.gp5 similarity index 100% rename from Documentation/input/assets/files/features/SongDetails.gp5 rename to test-data/visual-tests/features/general/song-details.gp5 diff --git a/test-data/visual-tests/features/general/song-details.png b/test-data/visual-tests/features/general/song-details.png new file mode 100644 index 000000000..1203273ce Binary files /dev/null and b/test-data/visual-tests/features/general/song-details.png differ diff --git a/Documentation/input/assets/files/features/Tuning.gp5 b/test-data/visual-tests/features/general/tuning.gp5 similarity index 100% rename from Documentation/input/assets/files/features/Tuning.gp5 rename to test-data/visual-tests/features/general/tuning.gp5 diff --git a/test-data/visual-tests/features/general/tuning.png b/test-data/visual-tests/features/general/tuning.png new file mode 100644 index 000000000..ada832186 Binary files /dev/null and b/test-data/visual-tests/features/general/tuning.png differ diff --git a/Documentation/input/assets/files/features/Rhythm.gp5 b/test-data/visual-tests/features/guitar-tabs/rhythm-with-beams.gp5 similarity index 100% rename from Documentation/input/assets/files/features/Rhythm.gp5 rename to test-data/visual-tests/features/guitar-tabs/rhythm-with-beams.gp5 diff --git a/test-data/visual-tests/features/guitar-tabs/rhythm-with-beams.png b/test-data/visual-tests/features/guitar-tabs/rhythm-with-beams.png new file mode 100644 index 000000000..90be14ead Binary files /dev/null and b/test-data/visual-tests/features/guitar-tabs/rhythm-with-beams.png differ diff --git a/test-data/visual-tests/features/guitar-tabs/rhythm.gp5 b/test-data/visual-tests/features/guitar-tabs/rhythm.gp5 new file mode 100644 index 000000000..b85640044 Binary files /dev/null and b/test-data/visual-tests/features/guitar-tabs/rhythm.gp5 differ diff --git a/test-data/visual-tests/features/guitar-tabs/rhythm.png b/test-data/visual-tests/features/guitar-tabs/rhythm.png new file mode 100644 index 000000000..302e8f11f Binary files /dev/null and b/test-data/visual-tests/features/guitar-tabs/rhythm.png differ diff --git a/Documentation/input/assets/files/features/Tabs.gp5 b/test-data/visual-tests/features/guitar-tabs/string-variations.gp5 similarity index 100% rename from Documentation/input/assets/files/features/Tabs.gp5 rename to test-data/visual-tests/features/guitar-tabs/string-variations.gp5 diff --git a/test-data/visual-tests/features/guitar-tabs/string-variations.png b/test-data/visual-tests/features/guitar-tabs/string-variations.png new file mode 100644 index 000000000..e8f1fb48c Binary files /dev/null and b/test-data/visual-tests/features/guitar-tabs/string-variations.png differ diff --git a/test-data/visual-tests/features/layout/grace-notes-advanced.png b/test-data/visual-tests/features/layout/grace-notes-advanced.png new file mode 100644 index 000000000..21b441d59 Binary files /dev/null and b/test-data/visual-tests/features/layout/grace-notes-advanced.png differ diff --git a/Documentation/input/assets/files/features/Skillet.gp5 b/test-data/visual-tests/features/layout/horizontal-layout-5to8.gp5 similarity index 100% rename from Documentation/input/assets/files/features/Skillet.gp5 rename to test-data/visual-tests/features/layout/horizontal-layout-5to8.gp5 diff --git a/test-data/visual-tests/features/layout/horizontal-layout-5to8.png b/test-data/visual-tests/features/layout/horizontal-layout-5to8.png new file mode 100644 index 000000000..c55159c84 Binary files /dev/null and b/test-data/visual-tests/features/layout/horizontal-layout-5to8.png differ diff --git a/test-data/visual-tests/features/layout/horizontal-layout.gp5 b/test-data/visual-tests/features/layout/horizontal-layout.gp5 new file mode 100644 index 000000000..791bd79be Binary files /dev/null and b/test-data/visual-tests/features/layout/horizontal-layout.gp5 differ diff --git a/test-data/visual-tests/features/layout/horizontal-layout.png b/test-data/visual-tests/features/layout/horizontal-layout.png new file mode 100644 index 000000000..0d08a23c7 Binary files /dev/null and b/test-data/visual-tests/features/layout/horizontal-layout.png differ diff --git a/test-data/visual-tests/features/layout/multi-track.gp5 b/test-data/visual-tests/features/layout/multi-track.gp5 new file mode 100644 index 000000000..791bd79be Binary files /dev/null and b/test-data/visual-tests/features/layout/multi-track.gp5 differ diff --git a/test-data/visual-tests/features/layout/multi-track.png b/test-data/visual-tests/features/layout/multi-track.png new file mode 100644 index 000000000..f4af76fad Binary files /dev/null and b/test-data/visual-tests/features/layout/multi-track.png differ diff --git a/Documentation/input/assets/files/features/MultiVoice.gp5 b/test-data/visual-tests/features/layout/multi-voice.gp5 similarity index 100% rename from Documentation/input/assets/files/features/MultiVoice.gp5 rename to test-data/visual-tests/features/layout/multi-voice.gp5 diff --git a/test-data/visual-tests/features/layout/multi-voice.png b/test-data/visual-tests/features/layout/multi-voice.png new file mode 100644 index 000000000..000000382 Binary files /dev/null and b/test-data/visual-tests/features/layout/multi-voice.png differ diff --git a/test-data/visual-tests/features/layout/page-layout-5barsperrow.gp5 b/test-data/visual-tests/features/layout/page-layout-5barsperrow.gp5 new file mode 100644 index 000000000..791bd79be Binary files /dev/null and b/test-data/visual-tests/features/layout/page-layout-5barsperrow.gp5 differ diff --git a/test-data/visual-tests/features/layout/page-layout-5barsperrow.png b/test-data/visual-tests/features/layout/page-layout-5barsperrow.png new file mode 100644 index 000000000..20c5df2e8 Binary files /dev/null and b/test-data/visual-tests/features/layout/page-layout-5barsperrow.png differ diff --git a/test-data/visual-tests/features/layout/page-layout-5to8.gp5 b/test-data/visual-tests/features/layout/page-layout-5to8.gp5 new file mode 100644 index 000000000..791bd79be Binary files /dev/null and b/test-data/visual-tests/features/layout/page-layout-5to8.gp5 differ diff --git a/test-data/visual-tests/features/layout/page-layout-5to8.png b/test-data/visual-tests/features/layout/page-layout-5to8.png new file mode 100644 index 000000000..cb9edd681 Binary files /dev/null and b/test-data/visual-tests/features/layout/page-layout-5to8.png differ diff --git a/test-data/visual-tests/features/layout/page-layout.gp5 b/test-data/visual-tests/features/layout/page-layout.gp5 new file mode 100644 index 000000000..791bd79be Binary files /dev/null and b/test-data/visual-tests/features/layout/page-layout.gp5 differ diff --git a/test-data/visual-tests/features/layout/page-layout.png b/test-data/visual-tests/features/layout/page-layout.png new file mode 100644 index 000000000..a51126f5c Binary files /dev/null and b/test-data/visual-tests/features/layout/page-layout.png differ diff --git a/test-data/visual-tests/features/layout/tied-notes.png b/test-data/visual-tests/features/layout/tied-notes.png new file mode 100644 index 000000000..407995f9f Binary files /dev/null and b/test-data/visual-tests/features/layout/tied-notes.png differ diff --git a/Documentation/input/assets/files/features/Accidentals.gp5 b/test-data/visual-tests/features/music-notation/accidentals.gp5 similarity index 100% rename from Documentation/input/assets/files/features/Accidentals.gp5 rename to test-data/visual-tests/features/music-notation/accidentals.gp5 diff --git a/test-data/visual-tests/features/music-notation/accidentals.png b/test-data/visual-tests/features/music-notation/accidentals.png new file mode 100644 index 000000000..308c38494 Binary files /dev/null and b/test-data/visual-tests/features/music-notation/accidentals.png differ diff --git a/Documentation/input/assets/files/features/Clefs.gpx b/test-data/visual-tests/features/music-notation/clefs.gpx similarity index 100% rename from Documentation/input/assets/files/features/Clefs.gpx rename to test-data/visual-tests/features/music-notation/clefs.gpx diff --git a/test-data/visual-tests/features/music-notation/clefs.png b/test-data/visual-tests/features/music-notation/clefs.png new file mode 100644 index 000000000..b092cba3a Binary files /dev/null and b/test-data/visual-tests/features/music-notation/clefs.png differ diff --git a/Documentation/input/assets/files/features/KeySignatures.gp5 b/test-data/visual-tests/features/music-notation/key-signatures.gp5 similarity index 100% rename from Documentation/input/assets/files/features/KeySignatures.gp5 rename to test-data/visual-tests/features/music-notation/key-signatures.gp5 diff --git a/test-data/visual-tests/features/music-notation/key-signatures.png b/test-data/visual-tests/features/music-notation/key-signatures.png new file mode 100644 index 000000000..167f40cca Binary files /dev/null and b/test-data/visual-tests/features/music-notation/key-signatures.png differ diff --git a/Documentation/input/assets/files/features/Beams.gp5 b/test-data/visual-tests/features/music-notation/notes-rests-beams.gp5 similarity index 100% rename from Documentation/input/assets/files/features/Beams.gp5 rename to test-data/visual-tests/features/music-notation/notes-rests-beams.gp5 diff --git a/test-data/visual-tests/features/music-notation/notes-rests-beams.png b/test-data/visual-tests/features/music-notation/notes-rests-beams.png new file mode 100644 index 000000000..7117ff4a2 Binary files /dev/null and b/test-data/visual-tests/features/music-notation/notes-rests-beams.png differ diff --git a/Documentation/input/assets/files/features/TimeSignatures.gp5 b/test-data/visual-tests/features/music-notation/time-signatures.gp5 similarity index 100% rename from Documentation/input/assets/files/features/TimeSignatures.gp5 rename to test-data/visual-tests/features/music-notation/time-signatures.gp5 diff --git a/test-data/visual-tests/features/music-notation/time-signatures.png b/test-data/visual-tests/features/music-notation/time-signatures.png new file mode 100644 index 000000000..d0cbce33c Binary files /dev/null and b/test-data/visual-tests/features/music-notation/time-signatures.png differ diff --git a/test-data/visual-tests/features/notation-elements/chord-diagrams-off.png b/test-data/visual-tests/features/notation-elements/chord-diagrams-off.png new file mode 100644 index 000000000..fda8ffcda Binary files /dev/null and b/test-data/visual-tests/features/notation-elements/chord-diagrams-off.png differ diff --git a/test-data/visual-tests/features/notation-elements/chord-diagrams-on.png b/test-data/visual-tests/features/notation-elements/chord-diagrams-on.png new file mode 100644 index 000000000..77e7b796c Binary files /dev/null and b/test-data/visual-tests/features/notation-elements/chord-diagrams-on.png differ diff --git a/test-data/visual-tests/features/notation-elements/effects-off.png b/test-data/visual-tests/features/notation-elements/effects-off.png new file mode 100644 index 000000000..100d6cb27 Binary files /dev/null and b/test-data/visual-tests/features/notation-elements/effects-off.png differ diff --git a/test-data/visual-tests/features/notation-elements/effects-on.png b/test-data/visual-tests/features/notation-elements/effects-on.png new file mode 100644 index 000000000..9d12c2189 Binary files /dev/null and b/test-data/visual-tests/features/notation-elements/effects-on.png differ diff --git a/test-data/visual-tests/features/notation-elements/guitar-tuning-off.png b/test-data/visual-tests/features/notation-elements/guitar-tuning-off.png new file mode 100644 index 000000000..2fbf13f3d Binary files /dev/null and b/test-data/visual-tests/features/notation-elements/guitar-tuning-off.png differ diff --git a/test-data/visual-tests/features/notation-elements/guitar-tuning-on.png b/test-data/visual-tests/features/notation-elements/guitar-tuning-on.png new file mode 100644 index 000000000..0c6573f44 Binary files /dev/null and b/test-data/visual-tests/features/notation-elements/guitar-tuning-on.png differ diff --git a/test-data/visual-tests/features/notation-elements/parenthesis-on-tied-bends-off.png b/test-data/visual-tests/features/notation-elements/parenthesis-on-tied-bends-off.png new file mode 100644 index 000000000..8136b41cb Binary files /dev/null and b/test-data/visual-tests/features/notation-elements/parenthesis-on-tied-bends-off.png differ diff --git a/test-data/visual-tests/features/notation-elements/parenthesis-on-tied-bends-on.png b/test-data/visual-tests/features/notation-elements/parenthesis-on-tied-bends-on.png new file mode 100644 index 000000000..f7ed5952b Binary files /dev/null and b/test-data/visual-tests/features/notation-elements/parenthesis-on-tied-bends-on.png differ diff --git a/test-data/visual-tests/features/notation-elements/score-info-album.png b/test-data/visual-tests/features/notation-elements/score-info-album.png new file mode 100644 index 000000000..21dfdda3b Binary files /dev/null and b/test-data/visual-tests/features/notation-elements/score-info-album.png differ diff --git a/test-data/visual-tests/features/notation-elements/score-info-all.png b/test-data/visual-tests/features/notation-elements/score-info-all.png new file mode 100644 index 000000000..fed896d98 Binary files /dev/null and b/test-data/visual-tests/features/notation-elements/score-info-all.png differ diff --git a/test-data/visual-tests/features/notation-elements/score-info-artist.png b/test-data/visual-tests/features/notation-elements/score-info-artist.png new file mode 100644 index 000000000..05c8f60bb Binary files /dev/null and b/test-data/visual-tests/features/notation-elements/score-info-artist.png differ diff --git a/test-data/visual-tests/features/notation-elements/score-info-copyright.png b/test-data/visual-tests/features/notation-elements/score-info-copyright.png new file mode 100644 index 000000000..854a81480 Binary files /dev/null and b/test-data/visual-tests/features/notation-elements/score-info-copyright.png differ diff --git a/test-data/visual-tests/features/notation-elements/score-info-music.png b/test-data/visual-tests/features/notation-elements/score-info-music.png new file mode 100644 index 000000000..3daf596b8 Binary files /dev/null and b/test-data/visual-tests/features/notation-elements/score-info-music.png differ diff --git a/test-data/visual-tests/features/notation-elements/score-info-subtitle.png b/test-data/visual-tests/features/notation-elements/score-info-subtitle.png new file mode 100644 index 000000000..515628515 Binary files /dev/null and b/test-data/visual-tests/features/notation-elements/score-info-subtitle.png differ diff --git a/test-data/visual-tests/features/notation-elements/score-info-title.png b/test-data/visual-tests/features/notation-elements/score-info-title.png new file mode 100644 index 000000000..612a19bcc Binary files /dev/null and b/test-data/visual-tests/features/notation-elements/score-info-title.png differ diff --git a/test-data/visual-tests/features/notation-elements/score-info-words-and-music.png b/test-data/visual-tests/features/notation-elements/score-info-words-and-music.png new file mode 100644 index 000000000..1ab786e54 Binary files /dev/null and b/test-data/visual-tests/features/notation-elements/score-info-words-and-music.png differ diff --git a/test-data/visual-tests/features/notation-elements/score-info-words.png b/test-data/visual-tests/features/notation-elements/score-info-words.png new file mode 100644 index 000000000..83b1ebd2d Binary files /dev/null and b/test-data/visual-tests/features/notation-elements/score-info-words.png differ diff --git a/test-data/visual-tests/features/notation-elements/tab-notes-on-tied-bends-off.png b/test-data/visual-tests/features/notation-elements/tab-notes-on-tied-bends-off.png new file mode 100644 index 000000000..5c4b2d6fe Binary files /dev/null and b/test-data/visual-tests/features/notation-elements/tab-notes-on-tied-bends-off.png differ diff --git a/test-data/visual-tests/features/notation-elements/tab-notes-on-tied-bends-on.png b/test-data/visual-tests/features/notation-elements/tab-notes-on-tied-bends-on.png new file mode 100644 index 000000000..cb6273204 Binary files /dev/null and b/test-data/visual-tests/features/notation-elements/tab-notes-on-tied-bends-on.png differ diff --git a/test-data/visual-tests/features/notation-elements/track-names-off.png b/test-data/visual-tests/features/notation-elements/track-names-off.png new file mode 100644 index 000000000..458a3208d Binary files /dev/null and b/test-data/visual-tests/features/notation-elements/track-names-off.png differ diff --git a/test-data/visual-tests/features/notation-elements/track-names-on (1).png b/test-data/visual-tests/features/notation-elements/track-names-on (1).png new file mode 100644 index 000000000..253b31bcd Binary files /dev/null and b/test-data/visual-tests/features/notation-elements/track-names-on (1).png differ diff --git a/test-data/visual-tests/features/notation-elements/track-names-on.png b/test-data/visual-tests/features/notation-elements/track-names-on.png new file mode 100644 index 000000000..253b31bcd Binary files /dev/null and b/test-data/visual-tests/features/notation-elements/track-names-on.png differ diff --git a/test-data/visual-tests/features/notation-elements/zeros-on-dive-whammys-off.png b/test-data/visual-tests/features/notation-elements/zeros-on-dive-whammys-off.png new file mode 100644 index 000000000..0724f06a0 Binary files /dev/null and b/test-data/visual-tests/features/notation-elements/zeros-on-dive-whammys-off.png differ diff --git a/test-data/visual-tests/features/notation-elements/zeros-on-dive-whammys-on.png b/test-data/visual-tests/features/notation-elements/zeros-on-dive-whammys-on.png new file mode 100644 index 000000000..fe1494e16 Binary files /dev/null and b/test-data/visual-tests/features/notation-elements/zeros-on-dive-whammys-on.png differ diff --git a/Documentation/input/assets/files/features/DeadNotes.gp5 b/test-data/visual-tests/features/special-notes/dead-notes.gp5 similarity index 100% rename from Documentation/input/assets/files/features/DeadNotes.gp5 rename to test-data/visual-tests/features/special-notes/dead-notes.gp5 diff --git a/test-data/visual-tests/features/special-notes/dead-notes.png b/test-data/visual-tests/features/special-notes/dead-notes.png new file mode 100644 index 000000000..20e3e957c Binary files /dev/null and b/test-data/visual-tests/features/special-notes/dead-notes.png differ diff --git a/Documentation/input/assets/files/features/GhostNotes.gp5 b/test-data/visual-tests/features/special-notes/ghost-notes.gp5 similarity index 100% rename from Documentation/input/assets/files/features/GhostNotes.gp5 rename to test-data/visual-tests/features/special-notes/ghost-notes.gp5 diff --git a/test-data/visual-tests/features/special-notes/ghost-notes.png b/test-data/visual-tests/features/special-notes/ghost-notes.png new file mode 100644 index 000000000..79e94063e Binary files /dev/null and b/test-data/visual-tests/features/special-notes/ghost-notes.png differ diff --git a/test-data/visual-tests/features/special-notes/grace-notes-advanced.gp b/test-data/visual-tests/features/special-notes/grace-notes-advanced.gp new file mode 100644 index 000000000..c0a901217 Binary files /dev/null and b/test-data/visual-tests/features/special-notes/grace-notes-advanced.gp differ diff --git a/test-data/visual-tests/features/special-notes/grace-notes-advanced.png b/test-data/visual-tests/features/special-notes/grace-notes-advanced.png new file mode 100644 index 000000000..21b441d59 Binary files /dev/null and b/test-data/visual-tests/features/special-notes/grace-notes-advanced.png differ diff --git a/Documentation/input/assets/files/features/GraceNotes.gp5 b/test-data/visual-tests/features/special-notes/grace-notes.gp5 similarity index 100% rename from Documentation/input/assets/files/features/GraceNotes.gp5 rename to test-data/visual-tests/features/special-notes/grace-notes.gp5 diff --git a/test-data/visual-tests/features/special-notes/grace-notes.png b/test-data/visual-tests/features/special-notes/grace-notes.png new file mode 100644 index 000000000..b036d294b Binary files /dev/null and b/test-data/visual-tests/features/special-notes/grace-notes.png differ diff --git a/Documentation/input/assets/files/features/TiedNotes.gp5 b/test-data/visual-tests/features/special-notes/tied-notes.gp5 similarity index 100% rename from Documentation/input/assets/files/features/TiedNotes.gp5 rename to test-data/visual-tests/features/special-notes/tied-notes.gp5 diff --git a/test-data/visual-tests/features/special-notes/tied-notes.png b/test-data/visual-tests/features/special-notes/tied-notes.png new file mode 100644 index 000000000..407995f9f Binary files /dev/null and b/test-data/visual-tests/features/special-notes/tied-notes.png differ diff --git a/Documentation/input/assets/files/features/Drums.gp5 b/test-data/visual-tests/features/special-tracks/drum-tabs.gp5 similarity index 100% rename from Documentation/input/assets/files/features/Drums.gp5 rename to test-data/visual-tests/features/special-tracks/drum-tabs.gp5 diff --git a/test-data/visual-tests/features/special-tracks/drum-tabs.png b/test-data/visual-tests/features/special-tracks/drum-tabs.png new file mode 100644 index 000000000..5017f56aa Binary files /dev/null and b/test-data/visual-tests/features/special-tracks/drum-tabs.png differ diff --git a/Documentation/input/assets/files/features/Piano.gpx b/test-data/visual-tests/features/special-tracks/grand-staff.gpx similarity index 100% rename from Documentation/input/assets/files/features/Piano.gpx rename to test-data/visual-tests/features/special-tracks/grand-staff.gpx diff --git a/test-data/visual-tests/features/special-tracks/grand-staff.png b/test-data/visual-tests/features/special-tracks/grand-staff.png new file mode 100644 index 000000000..2132d70a8 Binary files /dev/null and b/test-data/visual-tests/features/special-tracks/grand-staff.png differ diff --git a/Source/AlphaTab.Test/TestFiles/Xml/GPIF.xml b/test-data/xml/GPIF.xml similarity index 100% rename from Source/AlphaTab.Test/TestFiles/Xml/GPIF.xml rename to test-data/xml/GPIF.xml diff --git a/test/TestPlatform.ts b/test/TestPlatform.ts new file mode 100644 index 000000000..6fa8098de --- /dev/null +++ b/test/TestPlatform.ts @@ -0,0 +1,50 @@ +import { ByteBuffer } from '@src/io/ByteBuffer'; +import { IReadable } from '@src/io/IReadable'; +import { IOHelper } from '@src/io/IOHelper'; + +/** + * @partial + */ +export class TestPlatform { + public static createStringReader(tex: string): IReadable { + return ByteBuffer.fromString(tex); + } + + /** + * @target web + */ + public static loadFile(path: string): Promise { + return new Promise((resolve, reject) => { + let x: XMLHttpRequest = new XMLHttpRequest(); + x.open('GET', '/base/' + path, true, null, null); + x.responseType = 'arraybuffer'; + x.onreadystatechange = () => { + if (x.readyState === XMLHttpRequest.DONE) { + if (x.status === 200) { + let ab: ArrayBuffer = x.response; + let data: Uint8Array = new Uint8Array(ab); + resolve(data); + } else { + let response: string = x.response; + reject('Could not find file: ' + path + ', received:' + response); + } + } + }; + x.send(); + }); + } + + public static async loadFileAsString(path: string): Promise { + const data = await TestPlatform.loadFile(path); + return IOHelper.toString(data, 'UTF-8'); + } + + public static changeExtension(file: string, extension: string): string { + let lastDot: number = file.lastIndexOf('.'); + if (lastDot === -1) { + return file + extension; + } else { + return file.substr(0, lastDot) + extension; + } + } +} \ No newline at end of file diff --git a/test/audio/AlphaSynth.test.ts b/test/audio/AlphaSynth.test.ts new file mode 100644 index 000000000..da0ac03c8 --- /dev/null +++ b/test/audio/AlphaSynth.test.ts @@ -0,0 +1,39 @@ +import { AlphaSynthMidiFileHandler } from '@src/midi/AlphaSynthMidiFileHandler'; +import { MidiFileGenerator } from '@src/midi/MidiFileGenerator'; +import { MidiFile } from '@src/midi/MidiFile'; +import { AlphaSynth } from '@src/synth/AlphaSynth'; +import { AlphaTexImporter } from '@src/importer/AlphaTexImporter'; +import { Score } from '@src/model/Score'; +import { Settings } from '@src/Settings'; +import { TestOutput } from '@test/audio/TestOutput'; +import { TestPlatform } from '@test/TestPlatform'; + +describe('AlphaSynthTests', () => { + it('pcm-generation', async () => { + const data = await TestPlatform.loadFile('test-data/audio/default.sf2'); + let tex: string = + '\\tempo 102 \\tuning E4 B3 G3 D3 A2 E2 \\instrument 25 . r.8 (0.4 0.3 ).8 ' + + '(-.3 -.4 ).2 {d } | (0.4 0.3 ).8 r.8 (3.3 3.4 ).8 r.8 (5.4 5.3 ).4 r.8 (0.4 0.3 ).8 |' + + ' r.8 (3.4 3.3 ).8 r.8 (6.3 6.4 ).8 (5.4 5.3 ).4 {d }r.8 |' + + ' (0.4 0.3).8 r.8(3.4 3.3).8 r.8(5.4 5.3).4 r.8(3.4 3.3).8 | ' + + 'r.8(0.4 0.3).8(-.3 - .4).2 { d } | '; + let importer: AlphaTexImporter = new AlphaTexImporter(); + importer.init(TestPlatform.createStringReader(tex), new Settings()); + let score: Score = importer.readScore(); + let midi: MidiFile = new MidiFile(); + let gen: MidiFileGenerator = new MidiFileGenerator(score, null, new AlphaSynthMidiFileHandler(midi)); + gen.generate(); + let testOutput: TestOutput = new TestOutput(); + let synth: AlphaSynth = new AlphaSynth(testOutput); + synth.loadSoundFont(data); + synth.loadMidiFile(midi); + synth.play(); + let finished: boolean = false; + synth.finished.on(() => { + finished = true; + }); + while (!finished) { + testOutput.next(); + } + }); +}); diff --git a/test/audio/FlatMidiEventGenerator.ts b/test/audio/FlatMidiEventGenerator.ts new file mode 100644 index 000000000..97b72ca0a --- /dev/null +++ b/test/audio/FlatMidiEventGenerator.ts @@ -0,0 +1,335 @@ +import { ControllerType } from '@src/midi/ControllerType'; +import { IMidiFileHandler } from '@src/midi/IMidiFileHandler'; +import { DynamicValue } from '@src/model/DynamicValue'; + +export class FlatMidiEventGenerator implements IMidiFileHandler { + public midiEvents: MidiEvent[]; + + public constructor() { + this.midiEvents = []; + } + + public addTimeSignature(tick: number, timeSignatureNumerator: number, timeSignatureDenominator: number): void { + let e = new TimeSignatureEvent(tick, timeSignatureNumerator, timeSignatureDenominator); + this.midiEvents.push(e); + } + + public addRest(track: number, tick: number, channel: number): void { + let e = new RestEvent(tick, track, channel); + this.midiEvents.push(e); + } + + public addNote( + track: number, + start: number, + length: number, + key: number, + dynamicValue: DynamicValue, + channel: number + ): void { + let e = new NoteEvent(start, track, channel, length, key, dynamicValue); + this.midiEvents.push(e); + } + + public addControlChange(track: number, tick: number, channel: number, controller: number, value: number): void { + let e = new ControlChangeEvent(tick, track, channel, controller, value); + this.midiEvents.push(e); + } + + public addProgramChange(track: number, tick: number, channel: number, program: number): void { + let e = new ProgramChangeEvent(tick, track, channel, program); + this.midiEvents.push(e); + } + + public addTempo(tick: number, tempo: number): void { + let e = new TempoEvent(tick, tempo); + this.midiEvents.push(e); + } + + public addBend(track: number, tick: number, channel: number, value: number): void { + let e = new BendEvent(tick, track, channel, value); + this.midiEvents.push(e); + } + + public finishTrack(track: number, tick: number): void { + let e = new TrackEndEvent(tick, track); + this.midiEvents.push(e); + } +} + +export class MidiEvent { + public tick: number = 0; + + public constructor(tick: number) { + this.tick = tick; + } + + public toString(): string { + return `Tick[${this.tick}]`; + } + + protected equals_FlatMidiEventGenerator_MidiEvent(other: MidiEvent): boolean { + return this.tick === other.tick; + } + + public equals(obj: unknown): boolean { + if (!obj) { + return false; + } + + if (obj === this) { + return true; + } + + if (obj instanceof MidiEvent) { + return this.tick === obj.tick; + } + return false; + } +} + +export class TempoEvent extends MidiEvent { + public tempo: number = 0; + + public constructor(tick: number, tempo: number) { + super(tick); + this.tempo = tempo; + } + + public toString(): string { + return `Tempo: ${super.toString()} Tempo[${this.tempo}]`; + } + + public equals(obj: unknown): boolean { + if (!super.equals(obj)) { + return false; + } + + if (obj instanceof TempoEvent) { + return this.tempo === obj.tempo; + } + + return false; + } +} + +export class TimeSignatureEvent extends MidiEvent { + public numerator: number = 0; + public denominator: number = 0; + + public constructor(tick: number, numerator: number, denominator: number) { + super(tick); + this.numerator = numerator; + this.denominator = denominator; + } + + public toString(): string { + return `TimeSignature: ${super.toString()} Numerator[${this.numerator}] Denominator[${this.denominator}]`; + } + + public equals(obj: unknown): boolean { + if (!super.equals(obj)) { + return false; + } + + if (obj instanceof TimeSignatureEvent) { + return this.numerator === obj.numerator && this.denominator === obj.denominator; + } + + return false; + } +} + +export class TrackMidiEvent extends MidiEvent { + public track: number = 0; + + public constructor(tick: number, track: number) { + super(tick); + this.track = track; + } + + public toString(): string { + return `${super.toString()} Track[${this.track}]`; + } + + public equals(obj: unknown): boolean { + if (!super.equals(obj)) { + return false; + } + + if (obj instanceof TrackMidiEvent) { + return this.track === obj.track; + } + + return false; + } +} + +export class TrackEndEvent extends TrackMidiEvent { + public constructor(tick: number, track: number) { + super(tick, track); + } + + public toString(): string { + return 'End of Track ' + super.toString(); + } +} + +export class ChannelMidiEvent extends TrackMidiEvent { + public channel: number = 0; + + public constructor(tick: number, track: number, channel: number) { + super(tick, track); + this.channel = channel; + } + + public toString(): string { + return `${super.toString()} Channel[${this.channel}]`; + } + + public equals(obj: unknown): boolean { + if (!super.equals(obj)) { + return false; + } + + if (obj instanceof ChannelMidiEvent) { + return this.channel === obj.channel; + } + + return false; + } +} + +export class ControlChangeEvent extends ChannelMidiEvent { + public controller: ControllerType; + public value: number = 0; + + public constructor(tick: number, track: number, channel: number, controller: ControllerType, value: number) { + super(tick, track, channel); + this.controller = controller; + this.value = value; + } + + public toString(): string { + return `ControlChange: ${super.toString()} Controller[${this.controller}] Value[${this.value}]`; + } + + public equals(obj: unknown): boolean { + if (!super.equals(obj)) { + return false; + } + + if (obj instanceof ControlChangeEvent) { + return this.controller === obj.controller && this.channel === obj.channel && this.value === obj.value; + } + + return false; + } +} + +export class RestEvent extends ChannelMidiEvent { + public constructor(tick: number, track: number, channel: number) { + super(tick, track, channel); + } + + public toString(): string { + return `Rest: ${super.toString()}`; + } + + public equals(obj: unknown): boolean { + if (!super.equals(obj)) { + return false; + } + + if (obj instanceof TempoEvent) { + return true; + } + return false; + } +} + +export class ProgramChangeEvent extends ChannelMidiEvent { + public program: number = 0; + + public constructor(tick: number, track: number, channel: number, program: number) { + super(tick, track, channel); + this.program = program; + } + + public toString(): string { + return `ProgramChange: ${super.toString()} Program[${this.program}]`; + } + + public equals(obj: unknown): boolean { + if (!super.equals(obj)) { + return false; + } + + if (obj instanceof ProgramChangeEvent) { + return this.program === obj.program; + } + + return false; + } +} + +export class NoteEvent extends ChannelMidiEvent { + public length: number = 0; + public key: number = 0; + public dynamicValue: DynamicValue; + + public constructor( + tick: number, + track: number, + channel: number, + length: number, + key: number, + dynamicValue: DynamicValue + ) { + super(tick, track, channel); + this.length = length; + this.key = key; + this.dynamicValue = dynamicValue; + } + + public toString(): string { + return `Note: ${super.toString()} Length[${this.length}] Key[${this.key}] Dynamic[${this.dynamicValue}]`; + } + + public equals(obj: unknown): boolean { + if (!super.equals(obj)) { + return false; + } + + if (obj instanceof NoteEvent) { + return this.length === obj.length && this.key === obj.key && this.dynamicValue === obj.dynamicValue; + } + + return false; + } +} + +export class BendEvent extends ChannelMidiEvent { + public value: number = 0; + + public constructor(tick: number, track: number, channel: number, value: number) { + super(tick, track, channel); + this.value = value; + } + + public toString(): string { + return `Bend: ${super.toString()} Value[${this.value}]`; + } + + public equals(obj: unknown): boolean { + if (!super.equals(obj)) { + return false; + } + + if (obj instanceof BendEvent) { + return this.value === obj.value; + } + + return false; + } +} diff --git a/test/audio/MidiFileGenerator.test.ts b/test/audio/MidiFileGenerator.test.ts new file mode 100644 index 000000000..2c8223287 --- /dev/null +++ b/test/audio/MidiFileGenerator.test.ts @@ -0,0 +1,437 @@ +import { ControllerType } from '@src/midi/ControllerType'; +import { MidiEvent } from '@src/midi/MidiEvent'; +import { MidiFileGenerator } from '@src/midi/MidiFileGenerator'; +import { MidiFile } from '@src/midi/MidiFile'; +import { MidiUtils } from '@src/midi/MidiUtils'; +import { AlphaTexImporter } from '@src/importer/AlphaTexImporter'; +import { Gp3To5Importer } from '@src/importer/Gp3To5Importer'; +import { Gp7Importer } from '@src/importer/Gp7Importer'; +import { ByteBuffer } from '@src/io/ByteBuffer'; +import { Beat } from '@src/model/Beat'; +import { DynamicValue } from '@src/model/DynamicValue'; +import { GraceType } from '@src/model/GraceType'; +import { Note } from '@src/model/Note'; +import { PlaybackInformation } from '@src/model/PlaybackInformation'; +import { Score } from '@src/model/Score'; +import { Settings } from '@src/Settings'; +import { Logger } from '@src/Logger'; +import { + BendEvent, + ControlChangeEvent, + FlatMidiEventGenerator, + MidiEvent as FlatMidiEvent, + NoteEvent, + ProgramChangeEvent, + TempoEvent, + TimeSignatureEvent, + TrackEndEvent +} from '@test/audio/FlatMidiEventGenerator'; +import { TestPlatform } from '@test/TestPlatform'; + +describe('MidiFileGeneratorTest', () => { + const parseTex: (tex:string) => Score = (tex: string): Score => { + let importer: AlphaTexImporter = new AlphaTexImporter(); + importer.init(TestPlatform.createStringReader(tex), new Settings()); + return importer.readScore(); + }; + + it('full-song', async () => { + const buffer = await TestPlatform.loadFile('test-data/audio/full-song.gp5'); + let readerBase: Gp3To5Importer = new Gp3To5Importer(); + readerBase.init(ByteBuffer.fromBuffer(buffer), new Settings()); + let score: Score = readerBase.readScore(); + let generator: MidiFileGenerator = new MidiFileGenerator(score, null, new FlatMidiEventGenerator()); + generator.generate(); + }); + + it('midi-order', () => { + let midiFile: MidiFile = new MidiFile(); + midiFile.addEvent(new MidiEvent(0, 0, 0, 0)); + midiFile.addEvent(new MidiEvent(0, 0, 1, 0)); + midiFile.addEvent(new MidiEvent(100, 0, 2, 0)); + midiFile.addEvent(new MidiEvent(50, 0, 3, 0)); + midiFile.addEvent(new MidiEvent(50, 0, 4, 0)); + expect(midiFile.events[0].data1).toEqual(0); + expect(midiFile.events[1].data1).toEqual(1); + expect(midiFile.events[2].data1).toEqual(3); + expect(midiFile.events[3].data1).toEqual(4); + expect(midiFile.events[4].data1).toEqual(2); + }); + + it('bend', () => { + let tex: string = ':4 15.6{b(0 4)} 15.6'; + let score: Score = parseTex(tex); + expect(score.tracks.length).toEqual(1); + expect(score.tracks[0].staves[0].bars.length).toEqual(1); + expect(score.tracks[0].staves[0].bars[0].voices.length).toEqual(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).toEqual(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes.length).toEqual(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes.length).toEqual(1); + let handler: FlatMidiEventGenerator = new FlatMidiEventGenerator(); + let generator: MidiFileGenerator = new MidiFileGenerator(score, null, handler); + generator.generate(); + let info: PlaybackInformation = score.tracks[0].playbackInfo; + let note: Note = score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0]; + let expectedEvents: FlatMidiEvent[] = [ + // channel init + new ControlChangeEvent(0, 0, info.primaryChannel, ControllerType.VolumeCoarse, 120), + new ControlChangeEvent(0, 0, info.primaryChannel, ControllerType.PanCoarse, 64), + new ControlChangeEvent(0, 0, info.primaryChannel, ControllerType.ExpressionControllerCoarse, 127), + new ControlChangeEvent(0, 0, info.primaryChannel, ControllerType.RegisteredParameterFine, 0), + new ControlChangeEvent(0, 0, info.primaryChannel, ControllerType.RegisteredParameterCourse, 0), + new ControlChangeEvent(0, 0, info.primaryChannel, ControllerType.DataEntryFine, 0), + new ControlChangeEvent(0, 0, info.primaryChannel, ControllerType.DataEntryCoarse, 16), + new ProgramChangeEvent(0, 0, info.primaryChannel, info.program), + + new ControlChangeEvent(0, 0, info.secondaryChannel, ControllerType.VolumeCoarse, 120), + new ControlChangeEvent(0, 0, info.secondaryChannel, ControllerType.PanCoarse, 64), + new ControlChangeEvent(0, 0, info.secondaryChannel, ControllerType.ExpressionControllerCoarse, 127), + new ControlChangeEvent(0, 0, info.secondaryChannel, ControllerType.RegisteredParameterFine, 0), + new ControlChangeEvent(0, 0, info.secondaryChannel, ControllerType.RegisteredParameterCourse, 0), + new ControlChangeEvent(0, 0, info.secondaryChannel, ControllerType.DataEntryFine, 0), + new ControlChangeEvent(0, 0, info.secondaryChannel, ControllerType.DataEntryCoarse, 16), + new ProgramChangeEvent(0, 0, info.secondaryChannel, info.program), + + new TimeSignatureEvent(0, 4, 4), + new TempoEvent(0, 120), + + // bend effect + new BendEvent(0, 0, info.secondaryChannel, 8192), // no bend + new BendEvent(0, 0, info.secondaryChannel, 8192), + new BendEvent(1 * 80, 0, info.secondaryChannel, 8277), + new BendEvent(2 * 80, 0, info.secondaryChannel, 8363), + new BendEvent(3 * 80, 0, info.secondaryChannel, 8448), + new BendEvent(4 * 80, 0, info.secondaryChannel, 8533), + new BendEvent(5 * 80, 0, info.secondaryChannel, 8619), + new BendEvent(6 * 80, 0, info.secondaryChannel, 8704), + new BendEvent(7 * 80, 0, info.secondaryChannel, 8789), + new BendEvent(8 * 80, 0, info.secondaryChannel, 8875), + new BendEvent(9 * 80, 0, info.secondaryChannel, 8960), + new BendEvent(10 * 80, 0, info.secondaryChannel, 9045), + new BendEvent(11 * 80, 0, info.secondaryChannel, 9131), + + // note itself + new NoteEvent( + 0, + 0, + info.secondaryChannel, + MidiUtils.toTicks(note.beat.duration), + note.realValue, + note.dynamics + ), + + // reset bend + new BendEvent(960, 0, info.primaryChannel, 8192), + new NoteEvent( + 960, + 0, + info.primaryChannel, + MidiUtils.toTicks(note.beat.duration), + note.realValue, + note.dynamics + ), + + // end of track + new TrackEndEvent(3840, 0) // 3840 = end of bar + ]; + for (let i: number = 0; i < handler.midiEvents.length; i++) { + Logger.info('Test', `i[${i}] ${handler.midiEvents[i]}`); + if (i < expectedEvents.length) { + expect(expectedEvents[i].equals(handler.midiEvents[i])) + .withContext(`i[${i}] expected[${expectedEvents[i]}] !== actual[${handler.midiEvents[i]}]`) + .toEqual( + true, + ); + } + } + expect(handler.midiEvents.length).toEqual(expectedEvents.length); + }); + + it('grace-beats', async () => { + let reader: Gp7Importer = new Gp7Importer(); + const buffer = await TestPlatform.loadFile('test-data/audio/grace-beats.gp'); + let settings: Settings = Settings.songBook; + reader.init(ByteBuffer.fromBuffer(buffer), settings); + let score: Score = reader.readScore(); + let handler: FlatMidiEventGenerator = new FlatMidiEventGenerator(); + let generator: MidiFileGenerator = new MidiFileGenerator(score, settings, handler); + generator.generate(); + // on beat + let tick: number = 0; + let ticks: number[] = []; + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].absolutePlaybackStart).toEqual(tick); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].playbackDuration).toEqual(3840); + ticks.push(tick); + tick += score.tracks[0].staves[0].bars[0].voices[0].beats[0].playbackDuration; + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].absolutePlaybackStart).toEqual(tick); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].playbackDuration).toEqual(120); + ticks.push(tick); + tick += score.tracks[0].staves[0].bars[1].voices[0].beats[0].playbackDuration; + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].absolutePlaybackStart).toEqual(tick); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].playbackDuration).toEqual(3720); + ticks.push(tick); + tick += score.tracks[0].staves[0].bars[1].voices[0].beats[1].playbackDuration; + // before beat + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].absolutePlaybackStart).toEqual(tick); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].playbackDuration).toEqual(3720); + ticks.push(tick); + tick += score.tracks[0].staves[0].bars[2].voices[0].beats[0].playbackDuration; + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].absolutePlaybackStart).toEqual(tick); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].playbackDuration).toEqual(120); + ticks.push(tick); + tick += score.tracks[0].staves[0].bars[3].voices[0].beats[0].playbackDuration; + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[1].absolutePlaybackStart).toEqual(tick); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[1].playbackDuration).toEqual(3840); + ticks.push(tick); + tick += score.tracks[0].staves[0].bars[3].voices[0].beats[1].playbackDuration; + // bend + expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].graceType).toEqual(GraceType.BendGrace); + expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].absolutePlaybackStart).toEqual(tick); + expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].playbackDuration).toEqual(1920); + ticks.push(tick); + tick += score.tracks[0].staves[0].bars[4].voices[0].beats[0].playbackDuration; + expect(score.tracks[0].staves[0].bars[4].voices[0].beats[1].absolutePlaybackStart).toEqual(tick); + expect(score.tracks[0].staves[0].bars[4].voices[0].beats[1].playbackDuration).toEqual(1920); + ticks.push(tick); + tick += score.tracks[0].staves[0].bars[4].voices[0].beats[1].playbackDuration; + let info: PlaybackInformation = score.tracks[0].playbackInfo; + let expectedEvents: FlatMidiEvent[] = [ + // channel init + new ControlChangeEvent(0, 0, info.primaryChannel, ControllerType.VolumeCoarse, 120), + new ControlChangeEvent(0, 0, info.primaryChannel, ControllerType.PanCoarse, 64), + new ControlChangeEvent(0, 0, info.primaryChannel, ControllerType.ExpressionControllerCoarse, 127), + new ControlChangeEvent(0, 0, info.primaryChannel, ControllerType.RegisteredParameterFine, 0), + new ControlChangeEvent(0, 0, info.primaryChannel, ControllerType.RegisteredParameterCourse, 0), + new ControlChangeEvent(0, 0, info.primaryChannel, ControllerType.DataEntryFine, 0), + new ControlChangeEvent(0, 0, info.primaryChannel, ControllerType.DataEntryCoarse, 16), + new ProgramChangeEvent(0, 0, info.primaryChannel, info.program), + + new ControlChangeEvent(0, 0, info.secondaryChannel, ControllerType.VolumeCoarse, 120), + new ControlChangeEvent(0, 0, info.secondaryChannel, ControllerType.PanCoarse, 64), + new ControlChangeEvent(0, 0, info.secondaryChannel, ControllerType.ExpressionControllerCoarse, 127), + new ControlChangeEvent(0, 0, info.secondaryChannel, ControllerType.RegisteredParameterFine, 0), + new ControlChangeEvent(0, 0, info.secondaryChannel, ControllerType.RegisteredParameterCourse, 0), + new ControlChangeEvent(0, 0, info.secondaryChannel, ControllerType.DataEntryFine, 0), + new ControlChangeEvent(0, 0, info.secondaryChannel, ControllerType.DataEntryCoarse, 16), + new ProgramChangeEvent(0, 0, info.secondaryChannel, info.program), + + new TimeSignatureEvent(0, 4, 4), + new TempoEvent(0, 120), + + // on beat + new BendEvent(ticks[0], 0, info.primaryChannel, 8192), + new NoteEvent(ticks[0], 0, info.primaryChannel, 3840, 67, DynamicValue.F), + + new BendEvent(ticks[1], 0, info.primaryChannel, 8192), + new NoteEvent(ticks[1], 0, info.primaryChannel, 120, 67, DynamicValue.F), + + new BendEvent(ticks[2], 0, info.primaryChannel, 8192), + new NoteEvent(ticks[2], 0, info.primaryChannel, 3720, 67, DynamicValue.F), + + // before beat + new BendEvent(ticks[3], 0, info.primaryChannel, 8192), + new NoteEvent(ticks[3], 0, info.primaryChannel, 3720, 67, DynamicValue.F), + + new BendEvent(ticks[4], 0, info.primaryChannel, 8192), + new NoteEvent(ticks[4], 0, info.primaryChannel, 120, 67, DynamicValue.F), + + new BendEvent(ticks[5], 0, info.primaryChannel, 8192), + new NoteEvent(ticks[5], 0, info.primaryChannel, 3840, 67, DynamicValue.F), + + // bend beat + new BendEvent(ticks[6], 0, info.secondaryChannel, 8192), + new BendEvent(ticks[6] + 12 * 0, 0, info.secondaryChannel, 8192), + new BendEvent(ticks[6] + 12 * 1, 0, info.secondaryChannel, 8277), + new BendEvent(ticks[6] + 12 * 2, 0, info.secondaryChannel, 8363), + new BendEvent(ticks[6] + 12 * 3, 0, info.secondaryChannel, 8448), + new BendEvent(ticks[6] + 12 * 4, 0, info.secondaryChannel, 8533), + new BendEvent(ticks[6] + 12 * 5, 0, info.secondaryChannel, 8619), + new BendEvent(ticks[6] + 12 * 6, 0, info.secondaryChannel, 8704), + new BendEvent(ticks[6] + 12 * 7, 0, info.secondaryChannel, 8789), + new BendEvent(ticks[6] + 12 * 8, 0, info.secondaryChannel, 8875), + new BendEvent(ticks[6] + 12 * 9, 0, info.secondaryChannel, 8960), + new BendEvent(ticks[6] + 12 * 10, 0, info.secondaryChannel, 9045), + new BendEvent(ticks[6] + 12 * 11, 0, info.secondaryChannel, 9131), + new NoteEvent(ticks[6], 0, info.secondaryChannel, 3840, 67, DynamicValue.F), + + // end of track + new TrackEndEvent(19200, 0) // 3840 = end of bar + ]; + + for (let i: number = 0; i < handler.midiEvents.length; i++) { + Logger.info('Test', `i[${i}] ${handler.midiEvents[i]}`); + if (i < expectedEvents.length) { + expect(handler.midiEvents[i].equals(expectedEvents[i])) + .withContext(`i[${i}] expected[${expectedEvents[i]}] !== actual[${handler.midiEvents[i]}]`) + .toEqual(true); + } + } + expect(handler.midiEvents.length).toEqual(expectedEvents.length); + }); + + it('bend-multi-point', () => { + let tex: string = ':4 15.6{b(0 4 0)} 15.6'; + let score: Score = parseTex(tex); + expect(score.tracks.length).toEqual(1); + expect(score.tracks[0].staves[0].bars.length).toEqual(1); + expect(score.tracks[0].staves[0].bars[0].voices.length).toEqual(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).toEqual(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes.length).toEqual(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes.length).toEqual(1); + let handler: FlatMidiEventGenerator = new FlatMidiEventGenerator(); + let generator: MidiFileGenerator = new MidiFileGenerator(score, null, handler); + generator.generate(); + let info: PlaybackInformation = score.tracks[0].playbackInfo; + let note: Note = score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0]; + let expectedEvents: FlatMidiEvent[] = [ + // channel init + new ControlChangeEvent(0, 0, info.primaryChannel, ControllerType.VolumeCoarse, 120), + new ControlChangeEvent(0, 0, info.primaryChannel, ControllerType.PanCoarse, 64), + new ControlChangeEvent(0, 0, info.primaryChannel, ControllerType.ExpressionControllerCoarse, 127), + new ControlChangeEvent(0, 0, info.primaryChannel, ControllerType.RegisteredParameterFine, 0), + new ControlChangeEvent(0, 0, info.primaryChannel, ControllerType.RegisteredParameterCourse, 0), + new ControlChangeEvent(0, 0, info.primaryChannel, ControllerType.DataEntryFine, 0), + new ControlChangeEvent(0, 0, info.primaryChannel, ControllerType.DataEntryCoarse, 16), + new ProgramChangeEvent(0, 0, info.primaryChannel, info.program), + + new ControlChangeEvent(0, 0, info.secondaryChannel, ControllerType.VolumeCoarse, 120), + new ControlChangeEvent(0, 0, info.secondaryChannel, ControllerType.PanCoarse, 64), + new ControlChangeEvent(0, 0, info.secondaryChannel, ControllerType.ExpressionControllerCoarse, 127), + new ControlChangeEvent(0, 0, info.secondaryChannel, ControllerType.RegisteredParameterFine, 0), + new ControlChangeEvent(0, 0, info.secondaryChannel, ControllerType.RegisteredParameterCourse, 0), + new ControlChangeEvent(0, 0, info.secondaryChannel, ControllerType.DataEntryFine, 0), + new ControlChangeEvent(0, 0, info.secondaryChannel, ControllerType.DataEntryCoarse, 16), + new ProgramChangeEvent(0, 0, info.secondaryChannel, info.program), + + new TimeSignatureEvent(0, 4, 4), + new TempoEvent(0, 120), + + // bend effect + new BendEvent(0, 0, info.secondaryChannel, 8192), + new BendEvent(0 * 40, 0, info.secondaryChannel, 8192), // no bend + new BendEvent(1 * 40, 0, info.secondaryChannel, 8277), + new BendEvent(2 * 40, 0, info.secondaryChannel, 8363), + new BendEvent(3 * 40, 0, info.secondaryChannel, 8448), + new BendEvent(4 * 40, 0, info.secondaryChannel, 8533), + new BendEvent(5 * 40, 0, info.secondaryChannel, 8619), + new BendEvent(6 * 40, 0, info.secondaryChannel, 8704), + new BendEvent(7 * 40, 0, info.secondaryChannel, 8789), + new BendEvent(8 * 40, 0, info.secondaryChannel, 8875), + new BendEvent(9 * 40, 0, info.secondaryChannel, 8960), + new BendEvent(10 * 40, 0, info.secondaryChannel, 9045), + new BendEvent(11 * 40, 0, info.secondaryChannel, 9131), + new BendEvent(12 * 40, 0, info.secondaryChannel, 9216), // full bend + new BendEvent(13 * 40, 0, info.secondaryChannel, 9131), + new BendEvent(14 * 40, 0, info.secondaryChannel, 9045), + new BendEvent(15 * 40, 0, info.secondaryChannel, 8960), + new BendEvent(16 * 40, 0, info.secondaryChannel, 8875), + new BendEvent(17 * 40, 0, info.secondaryChannel, 8789), + new BendEvent(18 * 40, 0, info.secondaryChannel, 8704), + new BendEvent(19 * 40, 0, info.secondaryChannel, 8619), + new BendEvent(20 * 40, 0, info.secondaryChannel, 8533), + new BendEvent(21 * 40, 0, info.secondaryChannel, 8448), + new BendEvent(22 * 40, 0, info.secondaryChannel, 8363), + new BendEvent(23 * 40, 0, info.secondaryChannel, 8277), + new BendEvent(24 * 40, 0, info.secondaryChannel, 8192), // no bend + + // note itself + new NoteEvent( + 0, + 0, + info.secondaryChannel, + MidiUtils.toTicks(note.beat.duration), + note.realValue, + note.dynamics + ), + + // reset bend + new BendEvent(960, 0, info.primaryChannel, 8192), // finish + new NoteEvent( + 960, + 0, + info.primaryChannel, + MidiUtils.toTicks(note.beat.duration), + note.realValue, + note.dynamics + ), // end of track + new TrackEndEvent(3840, 0) // 3840 = end of bar + ]; + for (let i: number = 0; i < handler.midiEvents.length; i++) { + Logger.info('Test', `i[${i}] ${handler.midiEvents[i]}`); + if (i < expectedEvents.length) { + expect(expectedEvents[i].equals(handler.midiEvents[i])) + .withContext(`i[${i}] expected[${expectedEvents[i]}] !== actual[${handler.midiEvents[i]}]`) + .toEqual( + true + ); + } + } + expect(handler.midiEvents.length).toEqual(expectedEvents.length); + }); + + it('triplet-feel', () => { + let tex: string = + '\\ts 2 4 \\tf t8 3.2.8*4 | \\tf t16 3.2.16*8 | \\tf d8 3.2.8*4 | \\tf d16 3.2.16*8 | \\tf s8 3.2.8*4 | \\tf s16 3.2.16*8'; + let score: Score = parseTex(tex); + // prettier-ignore + let expectedPlaybackStartTimes: number[] = [ + 0, 480, 960, 1440, + 0, 240, 480, 720, 960, 1200, 1440, 1680, + 0, 480, 960, 1440, + 0, 240, 480, 720, 960, 1200, 1440, 1680, + 0, 480, 960, 1440, + 0, 240, 480, 720, 960, 1200, 1440, 1680 + ]; + // prettier-ignore + let expectedPlaybackDurations: number[] = [ + 480, 480, 480, 480, + 240, 240, 240, 240, 240, 240, 240, 240, + 480, 480, 480, 480, + 240, 240, 240, 240, 240, 240, 240, 240, + 480, 480, 480, 480, + 240, 240, 240, 240, 240, 240, 240, 240 + ]; + let actualPlaybackStartTimes: number[] = []; + let actualPlaybackDurations: number[] = []; + let beat: Beat | null = score.tracks[0].staves[0].bars[0].voices[0].beats[0]; + while (beat) { + actualPlaybackStartTimes.push(beat.playbackStart); + actualPlaybackDurations.push(beat.playbackDuration); + beat = beat.nextBeat; + } + expect(actualPlaybackStartTimes.join(',')).toEqual(expectedPlaybackStartTimes.join(',')); + expect(actualPlaybackDurations.join(',')).toEqual(expectedPlaybackDurations.join(',')); + // prettier-ignore + let expectedMidiStartTimes: number[] = [ + 0, 640, 960, 1600, + 1920, 2240, 2400, 2720, 2880, 3200, 3360, 3680, + 3840, 4560, 4800, 5520, + 5760, 6120, 6240, 6600, 6720, 7080, 7200, 7560, + 7680, 7920, 8640, 8880, + 9600, 9720, 10080, 10200, 10560, 10680, 11040, 11160 + ]; + // prettier-ignore + let expectedMidiDurations: number[] = [ + 640, 320, 640, 320, + 320, 160, 320, 160, 320, 160, 320 ,160, + 720, 240, 720, 240, + 360, 120, 360, 120, 360, 120, 360, 120, + 240, 720, 240, 720, + 120, 360, 120, 360, 120, 360, 120, 360 + ]; + + let actualMidiStartTimes: number[] = []; + let actualMidiDurations: number[] = []; + let handler: FlatMidiEventGenerator = new FlatMidiEventGenerator(); + let generator: MidiFileGenerator = new MidiFileGenerator(score, null, handler); + generator.generate(); + for (let midiEvent of handler.midiEvents) { + if (midiEvent instanceof NoteEvent) { + actualMidiStartTimes.push(midiEvent.tick); + actualMidiDurations.push(midiEvent.length); + } + } + expect(actualMidiStartTimes.join(',')).toEqual(expectedMidiStartTimes.join(',')); + expect(actualMidiDurations.join(',')).toEqual(expectedMidiDurations.join(',')); + }); +}); diff --git a/test/audio/MidiPlaybackController.test.ts b/test/audio/MidiPlaybackController.test.ts new file mode 100644 index 000000000..def70246a --- /dev/null +++ b/test/audio/MidiPlaybackController.test.ts @@ -0,0 +1,80 @@ +import { MidiPlaybackController } from '@src/midi/MidiPlaybackController'; +import { AlphaTexImporter } from '@src/importer/AlphaTexImporter'; +import { Score } from '@src/model/Score'; +import { Settings } from '@src/Settings'; +import { Logger } from '@src/Logger'; +import { GpImporterTestHelper } from '@test/importer/GpImporterTestHelper'; +import { TestPlatform } from '@test/TestPlatform'; + +describe('MidiPlaybackControllerTest', () => { + const testRepeat: ((score: Score, expectedIndexes: number[]) => void) = (score: Score, expectedIndexes: number[]): void => { + let controller: MidiPlaybackController = new MidiPlaybackController(score); + let i: number = 0; + while (!controller.finished) { + let index: number = controller.index; + controller.processCurrent(); + if (controller.shouldPlay) { + Logger.debug('Test', `Checking index ${i}, expected[${expectedIndexes[i]}]`, i, expectedIndexes[i]); + expect(index).toEqual(expectedIndexes[i]); + i++; + } + controller.moveNext(); + } + expect(i).toEqual(expectedIndexes.length); + expect(controller.finished).toBe(true); + }; + + it('repeat-close', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('audio/repeat-close.gp5'); + let score: Score = reader.readScore(); + let expectedIndexes = [0, 1, 0, 1, 2]; + testRepeat(score, expectedIndexes); + }); + + it('repeat-close-multi', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('audio/repeat-close-multi.gp5'); + let score: Score = reader.readScore(); + let expectedIndexes = [0, 1, 0, 1, 0, 1, 0, 1, 2]; + testRepeat(score, expectedIndexes); + }); + + it('repeat-close-without-start-at-beginning', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile( + 'audio/repeat-close-without-start-at-beginning.gp5' + ); + let score: Score = reader.readScore(); + let expectedIndexes = [0, 1, 0, 1]; + testRepeat(score, expectedIndexes); + }); + + it('repeat-close-alternate-endings', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile( + 'audio/repeat-close-alternate-endings.gp5' + ); + let score: Score = reader.readScore(); + let expectedIndexes = [0, 1, 0, 2, 3, 0, 1, 0, 4]; + testRepeat(score, expectedIndexes); + }); + + it('repeat-with-alphaTex', () => { + let tex: string = + '\\ro 1.3 2.3 3.3 4.3 | 5.3 6.3 7.3 8.3 | \\rc 2 1.3 2.3 3.3 4.3 | \\ro \\rc 3 1.3 2.3 3.3 4.3'; + let importer: AlphaTexImporter = new AlphaTexImporter(); + importer.init(TestPlatform.createStringReader(tex), new Settings()); + let score: Score = importer.readScore(); + let playedBars: number[] = []; + let controller: MidiPlaybackController = new MidiPlaybackController(score); + while (!controller.finished) { + let index: number = controller.index; + playedBars.push(index); + controller.processCurrent(); + controller.moveNext(); + if (playedBars.length > 50) { + fail('Too many bars generated'); + } + } + let expectedBars: number[] = [0, 1, 2, 0, 1, 2, 3, 3, 3]; + + expect(playedBars.join(',')).toEqual(expectedBars.join(',')); + }); +}); diff --git a/test/audio/TestOutput.ts b/test/audio/TestOutput.ts new file mode 100644 index 000000000..ae95311d5 --- /dev/null +++ b/test/audio/TestOutput.ts @@ -0,0 +1,71 @@ +import { ISynthOutput } from '@src/synth/ISynthOutput'; +import { EventEmitter, IEventEmitter, IEventEmitterOfT, EventEmitterOfT } from '@src/EventEmitter'; + +export class TestOutput implements ISynthOutput { + private _finished: boolean = false; + public samples: number[] = []; + + public get sampleRate(): number { + return 44100; + } + + public open(): void { + this.samples = []; + (this.ready as EventEmitter).trigger(); + } + + public sequencerFinished(): void { + this._finished = true; + } + + public play(): void { + // nothing to do + } + + public next(): void { + if (this._finished) { + (this.finished as EventEmitter).trigger(); + } else { + (this.sampleRequest as EventEmitter).trigger(); + } + } + + public pause(): void { + // nothing to do + } + + public addSamples(f: Float32Array): void { + for (let i: number = 0; i < f.length; i++) { + this.samples.push(f[i]); + } + (this.samplesPlayed as EventEmitterOfT).trigger(f.length); + } + + public resetSamples(): void { + // nothing to do + } + + public activate(): void { + // nothing to do + } + + /** + * Fired when the output has been successfully opened and is ready to play samples. + */ + readonly ready: IEventEmitter = new EventEmitter(); + + /** + * Fired when a certain number of samples have been played. + */ + readonly samplesPlayed: IEventEmitterOfT = new EventEmitterOfT(); + + /** + * Fired when the output needs more samples to be played. + */ + readonly sampleRequest: IEventEmitter = new EventEmitter(); + + /** + * Fired when the last samples after calling SequencerFinished have been played. + */ + readonly finished: IEventEmitter = new EventEmitter(); +} diff --git a/test/importer/AlphaTexImporter.test.ts b/test/importer/AlphaTexImporter.test.ts new file mode 100644 index 000000000..cc8a795c0 --- /dev/null +++ b/test/importer/AlphaTexImporter.test.ts @@ -0,0 +1,853 @@ +import { StaveProfile } from '@src/DisplaySettings'; +import { AlphaTexImporter } from '@src/importer/AlphaTexImporter'; +import { Beat } from '@src/model/Beat'; +import { Clef } from '@src/model/Clef'; +import { CrescendoType } from '@src/model/CrescendoType'; +import { Duration } from '@src/model/Duration'; +import { DynamicValue } from '@src/model/DynamicValue'; +import { Fingers } from '@src/model/Fingers'; +import { GraceType } from '@src/model/GraceType'; +import { HarmonicType } from '@src/model/HarmonicType'; +import { Score } from '@src/model/Score'; +import { SlideInType } from '@src/model/SlideInType'; +import { SlideOutType } from '@src/model/SlideOutType'; +import { Staff } from '@src/model/Staff'; +import { Track } from '@src/model/Track'; +import { TripletFeel } from '@src/model/TripletFeel'; +import { Tuning } from '@src/model/Tuning'; +import { HarmonicsEffectInfo } from '@src/rendering/effects/HarmonicsEffectInfo'; +import { ScoreRenderer } from '@src/rendering/ScoreRenderer'; +import { Settings } from '@src/Settings'; +import { TestPlatform } from '@test/TestPlatform'; + +describe('AlphaTexImporterTest', () => { + const parseTex: (tex:string) => Score = (tex: string): Score => { + let importer: AlphaTexImporter = new AlphaTexImporter(); + importer.init(TestPlatform.createStringReader(tex), new Settings()); + return importer.readScore(); + }; + + it('ensure-metadata-parsing-issue73', () => { + const tex = `\\title Test + \\words test + \\music alphaTab + \\copyright test + \\tempo 200 + \\instrument 30 + \\capo 2 + \\tuning G3 D2 G2 B2 D3 A4 + . + 0.5.2 1.5.4 3.4.4 | 5.3.8 5.3.8 5.3.8 5.3.8 r.2`; + + let score: Score = parseTex(tex); + expect(score.title).toEqual('Test'); + expect(score.words).toEqual('test'); + expect(score.music).toEqual('alphaTab'); + expect(score.copyright).toEqual('test'); + expect(score.tempo).toEqual(200); + expect(score.tracks.length).toEqual(1); + expect(score.tracks[0].playbackInfo.program).toEqual(30); + expect(score.tracks[0].staves[0].capo).toEqual(2); + expect(score.tracks[0].staves[0].tuning.join(',')).toEqual('55,38,43,47,50,69'); + expect(score.masterBars.length).toEqual(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).toEqual(3); + { + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes.length).toEqual(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].duration).toEqual(Duration.Half); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].fret).toEqual(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].string).toEqual(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes.length).toEqual(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].duration).toEqual(Duration.Quarter); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].fret).toEqual(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].string).toEqual(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes.length).toEqual(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].duration).toEqual(Duration.Quarter); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].fret).toEqual(3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].string).toEqual(3); + } + expect(score.tracks[0].staves[0].bars[1].voices[0].beats.length).toEqual(5); + { + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes.length).toEqual(1); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].duration).toEqual(Duration.Eighth); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].fret).toEqual(5); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].string).toEqual(4); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].notes.length).toEqual(1); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].duration).toEqual(Duration.Eighth); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].notes[0].fret).toEqual(5); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].notes[0].string).toEqual(4); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].notes.length).toEqual(1); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].duration).toEqual(Duration.Eighth); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].notes[0].fret).toEqual(5); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].notes[0].string).toEqual(4); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].notes.length).toEqual(1); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].duration).toEqual(Duration.Eighth); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].notes[0].fret).toEqual(5); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].notes[0].string).toEqual(4); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[4].notes.length).toEqual(0); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[4].duration).toEqual(Duration.Half); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[4].isRest).toEqual(true); + } + }); + + it('tuning', () => { + const tex = `\\tuning E4 B3 G3 D3 A2 E2 + . + 0.5.1`; + + let score: Score = parseTex(tex); + expect(score.tracks[0].staves[0].tuning.join(',')).toEqual(Tuning.getDefaultTuningFor(6)!.tunings.join(',')); + }); + + it('dead-notes1-issue79', () => { + let tex: string = ':4 x.3'; + let score: Score = parseTex(tex); + expect(score.tracks.length).toEqual(1); + expect(score.masterBars.length).toEqual(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).toEqual(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].fret).toEqual(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isDead).toEqual(true); + }); + + it('dead-notes2-issue79', () => { + let tex: string = ':4 3.3{x}'; + let score: Score = parseTex(tex); + expect(score.tracks.length).toEqual(1); + expect(score.masterBars.length).toEqual(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).toEqual(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].fret).toEqual(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isDead).toEqual(true); + }); + + it('trill-issue79', () => { + let tex: string = ':4 3.3{tr 5 16}'; + let score: Score = parseTex(tex); + expect(score.tracks.length).toEqual(1); + expect(score.masterBars.length).toEqual(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).toEqual(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].fret).toEqual(3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isTrill).toEqual(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].trillSpeed).toEqual(Duration.Sixteenth); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].trillFret).toEqual(5); + }); + + it('tremolo-issue79', () => { + let tex: string = ':4 3.3{tr 5 16}'; + let score: Score = parseTex(tex); + expect(score.tracks.length).toEqual(1); + expect(score.masterBars.length).toEqual(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).toEqual(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].fret).toEqual(3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isTrill).toEqual(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].trillSpeed).toEqual(Duration.Sixteenth); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].trillFret).toEqual(5); + }); + + it('tremolo-picking-issue79', () => { + let tex: string = ':4 3.3{tp 16}'; + let score: Score = parseTex(tex); + expect(score.tracks.length).toEqual(1); + expect(score.masterBars.length).toEqual(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).toEqual(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].fret).toEqual(3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].isTremolo).toEqual(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].tremoloSpeed).toEqual(Duration.Sixteenth); + }); + + it('hamonics-issue79', () => { + let tex: string = ':8 3.3{nh} 3.3{ah} 3.3{th} 3.3{ph} 3.3{sh}'; + let score: Score = parseTex(tex); + expect(score.tracks.length).toEqual(1); + expect(score.masterBars.length).toEqual(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).toEqual(5); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].harmonicType).toEqual( + HarmonicType.Natural + ); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].harmonicType).toEqual( + HarmonicType.Artificial + ); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].harmonicType).toEqual(HarmonicType.Tap); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].harmonicType).toEqual(HarmonicType.Pinch); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[4].notes[0].harmonicType).toEqual(HarmonicType.Semi); + }); + + it('hamonics-rendering-text-issue79', () => { + let tex: string = ':8 3.3{nh} 3.3{ah} 3.3{th} 3.3{ph} 3.3{sh}'; + let score: Score = parseTex(tex); + let settings: Settings = new Settings(); + settings.core.engine = 'svg'; + settings.display.staveProfile = StaveProfile.ScoreTab; + let renderer: ScoreRenderer = new ScoreRenderer(settings); + renderer.width = 970; + let svg: string = ''; + renderer.partialRenderFinished.on(r => { + svg += r.renderResult; + }); + renderer.renderScore(score, [0]); + let regexTemplate: string = ']+>\\s*{0}\\s*'; + expect( + new RegExp(regexTemplate.replace('{0}', HarmonicsEffectInfo.harmonicToString(HarmonicType.Natural))).exec( + svg + ) + ).toBeTruthy(); + expect( + new RegExp( + regexTemplate.replace('{0}', HarmonicsEffectInfo.harmonicToString(HarmonicType.Artificial)) + ).exec(svg) + ).toBeTruthy(); + expect( + new RegExp(regexTemplate.replace('{0}', HarmonicsEffectInfo.harmonicToString(HarmonicType.Tap))).exec(svg) + ).toBeTruthy(); + expect( + new RegExp(regexTemplate.replace('{0}', HarmonicsEffectInfo.harmonicToString(HarmonicType.Pinch))).exec(svg) + ).toBeTruthy(); + expect( + new RegExp(regexTemplate.replace('{0}', HarmonicsEffectInfo.harmonicToString(HarmonicType.Semi))).exec(svg) + ).toBeTruthy(); + }); + + it('grace-issue79', () => { + let tex: string = ':8 3.3{gr} 3.3{gr ob}'; + let score: Score = parseTex(tex); + expect(score.tracks.length).toEqual(1); + expect(score.masterBars.length).toEqual(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).toEqual(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].graceType).toEqual(GraceType.BeforeBeat); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].graceType).toEqual(GraceType.OnBeat); + }); + + it('left-hand-finger-single-note', () => { + let tex: string = ':8 3.3{lf 1} 3.3{lf 2} 3.3{lf 3} 3.3{lf 4} 3.3{lf 5}'; + let score: Score = parseTex(tex); + expect(score.tracks.length).toEqual(1); + expect(score.masterBars.length).toEqual(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).toEqual(5); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].leftHandFinger).toEqual(Fingers.Thumb); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].leftHandFinger).toEqual( + Fingers.IndexFinger + ); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].leftHandFinger).toEqual( + Fingers.MiddleFinger + ); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].leftHandFinger).toEqual( + Fingers.AnnularFinger + ); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[4].notes[0].leftHandFinger).toEqual( + Fingers.LittleFinger + ); + }); + + it('right-hand-finger-single-note', () => { + let tex: string = ':8 3.3{rf 1} 3.3{rf 2} 3.3{rf 3} 3.3{rf 4} 3.3{rf 5}'; + let score: Score = parseTex(tex); + expect(score.tracks.length).toEqual(1); + expect(score.masterBars.length).toEqual(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).toEqual(5); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].rightHandFinger).toEqual(Fingers.Thumb); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].rightHandFinger).toEqual( + Fingers.IndexFinger + ); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].rightHandFinger).toEqual( + Fingers.MiddleFinger + ); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].rightHandFinger).toEqual( + Fingers.AnnularFinger + ); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[4].notes[0].rightHandFinger).toEqual( + Fingers.LittleFinger + ); + }); + + it('left-hand-finger-chord', () => { + let tex: string = ':8 (3.1{lf 1} 3.2{lf 2} 3.3{lf 3} 3.4{lf 4} 3.5{lf 5})'; + let score: Score = parseTex(tex); + expect(score.tracks.length).toEqual(1); + expect(score.masterBars.length).toEqual(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).toEqual(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes.length).toEqual(5); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].leftHandFinger).toEqual(Fingers.Thumb); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[1].leftHandFinger).toEqual( + Fingers.IndexFinger + ); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[2].leftHandFinger).toEqual( + Fingers.MiddleFinger + ); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[3].leftHandFinger).toEqual( + Fingers.AnnularFinger + ); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[4].leftHandFinger).toEqual( + Fingers.LittleFinger + ); + }); + + it('right-hand-finger-chord', () => { + let tex: string = ':8 (3.1{rf 1} 3.2{rf 2} 3.3{rf 3} 3.4{rf 4} 3.5{rf 5})'; + let score: Score = parseTex(tex); + expect(score.tracks.length).toEqual(1); + expect(score.masterBars.length).toEqual(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).toEqual(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes.length).toEqual(5); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].rightHandFinger).toEqual(Fingers.Thumb); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[1].rightHandFinger).toEqual( + Fingers.IndexFinger + ); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[2].rightHandFinger).toEqual( + Fingers.MiddleFinger + ); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[3].rightHandFinger).toEqual( + Fingers.AnnularFinger + ); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[4].rightHandFinger).toEqual( + Fingers.LittleFinger + ); + }); + + it('unstringed', () => { + let tex: string = '\\tuning piano . c4 c#4 d4 d#4 | c4 db4 d4 eb4'; + let score: Score = parseTex(tex); + expect(score.tracks.length).toEqual(1); + expect(score.masterBars.length).toEqual(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats.length).toEqual(4); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isPiano).toEqual(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].realValue).toEqual(60); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isPiano).toEqual(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].realValue).toEqual(61); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isPiano).toEqual(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].realValue).toEqual(62); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isPiano).toEqual(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].realValue).toEqual(63); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats.length).toEqual(4); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].isPiano).toEqual(true); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].realValue).toEqual(60); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].isPiano).toEqual(true); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].notes[0].realValue).toEqual(61); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].isPiano).toEqual(true); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].notes[0].realValue).toEqual(62); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].isPiano).toEqual(true); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].notes[0].realValue).toEqual(63); + }); + + it('multi-staff-default-settings', () => { + let tex: string = '1.1 | 1.1 | \\staff 2.1 | 2.1'; + let score: Score = parseTex(tex); + expect(score.tracks.length).toEqual(1); + expect(score.masterBars.length).toEqual(2); + expect(score.tracks[0].staves.length).toEqual(2); + expect(score.tracks[0].staves[0].showTablature).toBe(true); + expect(score.tracks[0].staves[0].showStandardNotation).toBe(true); + expect(score.tracks[0].staves[0].bars.length).toEqual(2); + expect(score.tracks[0].staves[1].showTablature).toBe(true); // default settings used + + expect(score.tracks[0].staves[1].showStandardNotation).toBe(true); + expect(score.tracks[0].staves[1].bars.length).toEqual(2); + }); + + it('multi-staff-default-settings-braces', () => { + let tex: string = '1.1 | 1.1 | \\staff{} 2.1 | 2.1'; + let score: Score = parseTex(tex); + expect(score.tracks.length).toEqual(1); + expect(score.masterBars.length).toEqual(2); + expect(score.tracks[0].staves.length).toEqual(2); + expect(score.tracks[0].staves[0].showTablature).toBe(true); + expect(score.tracks[0].staves[0].showStandardNotation).toBe(true); + expect(score.tracks[0].staves[0].bars.length).toEqual(2); + expect(score.tracks[0].staves[1].showTablature).toBe(true); // default settings used + + expect(score.tracks[0].staves[1].showStandardNotation).toBe(true); + expect(score.tracks[0].staves[1].bars.length).toEqual(2); + }); + + it('single-staff-with-setting', () => { + let tex: string = '\\staff{score} 1.1 | 1.1'; + let score: Score = parseTex(tex); + expect(score.tracks.length).toEqual(1); + expect(score.masterBars.length).toEqual(2); + expect(score.tracks[0].staves.length).toEqual(1); + expect(score.tracks[0].staves[0].showTablature).toBe(false); + expect(score.tracks[0].staves[0].showStandardNotation).toBe(true); + expect(score.tracks[0].staves[0].bars.length).toEqual(2); + }); + + it('multi-staff-with-settings', () => { + const tex = `\\staff{score} 1.1 | 1.1 | + \\staff{tabs} \\capo 2 2.1 | 2.1 | + \\staff{score tabs} \\tuning A1 D2 A2 D3 G3 B3 E4 3.1 | 3.1`; + let score: Score = parseTex(tex); + expect(score.tracks.length).toEqual(1); + expect(score.masterBars.length).toEqual(2); + expect(score.tracks[0].staves.length).toEqual(3); + expect(score.tracks[0].staves[0].showTablature).toBe(false); + expect(score.tracks[0].staves[0].showStandardNotation).toBe(true); + expect(score.tracks[0].staves[0].bars.length).toEqual(2); + expect(score.tracks[0].staves[1].showTablature).toBe(true); + expect(score.tracks[0].staves[1].showStandardNotation).toBe(false); + expect(score.tracks[0].staves[1].bars.length).toEqual(2); + expect(score.tracks[0].staves[1].capo).toEqual(2); + expect(score.tracks[0].staves[2].showTablature).toBe(true); + expect(score.tracks[0].staves[2].showStandardNotation).toBe(true); + expect(score.tracks[0].staves[2].bars.length).toEqual(2); + expect(score.tracks[0].staves[2].tuning.length).toEqual(7); + }); + + it('multi-track', () => { + let tex: string = '\\track "First" 1.1 | 1.1 | \\track "Second" 2.2 | 2.2'; + let score: Score = parseTex(tex); + expect(score.tracks.length).toEqual(2); + expect(score.masterBars.length).toEqual(2); + expect(score.tracks[0].staves.length).toEqual(1); + expect(score.tracks[0].name).toEqual('First'); + expect(score.tracks[0].playbackInfo.primaryChannel).toEqual(0); + expect(score.tracks[0].playbackInfo.secondaryChannel).toEqual(1); + expect(score.tracks[0].staves[0].showTablature).toBe(true); + expect(score.tracks[0].staves[0].showStandardNotation).toBe(true); + expect(score.tracks[0].staves[0].bars.length).toEqual(2); + expect(score.tracks[1].staves.length).toEqual(1); + expect(score.tracks[1].name).toEqual('Second'); + expect(score.tracks[1].playbackInfo.primaryChannel).toEqual(2); + expect(score.tracks[1].playbackInfo.secondaryChannel).toEqual(3); + expect(score.tracks[1].staves[0].showTablature).toBe(true); + expect(score.tracks[1].staves[0].showStandardNotation).toBe(true); + expect(score.tracks[1].staves[0].bars.length).toEqual(2); + }); + + it('multi-track-names', () => { + let tex: string = + '\\track 1.1 | 1.1 | \\track "Only Long Name" 2.2 | 2.2 | \\track "Very Long Name" "shrt" 3.3 | 3.3 '; + let score: Score = parseTex(tex); + expect(score.tracks.length).toEqual(3); + expect(score.masterBars.length).toEqual(2); + expect(score.tracks[0].staves.length).toEqual(1); + expect(score.tracks[0].name).toEqual(''); + expect(score.tracks[0].shortName).toEqual(''); + expect(score.tracks[0].playbackInfo.primaryChannel).toEqual(0); + expect(score.tracks[0].playbackInfo.secondaryChannel).toEqual(1); + expect(score.tracks[0].staves[0].showTablature).toBe(true); + expect(score.tracks[0].staves[0].showStandardNotation).toBe(true); + expect(score.tracks[0].staves[0].bars.length).toEqual(2); + expect(score.tracks[1].staves.length).toEqual(1); + expect(score.tracks[1].name).toEqual('Only Long Name'); + expect(score.tracks[1].shortName).toEqual('Only Long '); + expect(score.tracks[1].playbackInfo.primaryChannel).toEqual(2); + expect(score.tracks[1].playbackInfo.secondaryChannel).toEqual(3); + expect(score.tracks[1].staves[0].showTablature).toBe(true); + expect(score.tracks[1].staves[0].showStandardNotation).toBe(true); + expect(score.tracks[1].staves[0].bars.length).toEqual(2); + expect(score.tracks[2].staves.length).toEqual(1); + expect(score.tracks[2].name).toEqual('Very Long Name'); + expect(score.tracks[2].shortName).toEqual('shrt'); + expect(score.tracks[2].playbackInfo.primaryChannel).toEqual(4); + expect(score.tracks[2].playbackInfo.secondaryChannel).toEqual(5); + expect(score.tracks[2].staves[0].showTablature).toBe(true); + expect(score.tracks[2].staves[0].showStandardNotation).toBe(true); + expect(score.tracks[2].staves[0].bars.length).toEqual(2); + }); + + it('multi-track-multi-staff', () => { + const tex = `\\track "Piano" + \\staff{score} \\tuning piano \\instrument acousticgrandpiano + c4 d4 e4 f4 | + + \\staff{score} \\tuning piano \\clef F4 + c2 c2 c2 c2 | + + \\track "Guitar" + \\staff{tabs} + 1.2 3.2 0.1 1.1 | + + \\track "Second Guitar" + 1.2 3.2 0.1 1.1 + `; + let score: Score = parseTex(tex); + expect(score.tracks.length).toEqual(3); + expect(score.masterBars.length).toEqual(1); + { + let track1: Track = score.tracks[0]; + expect(track1.name).toEqual('Piano'); + expect(track1.staves.length).toEqual(2); + expect(track1.playbackInfo.program).toEqual(0); + expect(track1.playbackInfo.primaryChannel).toEqual(0); + expect(track1.playbackInfo.secondaryChannel).toEqual(1); + { + let staff1: Staff = track1.staves[0]; + expect(staff1.showTablature).toBe(false); + expect(staff1.showStandardNotation).toBe(true); + expect(staff1.tuning.length).toEqual(0); + expect(staff1.bars.length).toEqual(1); + expect(staff1.bars[0].clef).toEqual(Clef.G2); + } + { + let staff2: Staff = track1.staves[1]; + expect(staff2.showTablature).toBe(false); + expect(staff2.showStandardNotation).toBe(true); + expect(staff2.tuning.length).toEqual(0); + expect(staff2.bars.length).toEqual(1); + expect(staff2.bars[0].clef).toEqual(Clef.F4); + } + } + { + let track2: Track = score.tracks[1]; + expect(track2.name).toEqual('Guitar'); + expect(track2.staves.length).toEqual(1); + expect(track2.playbackInfo.program).toEqual(25); + expect(track2.playbackInfo.primaryChannel).toEqual(2); + expect(track2.playbackInfo.secondaryChannel).toEqual(3); + { + let staff1: Staff = track2.staves[0]; + expect(staff1.showTablature).toBe(true); + expect(staff1.showStandardNotation).toBe(false); + expect(staff1.tuning.length).toEqual(6); + expect(staff1.bars.length).toEqual(1); + expect(staff1.bars[0].clef).toEqual(Clef.G2); + } + } + { + let track3: Track = score.tracks[2]; + expect(track3.name).toEqual('Second Guitar'); + expect(track3.staves.length).toEqual(1); + expect(track3.playbackInfo.program).toEqual(25); + expect(track3.playbackInfo.primaryChannel).toEqual(4); + expect(track3.playbackInfo.secondaryChannel).toEqual(5); + { + let staff1: Staff = track3.staves[0]; + expect(staff1.showTablature).toBe(true); + expect(staff1.showStandardNotation).toBe(true); + expect(staff1.tuning.length).toEqual(6); + expect(staff1.bars.length).toEqual(1); + expect(staff1.bars[0].clef).toEqual(Clef.G2); + } + } + }); + + it('multi-track-multi-staff-inconsistent-bars', () => { + let tex: string = ` + \\track "Piano" + \\staff{score} \\tuning piano \\instrument acousticgrandpiano + c4 d4 e4 f4 | + + \\staff{score} \\tuning piano \\clef F4 + c2 c2 c2 c2 | c2 c2 c2 c2 | c2 c2 c2 c2 | + + \\track "Guitar" + \\staff{tabs} + 1.2 3.2 0.1 1.1 | 1.2 3.2 0.1 1.1 | + + \\track "Second Guitar" + 1.2 3.2 0.1 1.1 + `; + let score: Score = parseTex(tex); + expect(score.tracks.length).toEqual(3); + expect(score.masterBars.length).toEqual(3); + { + let track1: Track = score.tracks[0]; + expect(track1.name).toEqual('Piano'); + expect(track1.staves.length).toEqual(2); + expect(track1.playbackInfo.program).toEqual(0); + expect(track1.playbackInfo.primaryChannel).toEqual(0); + expect(track1.playbackInfo.secondaryChannel).toEqual(1); + { + let staff1: Staff = track1.staves[0]; + expect(staff1.showTablature).toBe(false); + expect(staff1.showStandardNotation).toBe(true); + expect(staff1.tuning.length).toEqual(0); + expect(staff1.bars.length).toEqual(3); + expect(staff1.bars[0].isEmpty).toBe(false); + expect(staff1.bars[1].isEmpty).toBe(true); + expect(staff1.bars[2].isEmpty).toBe(true); + expect(staff1.bars[0].clef).toEqual(Clef.G2); + } + { + let staff2: Staff = track1.staves[1]; + expect(staff2.showTablature).toBe(false); + expect(staff2.showStandardNotation).toBe(true); + expect(staff2.tuning.length).toEqual(0); + expect(staff2.bars.length).toEqual(3); + expect(staff2.bars[0].isEmpty).toBe(false); + expect(staff2.bars[1].isEmpty).toBe(false); + expect(staff2.bars[2].isEmpty).toBe(false); + expect(staff2.bars[0].clef).toEqual(Clef.F4); + } + } + { + let track2: Track = score.tracks[1]; + expect(track2.name).toEqual('Guitar'); + expect(track2.staves.length).toEqual(1); + expect(track2.playbackInfo.program).toEqual(25); + expect(track2.playbackInfo.primaryChannel).toEqual(2); + expect(track2.playbackInfo.secondaryChannel).toEqual(3); + { + let staff1: Staff = track2.staves[0]; + expect(staff1.showTablature).toBe(true); + expect(staff1.showStandardNotation).toBe(false); + expect(staff1.tuning.length).toEqual(6); + expect(staff1.bars.length).toEqual(3); + expect(staff1.bars[0].isEmpty).toBe(false); + expect(staff1.bars[1].isEmpty).toBe(false); + expect(staff1.bars[2].isEmpty).toBe(true); + expect(staff1.bars[0].clef).toEqual(Clef.G2); + } + } + { + let track3: Track = score.tracks[2]; + expect(track3.name).toEqual('Second Guitar'); + expect(track3.staves.length).toEqual(1); + expect(track3.playbackInfo.program).toEqual(25); + expect(track3.playbackInfo.primaryChannel).toEqual(4); + expect(track3.playbackInfo.secondaryChannel).toEqual(5); + { + let staff1: Staff = track3.staves[0]; + expect(staff1.showTablature).toBe(true); + expect(staff1.showStandardNotation).toBe(true); + expect(staff1.tuning.length).toEqual(6); + expect(staff1.bars.length).toEqual(3); + expect(staff1.bars[0].isEmpty).toBe(false); + expect(staff1.bars[1].isEmpty).toBe(true); + expect(staff1.bars[2].isEmpty).toBe(true); + expect(staff1.bars[0].clef).toEqual(Clef.G2); + } + } + }); + + it('slides', () => { + let tex: string = '3.3{sl} 4.3 | 3.3{ss} 4.3 | 3.3{sib} 3.3{sia} 3.3{sou} 3.3{sod} | 3.3{psd} 3.3{psu}'; + let score: Score = parseTex(tex); + expect(score.tracks.length).toEqual(1); + expect(score.masterBars.length).toEqual(4); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].slideOutType).toEqual(SlideOutType.Legato); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].slideTarget!.id).toEqual( + score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].id + ); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].slideOutType).toEqual(SlideOutType.Shift); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].slideTarget!.id).toEqual( + score.tracks[0].staves[0].bars[1].voices[0].beats[1].notes[0].id + ); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].notes[0].slideInType).toEqual( + SlideInType.IntoFromBelow + ); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[1].notes[0].slideInType).toEqual( + SlideInType.IntoFromAbove + ); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[2].notes[0].slideOutType).toEqual(SlideOutType.OutUp); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[3].notes[0].slideOutType).toEqual( + SlideOutType.OutDown + ); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].notes[0].slideOutType).toEqual( + SlideOutType.PickSlideDown + ); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[1].notes[0].slideOutType).toEqual( + SlideOutType.PickSlideUp + ); + }); + + it('section', () => { + let tex: string = '\\section Intro 1.1 | 1.1 | \\section "Chorus 01" 1.1 | \\section S Solo'; + let score: Score = parseTex(tex); + expect(score.tracks.length).toEqual(1); + expect(score.masterBars.length).toEqual(4); + expect(score.masterBars[0].isSectionStart).toBe(true); + expect(score.masterBars[0].section!.text).toEqual('Intro'); + expect(score.masterBars[0].section!.marker).toEqual(''); + expect(score.masterBars[1].isSectionStart).toBe(false); + expect(score.masterBars[2].isSectionStart).toBe(true); + expect(score.masterBars[2].section!.text).toEqual('Chorus 01'); + expect(score.masterBars[2].section!.marker).toEqual(''); + expect(score.masterBars[3].isSectionStart).toBe(true); + expect(score.masterBars[3].section!.text).toEqual('Solo'); + expect(score.masterBars[3].section!.marker).toEqual('S'); + }); + + it('pop-slap-tap', () => { + let tex: string = '3.3{p} 3.3{s} 3.3{tt} r'; + let score: Score = parseTex(tex); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].pop).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].slap).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].tap).toBe(true); + }); + + it('triplet-feel-numeric', () => { + let tex: string = '\\tf 0 | \\tf 1 | \\tf 2 | \\tf 3 | \\tf 4 | \\tf 5 | \\tf 6'; + let score: Score = parseTex(tex); + expect(score.masterBars[0].tripletFeel).toEqual(TripletFeel.NoTripletFeel); + expect(score.masterBars[1].tripletFeel).toEqual(TripletFeel.Triplet16th); + expect(score.masterBars[2].tripletFeel).toEqual(TripletFeel.Triplet8th); + expect(score.masterBars[3].tripletFeel).toEqual(TripletFeel.Dotted16th); + expect(score.masterBars[4].tripletFeel).toEqual(TripletFeel.Dotted8th); + expect(score.masterBars[5].tripletFeel).toEqual(TripletFeel.Scottish16th); + expect(score.masterBars[6].tripletFeel).toEqual(TripletFeel.Scottish8th); + }); + + it('triplet-feel-long-names', () => { + let tex: string = + '\\tf none | \\tf triplet-16th | \\tf triplet-8th | \\tf dotted-16th | \\tf dotted-8th | \\tf scottish-16th | \\tf scottish-8th'; + let score: Score = parseTex(tex); + expect(score.masterBars[0].tripletFeel).toEqual(TripletFeel.NoTripletFeel); + expect(score.masterBars[1].tripletFeel).toEqual(TripletFeel.Triplet16th); + expect(score.masterBars[2].tripletFeel).toEqual(TripletFeel.Triplet8th); + expect(score.masterBars[3].tripletFeel).toEqual(TripletFeel.Dotted16th); + expect(score.masterBars[4].tripletFeel).toEqual(TripletFeel.Dotted8th); + expect(score.masterBars[5].tripletFeel).toEqual(TripletFeel.Scottish16th); + expect(score.masterBars[6].tripletFeel).toEqual(TripletFeel.Scottish8th); + }); + + it('triplet-feel-short-names', () => { + let tex: string = '\\tf no | \\tf t16 | \\tf t8 | \\tf d16 | \\tf d8 | \\tf s16 | \\tf s8'; + let score: Score = parseTex(tex); + expect(score.masterBars[0].tripletFeel).toEqual(TripletFeel.NoTripletFeel); + expect(score.masterBars[1].tripletFeel).toEqual(TripletFeel.Triplet16th); + expect(score.masterBars[2].tripletFeel).toEqual(TripletFeel.Triplet8th); + expect(score.masterBars[3].tripletFeel).toEqual(TripletFeel.Dotted16th); + expect(score.masterBars[4].tripletFeel).toEqual(TripletFeel.Dotted8th); + expect(score.masterBars[5].tripletFeel).toEqual(TripletFeel.Scottish16th); + expect(score.masterBars[6].tripletFeel).toEqual(TripletFeel.Scottish8th); + }); + + it('triplet-feel-multi-bar', () => { + let tex: string = '\\tf t16 | | | \\tf t8 | | | \\tf no | | '; + let score: Score = parseTex(tex); + expect(score.masterBars[0].tripletFeel).toEqual(TripletFeel.Triplet16th); + expect(score.masterBars[1].tripletFeel).toEqual(TripletFeel.Triplet16th); + expect(score.masterBars[2].tripletFeel).toEqual(TripletFeel.Triplet16th); + expect(score.masterBars[3].tripletFeel).toEqual(TripletFeel.Triplet8th); + expect(score.masterBars[4].tripletFeel).toEqual(TripletFeel.Triplet8th); + expect(score.masterBars[5].tripletFeel).toEqual(TripletFeel.Triplet8th); + expect(score.masterBars[6].tripletFeel).toEqual(TripletFeel.NoTripletFeel); + expect(score.masterBars[7].tripletFeel).toEqual(TripletFeel.NoTripletFeel); + expect(score.masterBars[8].tripletFeel).toEqual(TripletFeel.NoTripletFeel); + }); + + it('tuplet-repeat', () => { + let tex: string = ':8 5.3{tu 3}*3'; + let score: Score = parseTex(tex); + let durations: Duration[] = [Duration.Eighth, Duration.Eighth, Duration.Eighth]; + let tuplets = [3, 3, 3]; + let i: number = 0; + let b: Beat | null = score.tracks[0].staves[0].bars[0].voices[0].beats[0]; + while (b) { + expect(b.duration).toEqual(durations[i], `Duration on beat ${i} was wrong`); + if (tuplets[i] === 1) { + expect(b.hasTuplet).toBe(false); + } else { + expect(b.tupletNumerator).toEqual(tuplets[i], `Tuplet on beat ${i} was wrong`); + } + b = b.nextBeat; + i++; + } + expect(i).toEqual(durations.length); + }); + + it('simple-anacrusis', () => { + let tex: string = '\\ac 3.3 3.3 | 1.1 2.1 3.1 4.1'; + let score: Score = parseTex(tex); + expect(score.masterBars[0].isAnacrusis).toBe(true); + expect(score.masterBars[0].calculateDuration()).toEqual(1920); + expect(score.masterBars[1].calculateDuration()).toEqual(3840); + }); + + it('multi-bar-anacrusis', () => { + let tex: string = '\\ac 3.3 3.3 | \\ac 3.3 3.3 | 1.1 2.1 3.1 4.1'; + let score: Score = parseTex(tex); + expect(score.masterBars[0].isAnacrusis).toBe(true); + expect(score.masterBars[1].isAnacrusis).toBe(true); + expect(score.masterBars[0].calculateDuration()).toEqual(1920); + expect(score.masterBars[1].calculateDuration()).toEqual(1920); + expect(score.masterBars[2].calculateDuration()).toEqual(3840); + }); + + it('random-anacrusis', () => { + let tex: string = '\\ac 3.3 3.3 | 1.1 2.1 3.1 4.1 | \\ac 3.3 3.3 | 1.1 2.1 3.1 4.1'; + let score: Score = parseTex(tex); + expect(score.masterBars[0].isAnacrusis).toBe(true); + expect(score.masterBars[1].isAnacrusis).toBe(false); + expect(score.masterBars[2].isAnacrusis).toBe(true); + expect(score.masterBars[3].isAnacrusis).toBe(false); + expect(score.masterBars[0].calculateDuration()).toEqual(1920); + expect(score.masterBars[1].calculateDuration()).toEqual(3840); + expect(score.masterBars[2].calculateDuration()).toEqual(1920); + expect(score.masterBars[3].calculateDuration()).toEqual(3840); + }); + + it('repeat', () => { + let tex: string = + '\\ro 1.3 2.3 3.3 4.3 | 5.3 6.3 7.3 8.3 | \\rc 2 1.3 2.3 3.3 4.3 | \\ro \\rc 3 1.3 2.3 3.3 4.3 |'; + let score: Score = parseTex(tex); + expect(score.masterBars[0].repeatCount).toEqual(0); + expect(score.masterBars[1].repeatCount).toEqual(0); + expect(score.masterBars[2].repeatCount).toEqual(2); + expect(score.masterBars[3].repeatCount).toEqual(3); + }); + + it('default-transposition-on-instruments', () => { + let tex: string = ` + \\track "Piano with Grand Staff" "pno." + \\staff{score} \\tuning piano \\instrument acousticgrandpiano + c4 d4 e4 f4 | + \\staff{score} \\tuning piano \\clef F4 + c2 c2 c2 c2 | + \\track Guitar" + \\staff{tabs} \\capo 5 + 1.2 3.2 0.1 1.1 + `; + let score: Score = parseTex(tex); + + expect(score.tracks[0].staves[0].transpositionPitch).toEqual(0); + expect(score.tracks[0].staves[0].displayTranspositionPitch).toEqual(0); + expect(score.tracks[0].staves[1].transpositionPitch).toEqual(0); + expect(score.tracks[0].staves[1].displayTranspositionPitch).toEqual(0); + expect(score.tracks[1].staves[0].transpositionPitch).toEqual(0); + expect(score.tracks[1].staves[0].displayTranspositionPitch).toEqual(-12); + }); + + it('dynamics', () => { + let tex: string = '1.1.8{dy ppp} 1.1{dy pp} 1.1{dy p} 1.1{dy mp} 1.1{dy mf} 1.1{dy f} 1.1{dy ff} 1.1{dy fff}'; + let score: Score = parseTex(tex); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].dynamics).toEqual(DynamicValue.PPP); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].dynamics).toEqual(DynamicValue.PP); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].dynamics).toEqual(DynamicValue.P); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].dynamics).toEqual(DynamicValue.MP); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[4].dynamics).toEqual(DynamicValue.MF); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[5].dynamics).toEqual(DynamicValue.F); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[6].dynamics).toEqual(DynamicValue.FF); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[7].dynamics).toEqual(DynamicValue.FFF); + }); + + it('dynamics-auto', () => { + let tex: string = '1.1.4{dy ppp} 1.1 1.1{dy mp} 1.1'; + let score: Score = parseTex(tex); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].dynamics).toEqual(DynamicValue.PPP); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].dynamics).toEqual(DynamicValue.PPP); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].dynamics).toEqual(DynamicValue.MP); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].dynamics).toEqual(DynamicValue.MP); + }); + + it('dynamics-auto-reset-on-track', () => { + let tex: string = '1.1.4{dy ppp} 1.1 \\track "Second" 1.1.4'; + let score: Score = parseTex(tex); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].dynamics).toEqual(DynamicValue.PPP); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].dynamics).toEqual(DynamicValue.PPP); + expect(score.tracks[1].staves[0].bars[0].voices[0].beats[0].dynamics).toEqual(DynamicValue.F); + }); + + it('dynamics-auto-reset-on-staff', () => { + let tex: string = '1.1.4{dy ppp} 1.1 \\staff 1.1.4'; + let score: Score = parseTex(tex); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].dynamics).toEqual(DynamicValue.PPP); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].dynamics).toEqual(DynamicValue.PPP); + expect(score.tracks[0].staves[1].bars[0].voices[0].beats[0].dynamics).toEqual(DynamicValue.F); + }); + + it('crescendo', () => { + let tex: string = '1.1.4{dec} 1.1{dec} 1.1{cre} 1.1{cre}'; + let score: Score = parseTex(tex); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].crescendo).toEqual(CrescendoType.Decrescendo); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].crescendo).toEqual(CrescendoType.Decrescendo); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].crescendo).toEqual(CrescendoType.Crescendo); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].crescendo).toEqual(CrescendoType.Crescendo); + }); + + it('left-hand-tapping', () => { + let tex: string = ':4 1.1{lht} 1.1 1.1{lht} 1.1'; + let score: Score = parseTex(tex); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isLeftHandTapped).toEqual(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].isLeftHandTapped).toEqual(false); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].isLeftHandTapped).toEqual(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].isLeftHandTapped).toEqual(false); + }); +}); diff --git a/test/importer/BinaryStylesheet.test.ts b/test/importer/BinaryStylesheet.test.ts new file mode 100644 index 000000000..291af2dec --- /dev/null +++ b/test/importer/BinaryStylesheet.test.ts @@ -0,0 +1,224 @@ +import { BinaryStylesheet } from '@src/importer/BinaryStylesheet'; +import { Color } from '@src/model/Color'; +import { TestPlatform } from '@test/TestPlatform'; +import { ModelUtils } from '@src/model/ModelUtils'; + +describe('BinaryStylesheetParserTest', () => { + it('testRead', async () => { + const data = await TestPlatform.loadFile('test-data/guitarpro7/BinaryStylesheet'); + let stylesheet: BinaryStylesheet = new BinaryStylesheet(data); + + expect(stylesheet.raw.has('Global/chordNameStyle')).toBeTrue(); + expect(stylesheet.raw.get('Global/chordNameStyle')).toEqual(2); + + expect(stylesheet.raw.has('StandardNotation/deadNoteSymbol')).toBeTrue(); + expect(stylesheet.raw.get('StandardNotation/deadNoteSymbol')).toEqual(0); + + expect(stylesheet.raw.has('Header/WordsAndMusic')).toBeTrue(); + expect(stylesheet.raw.get('Header/WordsAndMusic')).toEqual('Words & Music by %MUSIC%'); + + expect(stylesheet.raw.has('Global/PickStrokePriority')).toBeTrue(); + expect(stylesheet.raw.get('Global/PickStrokePriority')).toEqual(1100); + + expect(stylesheet.raw.has('Odd/drawOddFooter')).toBeTrue(); + expect(stylesheet.raw.get('Odd/drawOddFooter')).toEqual(true); + + expect(stylesheet.raw.has('TablatureNotation/tabRhythmPlacementVoice3')).toBeTrue(); + expect(stylesheet.raw.get('TablatureNotation/tabRhythmPlacementVoice3')).toEqual(2); + + expect(stylesheet.raw.has('Global/HideTupletBracket')).toBeTrue(); + expect(stylesheet.raw.get('Global/HideTupletBracket')).toEqual(true); + + expect(stylesheet.raw.has('Global/DrawChords')).toBeTrue(); + expect(stylesheet.raw.get('Global/DrawChords')).toEqual(true); + + expect(stylesheet.raw.has('System/codaSplitWidth')).toBeTrue(); + expect(ModelUtils.isAlmostEqualTo(stylesheet.raw.get('System/codaSplitWidth') as number, 2.0)).toBeTrue(); + + expect(stylesheet.raw.has('Global/HarmonicPriority')).toBeTrue(); + expect(stylesheet.raw.get('Global/HarmonicPriority')).toEqual(2200); + + expect(stylesheet.raw.has('Global/LetRingThroughoutPriority')).toBeTrue(); + expect(stylesheet.raw.get('Global/LetRingThroughoutPriority')).toEqual(2500); + + expect(stylesheet.raw.has('Global/stretchFactor')).toBeTrue(); + expect(ModelUtils.isAlmostEqualTo(stylesheet.raw.get('Global/stretchFactor') as number, 0.5)).toBeTrue(); + + expect(stylesheet.raw.has('StandardNotation/bendHeight')).toBeTrue(); + expect(ModelUtils.isAlmostEqualTo(stylesheet.raw.get('StandardNotation/bendHeight') as number, 2.0)).toBeTrue(); + + expect(stylesheet.raw.has('Global/ChordDiagramPriority')).toBeTrue(); + expect(stylesheet.raw.get('Global/ChordDiagramPriority')).toEqual(3000); + + expect(stylesheet.raw.has('Global/AlternateEndingPriority')).toBeTrue(); + expect(stylesheet.raw.get('Global/AlternateEndingPriority')).toEqual(2800); + + expect(stylesheet.raw.has('StandardNotation/tieOffsetX')).toBeTrue(); + expect(ModelUtils.isAlmostEqualTo(stylesheet.raw.get('StandardNotation/tieOffsetX') as number, 0.105)).toBeTrue(); + + expect(stylesheet.raw.has('Global/PalmMutePriority')).toBeTrue(); + expect(stylesheet.raw.get('Global/PalmMutePriority')).toEqual(1200); + + expect(stylesheet.raw.has('System/hideLyrics')).toBeTrue(); + expect(stylesheet.raw.get('System/hideLyrics')).toEqual(false); + + expect(stylesheet.raw.has('Global/drawArpeggioArrow')).toBeTrue(); + expect(stylesheet.raw.get('Global/drawArpeggioArrow')).toEqual(true); + + expect(stylesheet.raw.has('Global/HoPoPriority')).toBeTrue(); + expect(stylesheet.raw.get('Global/HoPoPriority')).toEqual(800); + + expect(stylesheet.raw.has('Staff/repeatWidth')).toBeTrue(); + expect(ModelUtils.isAlmostEqualTo(stylesheet.raw.get('Staff/repeatWidth') as number, 0.5)).toBeTrue(); + + expect(stylesheet.raw.has('System/bracketWidth')).toBeTrue(); + expect(ModelUtils.isAlmostEqualTo(stylesheet.raw.get('System/bracketWidth') as number, 0.5)).toBeTrue(); + + expect(stylesheet.raw.has('Global/TuningSpaceInFrontOfStaff')).toBeTrue(); + expect( + ModelUtils.isAlmostEqualTo(stylesheet.raw.get('Global/TuningSpaceInFrontOfStaff') as number, 2.0) + ).toBeTrue(); + + expect(stylesheet.raw.has('StandardNotation/drawWholeRestOnEmptyBars')).toBeTrue(); + expect(stylesheet.raw.get('StandardNotation/drawWholeRestOnEmptyBars')).toEqual(false); + + expect(stylesheet.raw.has('Global/miniBrowserPosition')).toBeTrue(); + expect(stylesheet.raw.get('Global/miniBrowserPosition')).toEqual(0); + + expect(stylesheet.raw.has('StandardNotation/hideUselessRests')).toBeTrue(); + expect(stylesheet.raw.get('StandardNotation/hideUselessRests')).toEqual(true); + + expect(stylesheet.raw.has('Global/SpacingAffectFontsSize')).toBeTrue(); + expect(stylesheet.raw.get('Global/SpacingAffectFontsSize')).toEqual(true); + + expect(stylesheet.raw.has('Even/drawEvenCopyright')).toBeTrue(); + expect(stylesheet.raw.get('Even/drawEvenCopyright')).toEqual(true); + + expect(stylesheet.raw.has('Global/RepeatTargetPriority')).toBeTrue(); + expect(stylesheet.raw.get('Global/RepeatTargetPriority')).toEqual(3300); + + expect(stylesheet.raw.has('Global/SVGFont')).toBeTrue(); + expect(stylesheet.raw.get('Global/SVGFont')).toEqual(':/renderer/resources/notes.svg'); + + expect(stylesheet.raw.has('Footer/PageNumberAlignment')).toBeTrue(); + expect(stylesheet.raw.get('Footer/PageNumberAlignment')).toEqual(2); + + expect(stylesheet.raw.has('Global/graceFlatScaleFactor')).toBeTrue(); + expect( + ModelUtils.isAlmostEqualTo(stylesheet.raw.get('Global/graceFlatScaleFactor') as number, 0.8333282) + ).toBeTrue(); + + expect(stylesheet.raw.has('Global/shadowColorEnd')).toBeTrue(); + expect((stylesheet.raw.get('Global/shadowColorEnd') as Color).r).toEqual(90); + expect((stylesheet.raw.get('Global/shadowColorEnd') as Color).g).toEqual(90); + expect((stylesheet.raw.get('Global/shadowColorEnd') as Color).b).toEqual(90); + expect((stylesheet.raw.get('Global/shadowColorEnd') as Color).a).toEqual(10); + + expect(stylesheet.raw.has('Even/EvenCopyright')).toBeTrue(); + expect(stylesheet.raw.get('Even/EvenCopyright')).toEqual('%COPYRIGHT%'); + + expect(stylesheet.raw.has('Global/GolpePriority')).toBeTrue(); + expect(stylesheet.raw.get('Global/GolpePriority')).toEqual(350); + + expect(stylesheet.raw.has('Global/spaceSizeMM')).toBeTrue(); + expect(ModelUtils.isAlmostEqualTo(stylesheet.raw.get('Global/spaceSizeMM') as number, 0.5)).toBeTrue(); + + expect(stylesheet.raw.has('TablatureNotation/drawSecondNoteTrill')).toBeTrue(); + expect(stylesheet.raw.get('TablatureNotation/drawSecondNoteTrill')).toEqual(true); + + expect(stylesheet.raw.has('System/insertSize')).toBeTrue(); + expect(stylesheet.raw.get('System/insertSize')).toEqual(2); + + expect(stylesheet.raw.has('TablatureNotation/minimalInformationForHarmonic')).toBeTrue(); + expect(stylesheet.raw.get('TablatureNotation/minimalInformationForHarmonic')).toEqual(true); + + expect(stylesheet.raw.has('PageSetup/pageTopMargin')).toBeTrue(); + expect(ModelUtils.isAlmostEqualTo(stylesheet.raw.get('PageSetup/pageTopMargin') as number, 8.0)).toBeTrue(); + + expect(stylesheet.raw.has('StandardNotation/augmentationDotRadius')).toBeTrue(); + expect( + ModelUtils.isAlmostEqualTo(stylesheet.raw.get('StandardNotation/augmentationDotRadius') as number, 0.125) + ).toBeTrue(); + + expect(stylesheet.raw.has('Odd/drawOddCopyright')).toBeTrue(); + expect(stylesheet.raw.get('Odd/drawOddCopyright')).toEqual(false); + + expect(stylesheet.raw.has('TablatureNotation/forceRhythmicBand')).toBeTrue(); + expect(stylesheet.raw.get('TablatureNotation/forceRhythmicBand')).toEqual(false); + + expect(stylesheet.raw.has('System/codaSplit')).toBeTrue(); + expect(stylesheet.raw.get('System/codaSplit')).toEqual(true); + + expect(stylesheet.raw.has('StandardNotation/tieMaxHeight')).toBeTrue(); + expect(ModelUtils.isAlmostEqualTo(stylesheet.raw.get('StandardNotation/tieMaxHeight') as number, 2.0)).toBeTrue(); + + expect(stylesheet.raw.has('Header/WordsAndMusicAlignment')).toBeTrue(); + expect(stylesheet.raw.get('Header/WordsAndMusicAlignment')).toEqual(2); + + expect(stylesheet.raw.has('Even/drawEvenFooter')).toBeTrue(); + expect(stylesheet.raw.get('Even/drawEvenFooter')).toEqual(true); + + expect(stylesheet.raw.has('StandardNotation/rightFingeringPositionSN')).toBeTrue(); + expect(stylesheet.raw.get('StandardNotation/rightFingeringPositionSN')).toEqual(1); + + expect(stylesheet.raw.has('System/bracketCurveHeight')).toBeTrue(); + expect( + ModelUtils.isAlmostEqualTo(stylesheet.raw.get('System/bracketCurveHeight') as number, 1.600006) + ).toBeTrue(); + + expect(stylesheet.raw.has('Global/FreeTimePriority')).toBeTrue(); + expect(stylesheet.raw.get('Global/FreeTimePriority')).toEqual(2700); + + expect(stylesheet.raw.has('Global/ChordSpacingMillimeter')).toBeTrue(); + expect(ModelUtils.isAlmostEqualTo(stylesheet.raw.get('Global/ChordSpacingMillimeter') as number, 2.0)).toBeTrue(); + + expect(stylesheet.raw.has('Header/drawAlbum')).toBeTrue(); + expect(stylesheet.raw.get('Header/drawAlbum')).toEqual(true); + + expect(stylesheet.raw.has('System/trackNameModeMulti')).toBeTrue(); + expect(stylesheet.raw.get('System/trackNameModeMulti')).toEqual(1); + + expect(stylesheet.raw.has('System/insertSizeSameTrack')).toBeTrue(); + expect(stylesheet.raw.get('System/insertSizeSameTrack')).toEqual(1); + + expect(stylesheet.raw.has('System/marginMinimalBeforeFirstNote')).toBeTrue(); + expect( + ModelUtils.isAlmostEqualTo(stylesheet.raw.get('System/marginMinimalBeforeFirstNote') as number, 0.5) + ).toBeTrue(); + + expect(stylesheet.raw.has('Header/Subtitle')).toBeTrue(); + expect(stylesheet.raw.get('Header/Subtitle')).toEqual('%SUBTITLE%'); + + expect(stylesheet.raw.has('Global/alphaSuggested')).toBeTrue(); + expect(ModelUtils.isAlmostEqualTo(stylesheet.raw.get('Global/alphaSuggested') as number, 0.5)).toBeTrue(); + + expect(stylesheet.raw.has('Even/EvenHeaderAlignment')).toBeTrue(); + expect(stylesheet.raw.get('Even/EvenHeaderAlignment')).toEqual(0); + + expect(stylesheet.raw.has('Global/TechniqueSymbol')).toBeTrue(); + expect(stylesheet.raw.get('Global/TechniqueSymbol')).toEqual(25); + + expect(stylesheet.raw.has('Global/tuningBoxed')).toBeTrue(); + expect(stylesheet.raw.get('Global/tuningBoxed')).toEqual(false); + + expect(stylesheet.raw.has('StandardNotation/drawBends')).toBeTrue(); + expect(stylesheet.raw.get('StandardNotation/drawBends')).toEqual(true); + + expect(stylesheet.raw.has('Global/mouseClickMaxTime')).toBeTrue(); + expect(stylesheet.raw.get('Global/mouseClickMaxTime')).toEqual(200); + + expect(stylesheet.raw.has('Global/graceSharpScaleFactor')).toBeTrue(); + expect( + ModelUtils.isAlmostEqualTo(stylesheet.raw.get('Global/graceSharpScaleFactor') as number, 1.333344) + ).toBeTrue(); + + expect(stylesheet.raw.has('Global/GrayedOpacity')).toBeTrue(); + expect(ModelUtils.isAlmostEqualTo(stylesheet.raw.get('Global/GrayedOpacity') as number, 0.4000015)).toBeTrue(); + + expect(stylesheet.raw.has('Global/WhammyBarVibratoPriority')).toBeTrue(); + expect(stylesheet.raw.get('Global/WhammyBarVibratoPriority')).toEqual(1400); + + expect(stylesheet.raw.has('TablatureNotation/noStaffLineForSlashs')).toBeTrue(); + expect(stylesheet.raw.get('TablatureNotation/noStaffLineForSlashs')).toEqual(false); + }); +}); diff --git a/test/importer/Gp3Importer.test.ts b/test/importer/Gp3Importer.test.ts new file mode 100644 index 000000000..a0defad10 --- /dev/null +++ b/test/importer/Gp3Importer.test.ts @@ -0,0 +1,144 @@ +import { AutomationType } from '@src/model/Automation'; +import { BrushType } from '@src/model/BrushType'; +import { DynamicValue } from '@src/model/DynamicValue'; +import { Score } from '@src/model/Score'; +import { SlideOutType } from '@src/model/SlideOutType'; +import { GpImporterTestHelper } from '@test/importer/GpImporterTestHelper'; + +describe('Gp3ImporterTest', () => { + it('score-info', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro3/score-info.gp3'); + let score: Score = reader.readScore(); + expect(score.title).toEqual('Title'); + expect(score.subTitle).toEqual('Subtitle'); + expect(score.artist).toEqual('Artist'); + expect(score.album).toEqual('Album'); + expect(score.words).toEqual('Music'); // no words in gp4 + + expect(score.music).toEqual('Music'); + expect(score.copyright).toEqual('Copyright'); + expect(score.tab).toEqual('Tab'); + expect(score.instructions).toEqual('Instructions'); + expect(score.notices).toEqual('Notice1\r\nNotice2'); + expect(score.masterBars.length).toEqual(5); + expect(score.tracks.length).toEqual(1); + expect(score.tracks[0].name).toEqual('Track 1'); + }); + + it('notes', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro3/notes.gp3'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkNotes(score); + }); + + it('time-signatures', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro3/time-signatures.gp3'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkTimeSignatures(score); + }); + + it('dead', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro3/dead.gp3'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkDead(score); + }); + + it('accentuations', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro3/accentuations.gp3'); + let score: Score = reader.readScore(); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isGhost).toBe(true); + // it seems accentuation is handled as Forte Fortissimo + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].dynamics).toEqual(DynamicValue.FFF); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].isLetRing).toBe(true); + }); + + it('testGuitarPro3Harmonics', async () => { + // TODO: Find out about GP3 harmonics! + }); + + it('hammer', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro3/hammer.gp3'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkHammer(score); + }); + + it('bends', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro3/bends.gp3'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkBend(score); + }); + + it('slides', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro3/slides.gp3'); + let score: Score = reader.readScore(); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].getNoteOnString(5)!.slideOutType).toEqual( + SlideOutType.Shift + ); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].getNoteOnString(2)!.slideOutType).toEqual( + SlideOutType.Shift + ); + }); + + it('vibrato', async () => { + // TODO: Check why this vibrato is not recognized + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro3/vibrato.gp3'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkVibrato(score, false); + }); + + it('other-effects', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro3/other-effects.gp3'); + let score: Score = reader.readScore(); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].tap).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].slap).toBe(true); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].pop).toBe(true); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].fadeIn).toBe(true); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].hasChord).toBe(true); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].chord!.name).toEqual('C'); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[1].text).toEqual('Text'); + expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].getAutomation(AutomationType.Tempo)).toBeTruthy(); + expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].getAutomation(AutomationType.Tempo)!.value).toEqual( + 120 + ); + expect( + score.tracks[0].staves[0].bars[4].voices[0].beats[0].getAutomation(AutomationType.Instrument) + ).toBeTruthy(); + expect( + score.tracks[0].staves[0].bars[4].voices[0].beats[0].getAutomation(AutomationType.Instrument)!.value + ).toEqual(25); + }); + + it('strokes', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro3/strokes.gp3'); + let score: Score = reader.readScore(); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].brushType).toEqual(BrushType.BrushDown); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].brushType).toEqual(BrushType.BrushUp); + }); + + it('tuplets', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro3/tuplets.gp3'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkTuplets(score); + }); + + it('ranges', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro3/ranges.gp3'); + let score: Score = reader.readScore(); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].notes[0].isLetRing).toBe(true); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].notes[0].isLetRing).toBe(true); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].notes[0].isLetRing).toBe(true); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].notes[0].isLetRing).toBe(true); + }); + + it('effects', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro3/effects.gp3'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkEffects(score); + }); + + it('strings', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro3/strings.gp3'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkStrings(score); + }); +}); diff --git a/test/importer/Gp4Importer.test.ts b/test/importer/Gp4Importer.test.ts new file mode 100644 index 000000000..add5f942d --- /dev/null +++ b/test/importer/Gp4Importer.test.ts @@ -0,0 +1,143 @@ +import { Score } from '@src/model/Score'; +import { GpImporterTestHelper } from '@test/importer/GpImporterTestHelper'; + +describe('Gp4ImporterTest', () => { + it('score-info', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro4/score-info.gp4'); + let score: Score = reader.readScore(); + expect(score.title).toEqual('Title'); + expect(score.subTitle).toEqual('Subtitle'); + expect(score.artist).toEqual('Artist'); + expect(score.album).toEqual('Album'); + expect(score.words).toEqual('Music'); // no words in gp4 + + expect(score.music).toEqual('Music'); + expect(score.copyright).toEqual('Copyright'); + expect(score.tab).toEqual('Tab'); + expect(score.instructions).toEqual('Instructions'); + expect(score.notices).toEqual('Notice1\r\nNotice2'); + expect(score.masterBars.length).toEqual(5); + expect(score.tracks.length).toEqual(1); + expect(score.tracks[0].name).toEqual('Track 1'); + }); + + it('notes', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro4/notes.gp4'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkNotes(score); + }); + + it('time-signatures', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro4/time-signatures.gp4'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkTimeSignatures(score); + }); + + it('dead', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro4/dead.gp4'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkDead(score); + }); + + it('grace', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro4/grace.gp4'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkGrace(score); + }); + + it('accentuations', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro4/accentuations.gp4'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkAccentuations(score, false); + }); + + it('harmonics', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro4/harmonics.gp4'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkHarmonics(score); + }); + + it('hammer', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro4/hammer.gp4'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkHammer(score); + }); + + it('bend', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro4/bends.gp4'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkBend(score); + }); + + it('tremolo', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro4/tremolo.gp4'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkTremolo(score); + }); + + it('slides', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro4/slides.gp4'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkSlides(score); + }); + + it('vibrato', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro4/vibrato.gp4'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkVibrato(score, true); + }); + + it('trills', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro4/trills.gp4'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkTrills(score); + }); + + it('otherEffects', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro4/other-effects.gp4'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkOtherEffects(score, false); + }); + + it('fingering', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro4/fingering.gp4'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkFingering(score); + }); + + it('stroke', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro4/strokes.gp4'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkStroke(score); + }); + + it('tuplets', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro4/tuplets.gp4'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkTuplets(score); + }); + + it('ranges', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro4/ranges.gp4'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkRanges(score); + }); + + it('effects', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro4/effects.gp4'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkEffects(score); + }); + + it('strings', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro4/strings.gp4'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkStrings(score); + }); + + it('colors', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro4/colors.gp4'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkColors(score); + }); +}); diff --git a/test/importer/Gp5Importer.test.ts b/test/importer/Gp5Importer.test.ts new file mode 100644 index 000000000..52ae38e79 --- /dev/null +++ b/test/importer/Gp5Importer.test.ts @@ -0,0 +1,187 @@ +import { Score } from '@src/model/Score'; +import { GpImporterTestHelper } from '@test/importer/GpImporterTestHelper'; + +describe('Gp5ImporterTest', () => { + it('score-info', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro5/score-info.gp5'); + let score: Score = reader.readScore(); + expect(score.title).toEqual('Title'); + expect(score.subTitle).toEqual('Subtitle'); + expect(score.artist).toEqual('Artist'); + expect(score.album).toEqual('Album'); + expect(score.words).toEqual('Words'); + expect(score.music).toEqual('Music'); + expect(score.copyright).toEqual('Copyright'); + expect(score.tab).toEqual('Tab'); + expect(score.instructions).toEqual('Instructions'); + expect(score.notices).toEqual('Notice1\r\nNotice2'); + expect(score.masterBars.length).toEqual(5); + expect(score.tracks.length).toEqual(2); + expect(score.tracks[0].name).toEqual('Track 1'); + expect(score.tracks[1].name).toEqual('Track 2'); + }); + + it('notes', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro5/notes.gp5'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkNotes(score); + }); + + it('time-signatures', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro5/time-signatures.gp5'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkTimeSignatures(score); + }); + + it('dead', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro5/dead.gp5'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkDead(score); + }); + + it('grace', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro5/grace.gp5'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkGrace(score); + }); + + it('accentuations', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro5/accentuations.gp5'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkAccentuations(score, true); + }); + + it('harmonics', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro5/harmonics.gp5'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkHarmonics(score); + }); + + it('hammer', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro5/hammer.gp5'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkHammer(score); + }); + + it('bend', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro5/bends.gp5'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkBend(score); + }); + + it('tremolo', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro5/tremolo.gp5'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkTremolo(score); + }); + + it('slides', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro5/slides.gp5'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkSlides(score); + }); + + it('vibrato', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro5/vibrato.gp5'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkVibrato(score, true); + }); + + it('trills', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro5/trills.gp5'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkTrills(score); + }); + + it('other-effects', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro5/other-effects.gp5'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkOtherEffects(score, false); + }); + + it('fingering', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro5/fingering.gp5'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkFingering(score); + }); + + it('stroke', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro5/strokes.gp5'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkStroke(score); + }); + + it('tuplets', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro5/tuplets.gp5'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkTuplets(score); + }); + + it('ranges', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro5/ranges.gp5'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkRanges(score); + }); + + it('effects', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro5/effects.gp5'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkEffects(score); + }); + + it('serenade', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro5/serenade.gp5'); + reader.readScore(); + // only Check reading + }); + + it('strings', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro5/strings.gp5'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkStrings(score); + }); + + it('key-signatures', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro5/key-signatures.gp5'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkKeySignatures(score); + }); + + it('chords', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro5/chords.gp5'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkChords(score); + }); + + it('colors', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro5/colors.gp5'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkColors(score); + }); + + it('canon', async () => { + const reader = await GpImporterTestHelper.prepareImporterWithFile('guitarpro5/canon.gp5'); + let score: Score = reader.readScore(); + expect(score.title).toEqual('Canon Rock'); + expect(score.subTitle).toEqual(''); + expect(score.artist).toEqual('JerryC'); + expect(score.album).toEqual(''); + expect(score.words).toEqual(''); + expect(score.music).toEqual('JerryC'); + expect(score.copyright).toEqual(''); + expect(score.tab).toEqual(''); + expect(score.instructions).toEqual(''); + expect(score.notices).toEqual(''); + expect(score.masterBars.length).toEqual(224); + expect(score.tracks.length).toEqual(9); + expect(score.tracks[0].name).toEqual('Guitar Player'); + expect(score.tracks[1].name).toEqual('Low Bassy Sound'); + expect(score.tracks[2].name).toEqual('High Soundy Thing'); + expect(score.tracks[3].name).toEqual('Second Guitar'); + expect(score.tracks[4].name).toEqual('Drums'); + expect(score.tracks[5].name).toEqual('Harmonizer'); + expect(score.tracks[6].name).toEqual('The clean guitar'); + expect(score.tracks[7].name).toEqual('Track 8'); + expect(score.tracks[8].name).toEqual('Percussion'); + }); +}); diff --git a/test/importer/Gp7Importer.test.ts b/test/importer/Gp7Importer.test.ts new file mode 100644 index 000000000..ec9e5085d --- /dev/null +++ b/test/importer/Gp7Importer.test.ts @@ -0,0 +1,859 @@ +import { MidiUtils } from '@src/midi/MidiUtils'; +import { Gp7Importer } from '@src/importer/Gp7Importer'; +import { ByteBuffer } from '@src/io/ByteBuffer'; +import { Beat } from '@src/model/Beat'; +import { BendType } from '@src/model/BendType'; +import { FermataType } from '@src/model/Fermata'; +import { GraceType } from '@src/model/GraceType'; +import { MasterBar } from '@src/model/MasterBar'; +import { Note } from '@src/model/Note'; +import { Ottavia } from '@src/model/Ottavia'; +import { Score } from '@src/model/Score'; +import { SimileMark } from '@src/model/SimileMark'; +import { SlideOutType } from '@src/model/SlideOutType'; +import { VibratoType } from '@src/model/VibratoType'; +import { WhammyType } from '@src/model/WhammyType'; +import { Settings } from '@src/Settings'; +import { GpImporterTestHelper } from '@test/importer/GpImporterTestHelper'; +import { TestPlatform } from '@test/TestPlatform'; + +describe('Gp7ImporterTest', () => { + const prepareGp7ImporterWithFile:(name:string) => Promise = async (name: string): Promise => { + const data = await TestPlatform.loadFile('test-data/' + name); + return prepareGp7ImporterWithBytes(data); + }; + + const prepareGp7ImporterWithBytes: (buffer: Uint8Array) => Gp7Importer = (buffer: Uint8Array): Gp7Importer => { + let readerBase: Gp7Importer = new Gp7Importer(); + readerBase.init(ByteBuffer.fromBuffer(buffer), new Settings()); + return readerBase; + }; + + it('score-info', async () => { + const reader = await prepareGp7ImporterWithFile('guitarpro7/score-info.gp'); + let score: Score = reader.readScore(); + expect(score.title).toEqual('Title'); + expect(score.subTitle).toEqual('Subtitle'); + expect(score.artist).toEqual('Artist'); + expect(score.album).toEqual('Album'); + expect(score.words).toEqual('Words'); + expect(score.music).toEqual('Music'); + expect(score.copyright).toEqual('Copyright'); + expect(score.tab).toEqual('Tab'); + expect(score.instructions).toEqual('Instructions'); + expect(score.notices).toEqual('Notice1\nNotice2'); + expect(score.masterBars.length).toEqual(5); + expect(score.tracks.length).toEqual(2); + expect(score.tracks[0].name).toEqual('Track 1'); + expect(score.tracks[1].name).toEqual('Track 2'); + }); + + it('notes', async () => { + const reader = await prepareGp7ImporterWithFile('guitarpro7/notes.gp'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkNotes(score); + }); + + it('time-signatures', async () => { + const reader = await prepareGp7ImporterWithFile('guitarpro7/time-signatures.gp'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkTimeSignatures(score); + }); + + it('dead', async () => { + const reader = await prepareGp7ImporterWithFile('guitarpro7/dead.gp'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkDead(score); + }); + + it('grace', async () => { + const reader = await prepareGp7ImporterWithFile('guitarpro7/grace.gp'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkGrace(score); + }); + + it('accentuations', async () => { + const reader = await prepareGp7ImporterWithFile('guitarpro7/accentuations.gp'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkAccentuations(score, true); + }); + + it('harmonics', async () => { + const reader = await prepareGp7ImporterWithFile('guitarpro7/harmonics.gp'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkHarmonics(score); + }); + + it('hammer', async () => { + const reader = await prepareGp7ImporterWithFile('guitarpro7/hammer.gp'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkHammer(score); + }); + + it('bend', async () => { + const reader = await prepareGp7ImporterWithFile('guitarpro7/bends.gp'); + let score: Score = reader.readScore(); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendType).toEqual(BendType.Bend); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints.length).toEqual(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints[0].offset).toEqual(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints[0].value).toEqual(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints[1].offset).toEqual(60); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints[1].value).toEqual(4); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendType).toEqual(BendType.Bend); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints.length).toEqual(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints[0].offset).toEqual(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints[0].value).toEqual(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints[1].offset).toEqual(60); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints[1].value).toEqual(4); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendType).toEqual(BendType.BendRelease); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints.length).toEqual(4); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints[0].offset).toEqual(0); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints[0].value).toEqual(0); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints[1].offset).toEqual(30); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints[1].value).toEqual(12); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints[2].offset).toEqual(30); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints[2].value).toEqual(12); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints[3].offset).toEqual(60); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints[3].value).toEqual(6); + }); + + it('bends-advanced', async () => { + const reader = await prepareGp7ImporterWithFile('guitarpro7/bends-advanced.gp'); + let score: Score = reader.readScore(); + + // Simple Standalone Bends + + // // Bar 1 + let note: Note = score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0]; + expect(note.bendType).toEqual(BendType.Bend); + expect(note.bendPoints.length).toEqual(2); + expect(note.bendPoints[0].offset).toEqual(0); + expect(note.bendPoints[0].value).toEqual(0); + expect(note.bendPoints[1].offset).toEqual(15); + expect(note.bendPoints[1].value).toEqual(4); + + note = score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0]; + expect(note.bendType).toEqual(BendType.BendRelease); + expect(note.bendPoints.length).toEqual(4); + expect(note.bendPoints[0].offset).toEqual(0); + expect(note.bendPoints[0].value).toEqual(0); + expect(note.bendPoints[1].offset).toEqual(10); + expect(note.bendPoints[1].value).toEqual(4); + expect(note.bendPoints[2].offset).toEqual(20); + expect(note.bendPoints[2].value).toEqual(4); + expect(note.bendPoints[3].offset).toEqual(30); + expect(note.bendPoints[3].value).toEqual(0); + + // // Bar 2 + note = score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0]; + expect(note.bendType).toEqual(BendType.Bend); + expect(note.bendPoints.length).toEqual(2); + expect(note.bendPoints[0].offset).toEqual(0); + expect(note.bendPoints[0].value).toEqual(0); + expect(note.bendPoints[1].offset).toEqual(59); + expect(note.bendPoints[1].value).toEqual(4); + + note = score.tracks[0].staves[0].bars[1].voices[0].beats[1].notes[0]; + expect(note.bendType).toEqual(BendType.BendRelease); + expect(note.bendPoints.length).toEqual(4); + expect(note.bendPoints[0].offset).toEqual(0); + expect(note.bendPoints[0].value).toEqual(0); + expect(note.bendPoints[1].offset).toEqual(10); + expect(note.bendPoints[1].value).toEqual(4); + expect(note.bendPoints[2].offset).toEqual(45); + expect(note.bendPoints[2].value).toEqual(4); + expect(note.bendPoints[3].offset).toEqual(59); + expect(note.bendPoints[3].value).toEqual(0); + + // // Bar 3 + note = score.tracks[0].staves[0].bars[2].voices[0].beats[0].notes[0]; + expect(note.bendType).toEqual(BendType.Prebend); + expect(note.bendPoints.length).toEqual(2); + expect(note.bendPoints[0].offset).toEqual(0); + expect(note.bendPoints[0].value).toEqual(4); + expect(note.bendPoints[1].offset).toEqual(60); + expect(note.bendPoints[1].value).toEqual(4); + + note = score.tracks[0].staves[0].bars[2].voices[0].beats[1].notes[0]; + expect(note.bendType).toEqual(BendType.PrebendBend); + expect(note.bendPoints.length).toEqual(2); + expect(note.bendPoints[0].offset).toEqual(0); + expect(note.bendPoints[0].value).toEqual(4); + expect(note.bendPoints[1].offset).toEqual(15); + expect(note.bendPoints[1].value).toEqual(6); + + // // Bar 4 + note = score.tracks[0].staves[0].bars[3].voices[0].beats[0].notes[0]; + expect(note.bendType).toEqual(BendType.PrebendRelease); + expect(note.bendPoints.length).toEqual(2); + expect(note.bendPoints[0].offset).toEqual(0); + expect(note.bendPoints[0].value).toEqual(4); + expect(note.bendPoints[1].offset).toEqual(15); + expect(note.bendPoints[1].value).toEqual(0); + + // // Bar 5 + note = score.tracks[0].staves[0].bars[4].voices[0].beats[0].notes[0]; + expect(note.bendType).toEqual(BendType.Bend); + expect(note.bendPoints.length).toEqual(2); + expect(note.bendPoints[0].offset).toEqual(0); + expect(note.bendPoints[0].value).toEqual(0); + expect(note.bendPoints[1].offset).toEqual(14); + expect(note.bendPoints[1].value).toEqual(8); + + note = score.tracks[0].staves[0].bars[4].voices[0].beats[1].notes[0]; + expect(note.bendType).toEqual(BendType.BendRelease); + expect(note.bendPoints.length).toEqual(4); + expect(note.bendPoints[0].offset).toEqual(0); + expect(note.bendPoints[0].value).toEqual(0); + expect(note.bendPoints[1].offset).toEqual(9); + expect(note.bendPoints[1].value).toEqual(8); + expect(note.bendPoints[2].offset).toEqual(20); + expect(note.bendPoints[2].value).toEqual(8); + expect(note.bendPoints[3].offset).toEqual(31); + expect(note.bendPoints[3].value).toEqual(4); + + // // Bar 6 + note = score.tracks[0].staves[0].bars[5].voices[0].beats[0].notes[0]; + expect(note.bendType).toEqual(BendType.Prebend); + expect(note.bendPoints.length).toEqual(2); + expect(note.bendPoints[0].offset).toEqual(0); + expect(note.bendPoints[0].value).toEqual(8); + expect(note.bendPoints[1].offset).toEqual(60); + expect(note.bendPoints[1].value).toEqual(8); + + note = score.tracks[0].staves[0].bars[5].voices[0].beats[1].notes[0]; + expect(note.bendType).toEqual(BendType.PrebendBend); + expect(note.bendPoints.length).toEqual(2); + expect(note.bendPoints[0].offset).toEqual(0); + expect(note.bendPoints[0].value).toEqual(8); + expect(note.bendPoints[1].offset).toEqual(16); + expect(note.bendPoints[1].value).toEqual(12); + + // // Bar 7 + note = score.tracks[0].staves[0].bars[6].voices[0].beats[0].notes[0]; + expect(note.bendType).toEqual(BendType.PrebendRelease); + expect(note.bendPoints.length).toEqual(2); + expect(note.bendPoints[0].offset).toEqual(0); + expect(note.bendPoints[0].value).toEqual(8); + expect(note.bendPoints[1].offset).toEqual(14); + expect(note.bendPoints[1].value).toEqual(4); + + // // Bar 8 + note = score.tracks[0].staves[0].bars[7].voices[0].beats[0].notes[0]; + expect(note.bendType).toEqual(BendType.Bend); + expect(note.bendPoints.length).toEqual(2); + expect(note.bendPoints[0].offset).toEqual(0); + expect(note.bendPoints[0].value).toEqual(0); + expect(note.bendPoints[1].offset).toEqual(15); + expect(note.bendPoints[1].value).toEqual(4); + + // // Bar 9 + note = score.tracks[0].staves[0].bars[8].voices[0].beats[0].notes[0]; + expect(note.bendType).toEqual(BendType.BendRelease); + expect(note.bendPoints.length).toEqual(4); + expect(note.bendPoints[0].offset).toEqual(0); + expect(note.bendPoints[0].value).toEqual(0); + expect(note.bendPoints[1].offset).toEqual(10); + expect(note.bendPoints[1].value).toEqual(4); + expect(note.bendPoints[2].offset).toEqual(20); + expect(note.bendPoints[2].value).toEqual(4); + expect(note.bendPoints[3].offset).toEqual(30); + expect(note.bendPoints[3].value).toEqual(0); + // Combined Bends + + // // Bar 10 + note = score.tracks[0].staves[0].bars[9].voices[0].beats[0].notes[0]; + expect(note.bendType).toEqual(BendType.Bend); + expect(note.bendPoints.length).toEqual(2); + expect(note.bendPoints[0].offset).toEqual(0); + expect(note.bendPoints[0].value).toEqual(0); + expect(note.bendPoints[1].offset).toEqual(15); + expect(note.bendPoints[1].value).toEqual(4); + + note = score.tracks[0].staves[0].bars[9].voices[0].beats[1].notes[0]; + expect(note.bendType).toEqual(BendType.Release); + expect(note.isContinuedBend).toBe(true); + expect(note.bendPoints.length).toEqual(2); + expect(note.bendPoints[0].offset).toEqual(0); + expect(note.bendPoints[0].value).toEqual(4); + expect(note.bendPoints[1].offset).toEqual(15); + expect(note.bendPoints[1].value).toEqual(0); + + note = score.tracks[0].staves[0].bars[9].voices[0].beats[2].notes[0]; + expect(note.bendType).toEqual(BendType.Bend); + expect(note.isContinuedBend).toBe(false); + expect(note.bendPoints.length).toEqual(2); + expect(note.bendPoints[0].offset).toEqual(0); + expect(note.bendPoints[0].value).toEqual(0); + expect(note.bendPoints[1].offset).toEqual(15); + expect(note.bendPoints[1].value).toEqual(4); + + // // Bar 11 + note = score.tracks[0].staves[0].bars[10].voices[0].beats[0].notes[0]; + expect(note.bendType).toEqual(BendType.Bend); + expect(note.bendPoints.length).toEqual(2); + expect(note.bendPoints[0].offset).toEqual(0); + expect(note.bendPoints[0].value).toEqual(0); + expect(note.bendPoints[1].offset).toEqual(15); + expect(note.bendPoints[1].value).toEqual(4); + + note = score.tracks[0].staves[0].bars[10].voices[0].beats[1].notes[0]; + expect(note.bendType).toEqual(BendType.Bend); + expect(note.isContinuedBend).toBe(true); + expect(note.bendPoints.length).toEqual(2); + expect(note.bendPoints[0].offset).toEqual(0); + expect(note.bendPoints[0].value).toEqual(4); + expect(note.bendPoints[1].offset).toEqual(15); + expect(note.bendPoints[1].value).toEqual(8); + + note = score.tracks[0].staves[0].bars[10].voices[0].beats[2].notes[0]; + expect(note.bendType).toEqual(BendType.Release); + expect(note.isContinuedBend).toBe(true); + expect(note.bendPoints.length).toEqual(2); + expect(note.bendPoints[0].offset).toEqual(0); + expect(note.bendPoints[0].value).toEqual(8); + expect(note.bendPoints[1].offset).toEqual(15); + expect(note.bendPoints[1].value).toEqual(4); + + note = score.tracks[0].staves[0].bars[10].voices[0].beats[3].notes[0]; + expect(note.bendType).toEqual(BendType.Release); + expect(note.isContinuedBend).toBe(true); + expect(note.bendPoints.length).toEqual(2); + expect(note.bendPoints[0].offset).toEqual(0); + expect(note.bendPoints[0].value).toEqual(4); + expect(note.bendPoints[1].offset).toEqual(15); + expect(note.bendPoints[1].value).toEqual(0); + + // Grace Bends + + // // Bar 12 + note = score.tracks[0].staves[0].bars[11].voices[0].beats[0].notes[0]; + expect(note.beat.graceType).toEqual(GraceType.BeforeBeat); + expect(note.bendType).toEqual(BendType.Bend); + expect(note.bendPoints.length).toEqual(2); + expect(note.bendPoints[0].offset).toEqual(0); + expect(note.bendPoints[0].value).toEqual(0); + expect(note.bendPoints[1].offset).toEqual(15); + expect(note.bendPoints[1].value).toEqual(4); + + // // Bar 13 + note = score.tracks[0].staves[0].bars[12].voices[0].beats[0].notes[0]; + expect(note.beat.graceType).toEqual(GraceType.BeforeBeat); + expect(note.bendType).toEqual(BendType.Bend); + expect(note.bendPoints.length).toEqual(2); + expect(note.bendPoints[0].offset).toEqual(0); + expect(note.bendPoints[0].value).toEqual(0); + expect(note.bendPoints[1].offset).toEqual(15); + expect(note.bendPoints[1].value).toEqual(4); + + note = score.tracks[0].staves[0].bars[12].voices[0].beats[1].notes[0]; + expect(note.isContinuedBend).toBe(true); + expect(note.bendType).toEqual(BendType.Hold); + expect(note.bendPoints.length).toEqual(2); + expect(note.bendPoints[0].offset).toEqual(0); + expect(note.bendPoints[0].value).toEqual(4); + expect(note.bendPoints[1].offset).toEqual(60); + expect(note.bendPoints[1].value).toEqual(4); + + // // Bar 14 + note = score.tracks[0].staves[0].bars[13].voices[0].beats[0].notes[0]; + expect(note.beat.graceType).toEqual(GraceType.OnBeat); + expect(note.bendType).toEqual(BendType.Bend); + expect(note.bendPoints.length).toEqual(2); + expect(note.bendPoints[0].offset).toEqual(0); + expect(note.bendPoints[0].value).toEqual(0); + expect(note.bendPoints[1].offset).toEqual(18); + expect(note.bendPoints[1].value).toEqual(1); + + note = score.tracks[0].staves[0].bars[13].voices[0].beats[1].notes[0]; + expect(note.isContinuedBend).toBe(true); + expect(note.bendType).toEqual(BendType.Hold); + expect(note.bendPoints.length).toEqual(2); + expect(note.bendPoints[0].offset).toEqual(0); + expect(note.bendPoints[0].value).toEqual(1); + expect(note.bendPoints[1].offset).toEqual(60); + expect(note.bendPoints[1].value).toEqual(1); + + // // Bar 15 + note = score.tracks[0].staves[0].bars[14].voices[0].beats[0].notes[0]; + expect(note.beat.graceType).toEqual(GraceType.BeforeBeat); + expect(note.bendType).toEqual(BendType.Bend); + expect(note.bendPoints.length).toEqual(2); + expect(note.bendPoints[0].offset).toEqual(0); + expect(note.bendPoints[0].value).toEqual(0); + expect(note.bendPoints[1].offset).toEqual(15); + expect(note.bendPoints[1].value).toEqual(4); + + note = score.tracks[0].staves[0].bars[14].voices[0].beats[1].notes[0]; + expect(note.fret).toEqual(12); + expect(note.isTieDestination).toBe(true); + expect(note.isContinuedBend).toBe(true); + expect(note.bendType).toEqual(BendType.Hold); + expect(note.bendPoints.length).toEqual(2); + expect(note.bendPoints[0].offset).toEqual(0); + expect(note.bendPoints[0].value).toEqual(4); + expect(note.bendPoints[1].offset).toEqual(60); + expect(note.bendPoints[1].value).toEqual(4); + + note = score.tracks[0].staves[0].bars[14].voices[0].beats[1].notes[1]; + expect(note.fret).toEqual(10); + expect(note.isContinuedBend).toBe(false); + expect(note.hasBend).toBe(false); + expect(note.bendType).toEqual(BendType.None); + note = score.tracks[0].staves[0].bars[15].voices[0].beats[0].notes[0]; + expect(note.fret).toEqual(10); + expect(note.bendType).toEqual(BendType.None); + + // // Bar 16 + note = score.tracks[0].staves[0].bars[15].voices[0].beats[0].notes[1]; + expect(note.bendType).toEqual(BendType.Bend); + expect(note.bendPoints.length).toEqual(2); + expect(note.bendPoints[0].offset).toEqual(0); + expect(note.bendPoints[0].value).toEqual(0); + expect(note.bendPoints[1].offset).toEqual(15); + expect(note.bendPoints[1].value).toEqual(4); + }); + + it('whammy-advanced', async () => { + const reader = await prepareGp7ImporterWithFile('guitarpro7/whammy-advanced.gp'); + let score: Score = reader.readScore(); + + // Bar 1 + let beat: Beat = score.tracks[0].staves[0].bars[0].voices[0].beats[0]; + expect(beat.whammyBarType).toEqual(WhammyType.Dive); + expect(beat.whammyBarPoints.length).toEqual(2); + expect(beat.whammyBarPoints[0].offset).toEqual(0); + expect(beat.whammyBarPoints[0].value).toEqual(0); + expect(beat.whammyBarPoints[1].offset).toEqual(45); + expect(beat.whammyBarPoints[1].value).toEqual(-4); + + beat = score.tracks[0].staves[0].bars[0].voices[0].beats[2]; + expect(beat.whammyBarType).toEqual(WhammyType.PrediveDive); + expect(beat.whammyBarPoints.length).toEqual(2); + expect(beat.whammyBarPoints[0].offset).toEqual(0); + expect(beat.whammyBarPoints[0].value).toEqual(-4); + expect(beat.whammyBarPoints[1].offset).toEqual(60); + expect(beat.whammyBarPoints[1].value).toEqual(-16); + + // Bar 2 + beat = score.tracks[0].staves[0].bars[1].voices[0].beats[0]; + expect(beat.whammyBarType).toEqual(WhammyType.Dip); + expect(beat.whammyBarPoints.length).toEqual(3); + expect(beat.whammyBarPoints[0].offset).toEqual(0); + expect(beat.whammyBarPoints[0].value).toEqual(0); + expect(beat.whammyBarPoints[1].offset).toEqual(15); + expect(beat.whammyBarPoints[1].value).toEqual(-16); + expect(beat.whammyBarPoints[2].offset).toEqual(30); + expect(beat.whammyBarPoints[2].value).toEqual(0); + + beat = score.tracks[0].staves[0].bars[1].voices[0].beats[2]; + expect(beat.whammyBarType).toEqual(WhammyType.Dip); + expect(beat.whammyBarPoints.length).toEqual(4); + expect(beat.whammyBarPoints[0].offset).toEqual(0); + expect(beat.whammyBarPoints[0].value).toEqual(0); + expect(beat.whammyBarPoints[1].offset).toEqual(14); + expect(beat.whammyBarPoints[1].value).toEqual(-12); + expect(beat.whammyBarPoints[2].offset).toEqual(31); + expect(beat.whammyBarPoints[2].value).toEqual(-12); + expect(beat.whammyBarPoints[3].offset).toEqual(53); + expect(beat.whammyBarPoints[3].value).toEqual(0); + + // Bar 3 + beat = score.tracks[0].staves[0].bars[2].voices[0].beats[0]; + expect(beat.whammyBarType).toEqual(WhammyType.Dip); + expect(beat.whammyBarPoints.length).toEqual(3); + expect(beat.whammyBarPoints[0].offset).toEqual(0); + expect(beat.whammyBarPoints[0].value).toEqual(0); + expect(beat.whammyBarPoints[1].offset).toEqual(15); + expect(beat.whammyBarPoints[1].value).toEqual(-16); + expect(beat.whammyBarPoints[2].offset).toEqual(30); + expect(beat.whammyBarPoints[2].value).toEqual(0); + + beat = score.tracks[0].staves[0].bars[2].voices[0].beats[2]; + expect(beat.whammyBarType).toEqual(WhammyType.Dip); + expect(beat.whammyBarPoints.length).toEqual(4); + expect(beat.whammyBarPoints[0].offset).toEqual(0); + expect(beat.whammyBarPoints[0].value).toEqual(0); + expect(beat.whammyBarPoints[1].offset).toEqual(14); + expect(beat.whammyBarPoints[1].value).toEqual(-12); + expect(beat.whammyBarPoints[2].offset).toEqual(31); + expect(beat.whammyBarPoints[2].value).toEqual(-12); + expect(beat.whammyBarPoints[3].offset).toEqual(53); + expect(beat.whammyBarPoints[3].value).toEqual(0); + + // Bar 4 + beat = score.tracks[0].staves[0].bars[3].voices[0].beats[0]; + expect(beat.whammyBarType).toEqual(WhammyType.Predive); + expect(beat.whammyBarPoints.length).toEqual(2); + expect(beat.whammyBarPoints[0].offset).toEqual(0); + expect(beat.whammyBarPoints[0].value).toEqual(-8); + expect(beat.whammyBarPoints[1].offset).toEqual(60); + expect(beat.whammyBarPoints[1].value).toEqual(-8); + + // Bar 5 + beat = score.tracks[0].staves[0].bars[4].voices[0].beats[0]; + expect(beat.whammyBarType).toEqual(WhammyType.PrediveDive); + expect(beat.whammyBarPoints.length).toEqual(2); + expect(beat.whammyBarPoints[0].offset).toEqual(0); + expect(beat.whammyBarPoints[0].value).toEqual(-4); + expect(beat.whammyBarPoints[1].offset).toEqual(30); + expect(beat.whammyBarPoints[1].value).toEqual(0); + + // Bar 6 + beat = score.tracks[0].staves[0].bars[5].voices[0].beats[0]; + expect(beat.whammyBarType).toEqual(WhammyType.PrediveDive); + expect(beat.whammyBarPoints.length).toEqual(2); + expect(beat.whammyBarPoints[0].offset).toEqual(0); + expect(beat.whammyBarPoints[0].value).toEqual(-4); + expect(beat.whammyBarPoints[1].offset).toEqual(29); + expect(beat.whammyBarPoints[1].value).toEqual(-12); + + beat = score.tracks[0].staves[0].bars[5].voices[0].beats[1]; + expect(beat.whammyBarType).toEqual(WhammyType.Dive); + expect(beat.whammyBarPoints.length).toEqual(2); + expect(beat.whammyBarPoints[0].offset).toEqual(0); + expect(beat.whammyBarPoints[0].value).toEqual(-12); + expect(beat.whammyBarPoints[1].offset).toEqual(45); + expect(beat.whammyBarPoints[1].value).toEqual(0); + + // Bar 7 + beat = score.tracks[0].staves[0].bars[6].voices[0].beats[0]; + expect(beat.whammyBarType).toEqual(WhammyType.Dive); + expect(beat.whammyBarPoints.length).toEqual(2); + expect(beat.whammyBarPoints[0].offset).toEqual(0); + expect(beat.whammyBarPoints[0].value).toEqual(0); + expect(beat.whammyBarPoints[1].offset).toEqual(45); + expect(beat.whammyBarPoints[1].value).toEqual(-4); + + beat = score.tracks[0].staves[0].bars[6].voices[0].beats[1]; + expect(beat.whammyBarType).toEqual(WhammyType.Hold); + expect(beat.whammyBarPoints.length).toEqual(2); + expect(beat.whammyBarPoints[0].offset).toEqual(0); + expect(beat.whammyBarPoints[0].value).toEqual(-4); + expect(beat.whammyBarPoints[1].offset).toEqual(60); + expect(beat.whammyBarPoints[1].value).toEqual(-4); + + // Bar 8 + beat = score.tracks[0].staves[0].bars[7].voices[0].beats[0]; + expect(beat.whammyBarType).toEqual(WhammyType.Dive); + expect(beat.whammyBarPoints.length).toEqual(2); + expect(beat.whammyBarPoints[0].offset).toEqual(0); + expect(beat.whammyBarPoints[0].value).toEqual(-4); + expect(beat.whammyBarPoints[1].offset).toEqual(46); + expect(beat.whammyBarPoints[1].value).toEqual(-12); + + beat = score.tracks[0].staves[0].bars[7].voices[0].beats[1]; + expect(beat.whammyBarType).toEqual(WhammyType.Dive); + expect(beat.whammyBarPoints.length).toEqual(2); + expect(beat.whammyBarPoints[0].offset).toEqual(0); + expect(beat.whammyBarPoints[0].value).toEqual(-12); + expect(beat.whammyBarPoints[1].offset).toEqual(44); + expect(beat.whammyBarPoints[1].value).toEqual(8); + + // Bar 9 + beat = score.tracks[0].staves[0].bars[8].voices[0].beats[0]; + expect(beat.whammyBarType).toEqual(WhammyType.Dip); + expect(beat.whammyBarPoints.length).toEqual(3); + expect(beat.whammyBarPoints[0].offset).toEqual(0); + expect(beat.whammyBarPoints[0].value).toEqual(8); + expect(beat.whammyBarPoints[1].offset).toEqual(15); + expect(beat.whammyBarPoints[1].value).toEqual(12); + expect(beat.whammyBarPoints[2].offset).toEqual(30); + expect(beat.whammyBarPoints[2].value).toEqual(0); + + beat = score.tracks[0].staves[0].bars[8].voices[0].beats[1]; + expect(beat.whammyBarType).toEqual(WhammyType.Dip); + expect(beat.whammyBarPoints.length).toEqual(3); + expect(beat.whammyBarPoints[0].offset).toEqual(0); + expect(beat.whammyBarPoints[0].value).toEqual(0); + expect(beat.whammyBarPoints[1].offset).toEqual(15); + expect(beat.whammyBarPoints[1].value).toEqual(-4); + expect(beat.whammyBarPoints[2].offset).toEqual(30); + expect(beat.whammyBarPoints[2].value).toEqual(0); + }); + + it('tremolo', async () => { + const reader = await prepareGp7ImporterWithFile('guitarpro7/tremolo.gp'); + let score: Score = reader.readScore(); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints.length).toEqual(3); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints[0].offset).toEqual(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints[0].value).toEqual(0); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints[1].offset).toEqual(30); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints[1].value).toEqual(-4); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints[2].offset).toEqual(60); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints[2].value).toEqual(0); + + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints.length).toEqual(2); + + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints[0].offset).toEqual(0); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints[0].value).toEqual(-4); + + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints[1].offset).toEqual(60); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints[1].value).toEqual(0); + + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints.length).toEqual(4); + + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints[0].offset).toEqual(0); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints[0].value).toEqual(0); + + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints[1].offset).toEqual(30); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints[1].value).toEqual(-4); + + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints[2].offset).toEqual(30); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints[2].value).toEqual(-4); + + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints[3].offset).toEqual(60); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints[3].value).toEqual(-4); + + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints.length).toEqual(4); + + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints[0].offset).toEqual(0); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints[0].value).toEqual(-4); + + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints[1].offset).toEqual(15); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints[1].value).toEqual(-12); + + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints[2].offset).toEqual(30); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints[2].value).toEqual(-12); + + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints[3].offset).toEqual(45); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints[3].value).toEqual(0); + }); + + it('slides', async () => { + const reader = await prepareGp7ImporterWithFile('guitarpro7/slides.gp'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkSlides(score); + }); + + it('vibrato', async () => { + const reader = await prepareGp7ImporterWithFile('guitarpro7/vibrato.gp'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkVibrato(score, true); + }); + + it('trills', async () => { + const reader = await prepareGp7ImporterWithFile('guitarpro7/trills.gp'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkTrills(score); + }); + + it('other-effects', async () => { + const reader = await prepareGp7ImporterWithFile('guitarpro7/other-effects.gp'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkOtherEffects(score, true); + }); + + it('fingering', async () => { + const reader = await prepareGp7ImporterWithFile('guitarpro7/fingering.gp'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkFingering(score); + }); + + it('stroke', async () => { + const reader = await prepareGp7ImporterWithFile('guitarpro7/strokes.gp'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkStroke(score); + }); + + it('tuplets', async () => { + const reader = await prepareGp7ImporterWithFile('guitarpro7/tuplets.gp'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkTuplets(score); + }); + + it('ranges', async () => { + const reader = await prepareGp7ImporterWithFile('guitarpro7/ranges.gp'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkRanges(score); + }); + + it('effects', async () => { + const reader = await prepareGp7ImporterWithFile('guitarpro7/effects.gp'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkEffects(score); + }); + + it('serenade', async () => { + const reader = await prepareGp7ImporterWithFile('guitarpro7/serenade.gp'); + reader.readScore(); + // only Check reading + }); + + it('strings', async () => { + const reader = await prepareGp7ImporterWithFile('guitarpro7/strings.gp'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkStrings(score); + }); + + it('key-signatures', async () => { + const reader = await prepareGp7ImporterWithFile('guitarpro7/key-signatures.gp'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkKeySignatures(score); + }); + + it('chords', async () => { + const reader = await prepareGp7ImporterWithFile('guitarpro7/chords.gp'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkChords(score); + }); + + it('colors', async () => { + const reader = await prepareGp7ImporterWithFile('guitarpro7/colors.gp'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkColors(score); + }); + + it('tremolo-vibrato', async () => { + const reader = await prepareGp7ImporterWithFile('guitarpro7/tremolo-vibrato.gp'); + let score: Score = reader.readScore(); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].vibrato).toEqual(VibratoType.Slight); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].vibrato).toEqual(VibratoType.Wide); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[1].vibrato).toEqual(VibratoType.Slight); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].vibrato).toEqual(VibratoType.Slight); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].vibrato).toEqual(VibratoType.Wide); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].vibrato).toEqual(VibratoType.Wide); + }); + + it('ottavia', async () => { + const reader = await prepareGp7ImporterWithFile('guitarpro7/ottavia.gp'); + let score: Score = reader.readScore(); + expect(score.tracks[0].staves[0].bars[0].clefOttava).toEqual(Ottavia._8va); + expect(score.tracks[0].staves[0].bars[1].clefOttava).toEqual(Ottavia._8vb); + expect(score.tracks[0].staves[0].bars[2].clefOttava).toEqual(Ottavia._15ma); + expect(score.tracks[0].staves[0].bars[3].clefOttava).toEqual(Ottavia._15mb); + expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].ottava).toEqual(Ottavia._8va); + expect(score.tracks[0].staves[0].bars[4].voices[0].beats[1].ottava).toEqual(Ottavia._8vb); + expect(score.tracks[0].staves[0].bars[4].voices[0].beats[2].ottava).toEqual(Ottavia._15ma); + expect(score.tracks[0].staves[0].bars[4].voices[0].beats[3].ottava).toEqual(Ottavia._15mb); + }); + + it('simile-mark', async () => { + const reader = await prepareGp7ImporterWithFile('guitarpro7/simile-mark.gp'); + let score: Score = reader.readScore(); + expect(score.tracks[0].staves[0].bars[0].simileMark).toEqual(SimileMark.None); + expect(score.tracks[0].staves[0].bars[1].simileMark).toEqual(SimileMark.Simple); + expect(score.tracks[0].staves[0].bars[2].simileMark).toEqual(SimileMark.None); + expect(score.tracks[0].staves[0].bars[3].simileMark).toEqual(SimileMark.None); + expect(score.tracks[0].staves[0].bars[4].simileMark).toEqual(SimileMark.FirstOfDouble); + expect(score.tracks[0].staves[0].bars[5].simileMark).toEqual(SimileMark.SecondOfDouble); + }); + + it('anacrusis', async () => { + const reader = await prepareGp7ImporterWithFile('guitarpro7/anacrusis.gp'); + let score: Score = reader.readScore(); + expect(score.masterBars[0].isAnacrusis).toBe(true); + expect(score.masterBars[0].calculateDuration()).toEqual(1920); + expect(score.masterBars[1].calculateDuration()).toEqual(3840); + }); + + it('left-hand-tap', async () => { + const reader = await prepareGp7ImporterWithFile('guitarpro7/left-hand-tap.gp'); + let score: Score = reader.readScore(); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].isLeftHandTapped).toBe(true); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].notes[0].isLeftHandTapped).toBe(true); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[3].notes[0].isLeftHandTapped).toBe(true); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[6].notes[0].isLeftHandTapped).toBe(true); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[9].notes[0].isLeftHandTapped).toBe(true); + }); + + it('fermata', async () => { + const reader = await prepareGp7ImporterWithFile('guitarpro7/fermata.gp'); + let score: Score = reader.readScore(); + expect(score.masterBars[0].fermata.size).toEqual(5); + expect(score.masterBars[1].fermata.size).toEqual(5); + expect(score.masterBars[2].fermata.size).toEqual(5); // Short + let offsets = [ + 0, + (MidiUtils.QuarterTime * (1 / 2)) | 0, + (MidiUtils.QuarterTime * (1 / 1)) | 0, + (MidiUtils.QuarterTime * (2 / 1)) | 0, + (MidiUtils.QuarterTime * (3 / 1)) | 0 + ]; + let types: FermataType[] = [FermataType.Short, FermataType.Medium, FermataType.Long]; + for (let i: number = 0; i < 3; i++) { + let masterBar: MasterBar = score.masterBars[i]; + expect(masterBar.fermata.size).toEqual(5); + for (let offset of offsets) { + let fermata = masterBar.fermata.get(offset); + expect(fermata).toBeTruthy(); + expect(fermata!.type).toEqual(types[i]); + } + let beats: Beat[] = score.tracks[0].staves[0].bars[i].voices[0].beats; + for (let beat of beats) { + let fermata = masterBar.fermata.get(beat.playbackStart); + let beatFermata = beat.fermata; + expect(beatFermata).toBeTruthy(); + expect(fermata).toBeTruthy(); + expect(beatFermata!.type).toEqual(types[i]); + expect(fermata!.type).toEqual(types[i]); + } + } + }); + + it('pick-slide', async () => { + const reader = await prepareGp7ImporterWithFile('guitarpro7/pick-slide.gp'); + let score: Score = reader.readScore(); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].slideOutType).toEqual( + SlideOutType.PickSlideUp + ); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].fret).toEqual(10); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].fret).toEqual(10); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].slideOutType).toEqual( + SlideOutType.PickSlideDown + ); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].fret).toEqual(10); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].fret).toEqual(0); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].slideOutType).toEqual( + SlideOutType.PickSlideUp + ); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].fret).toEqual(0); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].notes[0].fret).toEqual(10); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].notes[0].slideOutType).toEqual( + SlideOutType.PickSlideDown + ); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].notes[0].fret).toEqual(10); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].notes[0].fret).toEqual(5); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].notes[0].slideOutType).toEqual( + SlideOutType.PickSlideDown + ); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].notes[0].fret).toEqual(20); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[1].notes[0].slideOutType).toEqual( + SlideOutType.PickSlideDown + ); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[1].notes[0].fret).toEqual(12); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[2].notes[0].slideOutType).toEqual( + SlideOutType.PickSlideDown + ); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[2].notes[0].fret).toEqual(5); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[3].notes[0].slideOutType).toEqual( + SlideOutType.PickSlideDown + ); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[3].notes[0].fret).toEqual(0); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].notes[0].slideOutType).toEqual( + SlideOutType.PickSlideDown + ); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].notes[0].fret).toEqual(20); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[1].notes[0].slideOutType).toEqual( + SlideOutType.PickSlideDown + ); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[1].notes[0].fret).toEqual(12); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[2].notes[0].slideOutType).toEqual( + SlideOutType.PickSlideUp + ); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[2].notes[0].fret).toEqual(5); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[3].notes[0].slideOutType).toEqual( + SlideOutType.PickSlideUp + ); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[3].notes[0].fret).toEqual(10); + expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].notes[0].slideOutType).toEqual( + SlideOutType.PickSlideDown + ); + expect(score.tracks[0].staves[0].bars[4].voices[0].beats[0].notes[0].fret).toEqual(20); + }); +}); diff --git a/test/importer/GpImporterTestHelper.ts b/test/importer/GpImporterTestHelper.ts new file mode 100644 index 000000000..7c95f94da --- /dev/null +++ b/test/importer/GpImporterTestHelper.ts @@ -0,0 +1,527 @@ +import { Gp3To5Importer } from '@src/importer/Gp3To5Importer'; +import { ByteBuffer } from '@src/io/ByteBuffer'; +import { AccentuationType } from '@src/model/AccentuationType'; +import { AutomationType } from '@src/model/Automation'; +import { BrushType } from '@src/model/BrushType'; +import { Chord } from '@src/model/Chord'; +import { Duration } from '@src/model/Duration'; +import { Fingers } from '@src/model/Fingers'; +import { GraceType } from '@src/model/GraceType'; +import { HarmonicType } from '@src/model/HarmonicType'; +import { KeySignature } from '@src/model/KeySignature'; +import { KeySignatureType } from '@src/model/KeySignatureType'; +import { PickStroke } from '@src/model/PickStroke'; +import { Score } from '@src/model/Score'; +import { SlideInType } from '@src/model/SlideInType'; +import { SlideOutType } from '@src/model/SlideOutType'; +import { Staff } from '@src/model/Staff'; +import { Track } from '@src/model/Track'; +import { VibratoType } from '@src/model/VibratoType'; +import { Settings } from '@src/Settings'; +import { TestPlatform } from '@test/TestPlatform'; + +export class GpImporterTestHelper { + public static async prepareImporterWithFile(name: string): Promise { + let path: string = 'test-data/'; + const buffer = await TestPlatform.loadFile(path + name); + return GpImporterTestHelper.prepareImporterWithBytes(buffer); + } + + public static prepareImporterWithBytes(buffer: Uint8Array): Gp3To5Importer { + let readerBase: Gp3To5Importer = new Gp3To5Importer(); + readerBase.init(ByteBuffer.fromBuffer(buffer), new Settings()); + return readerBase; + } + + public static checkNotes(score: Score): void { + // Whole Notes + let beat: number = 0; + let durationsInFile: Duration[] = [ + Duration.Whole, + Duration.Half, + Duration.Quarter, + Duration.Eighth, + Duration.Sixteenth, + Duration.ThirtySecond, + Duration.SixtyFourth + ]; + for (let duration of durationsInFile) { + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[beat].notes[0].fret).toEqual(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[beat].notes[0].string).toEqual(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[beat].duration).toEqual(duration); + beat++; + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[beat].notes[0].fret).toEqual(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[beat].notes[0].string).toEqual(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[beat].duration).toEqual(duration); + beat++; + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[beat].notes[0].fret).toEqual(3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[beat].notes[0].string).toEqual(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[beat].duration).toEqual(duration); + beat++; + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[beat].notes[0].fret).toEqual(4); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[beat].notes[0].string).toEqual(1); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[beat].duration).toEqual(duration); + beat++; + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[beat].isRest).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[beat].duration).toEqual(duration); + beat++; + } + } + + public static checkTimeSignatures(score: Score): void { + expect(score.masterBars[0].timeSignatureNumerator).toEqual(4); + expect(score.masterBars[0].timeSignatureDenominator).toEqual(4); + + expect(score.masterBars[1].timeSignatureNumerator).toEqual(3); + expect(score.masterBars[1].timeSignatureDenominator).toEqual(4); + + expect(score.masterBars[2].timeSignatureNumerator).toEqual(2); + expect(score.masterBars[2].timeSignatureDenominator).toEqual(4); + + expect(score.masterBars[3].timeSignatureNumerator).toEqual(1); + expect(score.masterBars[3].timeSignatureDenominator).toEqual(4); + + expect(score.masterBars[4].timeSignatureNumerator).toEqual(20); + expect(score.masterBars[4].timeSignatureDenominator).toEqual(32); + } + + public static checkDead(score: Score): void { + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isDead).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].string).toEqual(1); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].isDead).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].string).toEqual(2); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].isDead).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].string).toEqual(3); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].isDead).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].string).toEqual(4); + } + + public static checkGrace(score: Score): void { + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].graceType).toEqual(GraceType.BeforeBeat); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].fret).toEqual(3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].duration).toEqual(Duration.Eighth); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].fret).toEqual(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].duration).toEqual(Duration.Quarter); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].graceType).toEqual(GraceType.BeforeBeat); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].fret).toEqual(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].duration).toEqual(Duration.Eighth); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].fret).toEqual(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].duration).toEqual(Duration.Quarter); + } + + public static checkAccentuations(score: Score, includeHeavy: boolean): void { + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isGhost).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].accentuated).toEqual( + AccentuationType.Normal + ); + if (includeHeavy) { + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].accentuated).toEqual( + AccentuationType.Heavy + ); + } + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].isLetRing).toBe(true); + } + + public static checkHarmonics(score: Score): void { + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].harmonicType).toEqual( + HarmonicType.Natural + ); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].harmonicType).toEqual( + HarmonicType.Artificial + ); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].harmonicType).toEqual(HarmonicType.Tap); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].harmonicType).toEqual(HarmonicType.Semi); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[4].notes[0].harmonicType).toEqual(HarmonicType.Pinch); + // TODO: Harmonic Values + } + + public static checkHammer(score: Score): void { + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isHammerPullOrigin).toEqual(false); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[1].isHammerPullOrigin).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[2].isHammerPullOrigin).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[3].isHammerPullOrigin).toBe(true); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].hammerPullOrigin).toBeFalsy(); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[1].hammerPullOrigin).toBeTruthy(); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[2].hammerPullOrigin).toBeTruthy(); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[3].hammerPullOrigin).toBeTruthy(); + + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].isHammerPullOrigin).toBe(true); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].notes[0].isHammerPullOrigin).toBe(true); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].notes[0].hammerPullOrigin).toBeTruthy(); + } + + public static checkBend(score: Score): void { + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints.length).toEqual(3); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints[0].offset).toEqual(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints[0].value).toEqual(0); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints[1].offset).toEqual(15); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints[1].value).toEqual(4); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints[2].offset).toEqual(60); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints[2].value).toEqual(4); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints.length).toEqual(7); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints[0].offset).toEqual(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints[0].value).toEqual(0); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints[1].offset).toEqual(10); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints[1].value).toEqual(4); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints[2].offset).toEqual(20); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints[2].value).toEqual(4); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints[3].offset).toEqual(30); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints[3].value).toEqual(0); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints[4].offset).toEqual(40); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints[4].value).toEqual(0); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints[5].offset).toEqual(50); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints[5].value).toEqual(4); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints[6].offset).toEqual(60); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints[6].value).toEqual(4); + } + + public static checkTremolo(score: Score): void { + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints.length).toEqual(3); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints[0].offset).toEqual(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints[0].value).toEqual(0); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints[1].offset).toEqual(30); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints[1].value).toEqual(-4); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints[2].offset).toEqual(60); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints[2].value).toEqual(0); + + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints.length).toEqual(3); + + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints[0].offset).toEqual(0); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints[0].value).toEqual(-4); + + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints[1].offset).toEqual(45); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints[1].value).toEqual(-4); + + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints[2].offset).toEqual(60); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints[2].value).toEqual(0); + + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints.length).toEqual(3); + + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints[0].offset).toEqual(0); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints[0].value).toEqual(0); + + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints[1].offset).toEqual(45); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints[1].value).toEqual(-4); + + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints[2].offset).toEqual(60); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints[2].value).toEqual(-4); + } + + public static checkSlides(score: Score): void { + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].getNoteOnString(5)!.slideOutType).toEqual( + SlideOutType.Legato + ); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].getNoteOnString(2)!.slideOutType).toEqual( + SlideOutType.Shift + ); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].getNoteOnString(5)!.slideInType).toEqual( + SlideInType.IntoFromBelow + ); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].getNoteOnString(5)!.slideInType).toEqual( + SlideInType.IntoFromAbove + ); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].getNoteOnString(5)!.slideOutType).toEqual( + SlideOutType.OutDown + ); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].getNoteOnString(5)!.slideOutType).toEqual( + SlideOutType.OutUp + ); + } + + public static checkStrings(score: Score): void { + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes.length).toEqual(6); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].getNoteOnString(1)!.fret).toEqual(6); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].getNoteOnString(2)!.fret).toEqual(5); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].getNoteOnString(3)!.fret).toEqual(4); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].getNoteOnString(4)!.fret).toEqual(3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].getNoteOnString(5)!.fret).toEqual(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].getNoteOnString(6)!.fret).toEqual(1); + } + + public static checkVibrato(score: Score, checkNotes: boolean): void { + if (checkNotes) { + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].vibrato).toEqual(VibratoType.Slight); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].vibrato).toEqual(VibratoType.Slight); + } + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].vibrato).toEqual(VibratoType.Slight); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].vibrato).toEqual(VibratoType.Slight); + } + + public static checkTrills(score: Score): void { + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].trillFret).toEqual(2); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].trillSpeed).toEqual(Duration.Sixteenth); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].isTremolo).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].tremoloSpeed).toEqual(Duration.ThirtySecond); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].isTremolo).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].tremoloSpeed).toEqual(Duration.Sixteenth); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].isTremolo).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].tremoloSpeed).toEqual(Duration.Eighth); + } + + public static checkOtherEffects(score: Score, skipInstrumentCheck: boolean = false): void { + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isPalmMute).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].isStaccato).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].tap).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].slap).toBe(true); + + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].pop).toBe(true); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].fadeIn).toBe(true); + + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].hasChord).toBe(true); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].chord!.name).toEqual('C'); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[1].text).toEqual('Text'); + + expect(score.masterBars[4].isDoubleBar).toBe(true); + expect(score.masterBars[4].tempoAutomation).toBeTruthy(); + expect(score.masterBars[4].tempoAutomation!.value).toEqual(120.0); + if (!skipInstrumentCheck) { + expect( + score.tracks[0].staves[0].bars[4].voices[0].beats[0].getAutomation(AutomationType.Instrument) + ).toBeTruthy(); + expect( + score.tracks[0].staves[0].bars[4].voices[0].beats[0].getAutomation(AutomationType.Instrument)!.value + ).toEqual(25); + } + } + + public static checkFingering(score: Score): void { + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isFingering).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].leftHandFinger).toEqual(Fingers.Thumb); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].leftHandFinger).toEqual( + Fingers.IndexFinger + ); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].leftHandFinger).toEqual( + Fingers.MiddleFinger + ); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].leftHandFinger).toEqual( + Fingers.AnnularFinger + ); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[4].notes[0].leftHandFinger).toEqual( + Fingers.LittleFinger + ); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[5].notes[0].rightHandFinger).toEqual(Fingers.Thumb); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[6].notes[0].rightHandFinger).toEqual( + Fingers.IndexFinger + ); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[7].notes[0].rightHandFinger).toEqual( + Fingers.MiddleFinger + ); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[8].notes[0].rightHandFinger).toEqual( + Fingers.AnnularFinger + ); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[9].notes[0].rightHandFinger).toEqual( + Fingers.LittleFinger + ); + } + + public static checkStroke(score: Score): void { + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].brushType).toEqual(BrushType.BrushDown); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].brushType).toEqual(BrushType.BrushUp); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].pickStroke).toEqual(PickStroke.Up); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].pickStroke).toEqual(PickStroke.Down); + } + + public static checkTuplets(score: Score): void { + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].tupletNumerator).toEqual(3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].tupletNumerator).toEqual(3); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].tupletNumerator).toEqual(3); + + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].tupletNumerator).toEqual(5); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].tupletNumerator).toEqual(5); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].tupletNumerator).toEqual(5); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].tupletNumerator).toEqual(5); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[4].tupletNumerator).toEqual(5); + } + + public static checkRanges(score: Score): void { + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].isPalmMute).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].isPalmMute).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].notes[0].isPalmMute).toBe(true); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[3].notes[0].isPalmMute).toBe(true); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].isPalmMute).toBe(true); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].isPalmMute).toBe(true); + + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].notes[0].isLetRing).toBe(true); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].notes[0].isLetRing).toBe(true); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].notes[0].isLetRing).toBe(true); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].notes[0].isLetRing).toBe(true); + } + + public static checkEffects(score: Score): void { + // just check if reading works + expect(true).toBe(true); + } + + public static checkKeySignatures(score: Score): void { + // major - flats + expect(score.masterBars[0].keySignature).toEqual(KeySignature.C); + expect(score.masterBars[0].keySignatureType).toEqual(KeySignatureType.Major); + expect(score.masterBars[1].keySignature).toEqual(KeySignature.F); + expect(score.masterBars[1].keySignatureType).toEqual(KeySignatureType.Major); + expect(score.masterBars[2].keySignature).toEqual(KeySignature.Bb); + expect(score.masterBars[2].keySignatureType).toEqual(KeySignatureType.Major); + expect(score.masterBars[3].keySignature).toEqual(KeySignature.Eb); + expect(score.masterBars[3].keySignatureType).toEqual(KeySignatureType.Major); + expect(score.masterBars[4].keySignature).toEqual(KeySignature.Ab); + expect(score.masterBars[4].keySignatureType).toEqual(KeySignatureType.Major); + expect(score.masterBars[5].keySignature).toEqual(KeySignature.Db); + expect(score.masterBars[5].keySignatureType).toEqual(KeySignatureType.Major); + expect(score.masterBars[6].keySignature).toEqual(KeySignature.Gb); + expect(score.masterBars[6].keySignatureType).toEqual(KeySignatureType.Major); + expect(score.masterBars[7].keySignature).toEqual(KeySignature.Cb); + expect(score.masterBars[7].keySignatureType).toEqual(KeySignatureType.Major); + + // major - sharps + expect(score.masterBars[8].keySignature).toEqual(KeySignature.C); + expect(score.masterBars[8].keySignatureType).toEqual(KeySignatureType.Major); + expect(score.masterBars[9].keySignature).toEqual(KeySignature.G); + expect(score.masterBars[9].keySignatureType).toEqual(KeySignatureType.Major); + expect(score.masterBars[10].keySignature).toEqual(KeySignature.D); + expect(score.masterBars[10].keySignatureType).toEqual(KeySignatureType.Major); + expect(score.masterBars[11].keySignature).toEqual(KeySignature.A); + expect(score.masterBars[11].keySignatureType).toEqual(KeySignatureType.Major); + expect(score.masterBars[12].keySignature).toEqual(KeySignature.E); + expect(score.masterBars[12].keySignatureType).toEqual(KeySignatureType.Major); + expect(score.masterBars[13].keySignature).toEqual(KeySignature.B); + expect(score.masterBars[13].keySignatureType).toEqual(KeySignatureType.Major); + expect(score.masterBars[14].keySignature).toEqual(KeySignature.FSharp); + expect(score.masterBars[14].keySignatureType).toEqual(KeySignatureType.Major); + expect(score.masterBars[15].keySignature).toEqual(KeySignature.CSharp); + expect(score.masterBars[15].keySignatureType).toEqual(KeySignatureType.Major); + + // minor flats + expect(score.masterBars[16].keySignature).toEqual(KeySignature.C); + expect(score.masterBars[16].keySignatureType).toEqual(KeySignatureType.Minor); + expect(score.masterBars[17].keySignature).toEqual(KeySignature.F); + expect(score.masterBars[17].keySignatureType).toEqual(KeySignatureType.Minor); + expect(score.masterBars[18].keySignature).toEqual(KeySignature.Bb); + expect(score.masterBars[18].keySignatureType).toEqual(KeySignatureType.Minor); + expect(score.masterBars[19].keySignature).toEqual(KeySignature.Eb); + expect(score.masterBars[19].keySignatureType).toEqual(KeySignatureType.Minor); + expect(score.masterBars[20].keySignature).toEqual(KeySignature.Ab); + expect(score.masterBars[20].keySignatureType).toEqual(KeySignatureType.Minor); + expect(score.masterBars[21].keySignature).toEqual(KeySignature.Db); + expect(score.masterBars[21].keySignatureType).toEqual(KeySignatureType.Minor); + expect(score.masterBars[22].keySignature).toEqual(KeySignature.Gb); + expect(score.masterBars[22].keySignatureType).toEqual(KeySignatureType.Minor); + expect(score.masterBars[23].keySignature).toEqual(KeySignature.Cb); + expect(score.masterBars[23].keySignatureType).toEqual(KeySignatureType.Minor); + + // minor sharps + expect(score.masterBars[24].keySignature).toEqual(KeySignature.C); + expect(score.masterBars[24].keySignatureType).toEqual(KeySignatureType.Minor); + expect(score.masterBars[25].keySignature).toEqual(KeySignature.G); + expect(score.masterBars[25].keySignatureType).toEqual(KeySignatureType.Minor); + expect(score.masterBars[26].keySignature).toEqual(KeySignature.D); + expect(score.masterBars[26].keySignatureType).toEqual(KeySignatureType.Minor); + expect(score.masterBars[27].keySignature).toEqual(KeySignature.A); + expect(score.masterBars[27].keySignatureType).toEqual(KeySignatureType.Minor); + expect(score.masterBars[28].keySignature).toEqual(KeySignature.E); + expect(score.masterBars[28].keySignatureType).toEqual(KeySignatureType.Minor); + expect(score.masterBars[29].keySignature).toEqual(KeySignature.B); + expect(score.masterBars[29].keySignatureType).toEqual(KeySignatureType.Minor); + expect(score.masterBars[30].keySignature).toEqual(KeySignature.FSharp); + expect(score.masterBars[30].keySignatureType).toEqual(KeySignatureType.Minor); + expect(score.masterBars[31].keySignature).toEqual(KeySignature.CSharp); + expect(score.masterBars[31].keySignatureType).toEqual(KeySignatureType.Minor); + } + + public static checkColors(score: Score): void { + expect(score.tracks[0].name).toEqual('Red'); + expect(score.tracks[0].color.rgba).toEqual('#FF0000'); + expect(score.tracks[1].name).toEqual('Green'); + expect(score.tracks[1].color.rgba).toEqual('#00FF00'); + expect(score.tracks[2].name).toEqual('Yellow'); + expect(score.tracks[2].color.rgba).toEqual('#FFFF00'); + expect(score.tracks[3].name).toEqual('Blue'); + expect(score.tracks[3].color.rgba).toEqual('#0000FF'); + } + + private static createChord(name: string, firstFret: number, strings: number[], barreFrets?: number[]) { + const chord = new Chord(); + chord.name = name; + chord.firstFret = firstFret; + chord.strings = strings; + if (barreFrets) { + chord.barreFrets = barreFrets; + } + return chord; + } + + public static checkChords(score: Score): void { + let track: Track = score.tracks[0]; + let staff: Staff = track.staves[0]; + expect(staff.chords.size).toEqual(8); + + GpImporterTestHelper.checkChord( + GpImporterTestHelper.createChord('C', 1, [0, 1, 0, 2, 3, -1]), + track.staves[0].bars[0].voices[0].beats[0].chord + ); + GpImporterTestHelper.checkChord( + GpImporterTestHelper.createChord('Cm', 1, [-1, -1, 0, 1, 3, -1]), + track.staves[0].bars[0].voices[0].beats[1].chord + ); + GpImporterTestHelper.checkChord( + GpImporterTestHelper.createChord('C', 1, [3, 5, 5, 5, 3, -1], [3]), + track.staves[0].bars[0].voices[0].beats[2].chord + ); + GpImporterTestHelper.checkChord( + GpImporterTestHelper.createChord('Cm', 1, [3, 4, 5, 5, 3, -1], [3]), + track.staves[0].bars[0].voices[0].beats[3].chord + ); + + GpImporterTestHelper.checkChord( + GpImporterTestHelper.createChord('D', 1, [2, 3, 2, 0, -1, -1], [2]), + track.staves[0].bars[1].voices[0].beats[0].chord + ); + GpImporterTestHelper.checkChord( + GpImporterTestHelper.createChord('Dm', 1, [1, 3, 2, 0, -1, -1]), + track.staves[0].bars[1].voices[0].beats[1].chord + ); + GpImporterTestHelper.checkChord( + GpImporterTestHelper.createChord('D', 5, [5, 7, 7, 7, 5, -1], [5]), + track.staves[0].bars[1].voices[0].beats[2].chord + ); + GpImporterTestHelper.checkChord( + GpImporterTestHelper.createChord('Dm', 5, [5, 6, 7, 7, 5, -1], [5]), + track.staves[0].bars[1].voices[0].beats[3].chord + ); + } + + public static checkChord(expected: Chord | null, actual: Chord | null): void { + expect(actual === null).toEqual(expected === null); + if (expected && actual) { + expect(actual.name).toEqual(expected.name); + expect(actual.firstFret).toEqual(expected.firstFret); + expect(actual.strings.length).toEqual(expected.strings.length); + expect(actual.strings.join(',')).toEqual(expected.strings.join(',')); + expect(actual.barreFrets.join(',')).toEqual(expected.barreFrets.join(',')); + } + } +} diff --git a/test/importer/GpxImporter.test.ts b/test/importer/GpxImporter.test.ts new file mode 100644 index 000000000..82308ff5a --- /dev/null +++ b/test/importer/GpxImporter.test.ts @@ -0,0 +1,271 @@ +import { GpxFile, GpxFileSystem } from '@src/importer/GpxFileSystem'; +import { GpxImporter } from '@src/importer/GpxImporter'; +import { ByteBuffer } from '@src/io/ByteBuffer'; +import { Score } from '@src/model/Score'; +import { Settings } from '@src/Settings'; +import { Logger } from '@src/Logger'; +import { GpImporterTestHelper } from '@test/importer/GpImporterTestHelper'; +import { TestPlatform } from '@test/TestPlatform'; + +describe('GpxImporterTest', () => { + const prepareGpxImporterWithFile: (name: string) => Promise = async ( + name: string + ): Promise => { + const data = await TestPlatform.loadFile('test-data/' + name); + return prepareGpxImporterWithBytes(data); + }; + + const prepareGpxImporterWithBytes: (buffer: Uint8Array) => GpxImporter = (buffer: Uint8Array): GpxImporter => { + let readerBase: GpxImporter = new GpxImporter(); + readerBase.init(ByteBuffer.fromBuffer(buffer), new Settings()); + return readerBase; + }; + + it('file-system-compressed', async () => { + const data = await TestPlatform.loadFile('test-data/guitarpro6/file-system-compressed.gpx'); + let fileSystem: GpxFileSystem = new GpxFileSystem(); + fileSystem.load(ByteBuffer.fromBuffer(data)); + let names: string[] = [ + 'score.gpif', + 'misc.xml', + 'BinaryStylesheet', + 'PartConfiguration', + 'LayoutConfiguration' + ]; + let sizes = [8488, 130, 12204, 20, 12]; + for (let i: number = 0; i < fileSystem.files.length; i++) { + let file: GpxFile = fileSystem.files[i]; + Logger.info('Test', `${file.fileName} - ${file.fileSize}`); + expect(file.fileName).toEqual(names[i]); + expect(file.fileSize).toEqual(sizes[i]); + } + }); + + it('score-info', async () => { + const reader = await prepareGpxImporterWithFile('guitarpro6/score-info.gpx'); + let score: Score = reader.readScore(); + expect(score.title).toEqual('Title'); + expect(score.subTitle).toEqual('Subtitle'); + expect(score.artist).toEqual('Artist'); + expect(score.album).toEqual('Album'); + expect(score.words).toEqual('Words'); + expect(score.music).toEqual('Music'); + expect(score.copyright).toEqual('Copyright'); + expect(score.tab).toEqual('Tab'); + expect(score.instructions).toEqual('Instructions'); + expect(score.notices).toEqual('Notice1\nNotice2'); + expect(score.masterBars.length).toEqual(5); + expect(score.tracks.length).toEqual(2); + expect(score.tracks[0].name).toEqual('Track 1'); + expect(score.tracks[1].name).toEqual('Track 2'); + }); + + it('notes', async () => { + const reader = await prepareGpxImporterWithFile('guitarpro6/notes.gpx'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkNotes(score); + }); + + it('time-signatures', async () => { + const reader = await prepareGpxImporterWithFile('guitarpro6/time-signatures.gpx'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkTimeSignatures(score); + }); + + it('dead', async () => { + const reader = await prepareGpxImporterWithFile('guitarpro6/dead.gpx'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkDead(score); + }); + + it('grace', async () => { + const reader = await prepareGpxImporterWithFile('guitarpro6/grace.gpx'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkGrace(score); + }); + + it('accentuations', async () => { + const reader = await prepareGpxImporterWithFile('guitarpro6/accentuations.gpx'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkAccentuations(score, true); + }); + + it('harmonics', async () => { + const reader = await prepareGpxImporterWithFile('guitarpro6/harmonics.gpx'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkHarmonics(score); + }); + + it('hammer', async () => { + const reader = await prepareGpxImporterWithFile('guitarpro6/hammer.gpx'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkHammer(score); + }); + + it('bends', async () => { + const reader = await prepareGpxImporterWithFile('guitarpro6/bends.gpx'); + let score: Score = reader.readScore(); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints.length).toEqual(2); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints[0].offset).toEqual(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints[0].value).toEqual(0); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints[1].offset).toEqual(60); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0].bendPoints[1].value).toEqual(4); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints.length).toEqual(2); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints[0].offset).toEqual(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints[0].value).toEqual(0); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints[1].offset).toEqual(60); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[1].notes[0].bendPoints[1].value).toEqual(4); + + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints.length).toEqual(3); + + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints[0].offset).toEqual(0); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints[0].value).toEqual(0); + + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints[0].offset).toEqual(0); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints[0].value).toEqual(0); + + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints[1].offset).toEqual(30); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints[1].value).toEqual(12); + + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints[2].offset).toEqual(60); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].notes[0].bendPoints[2].value).toEqual(6); + }); + + it('tremolo', async () => { + const reader = await prepareGpxImporterWithFile('guitarpro6/tremolo.gpx'); + let score: Score = reader.readScore(); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints.length).toEqual(3); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints[0].offset).toEqual(0); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints[0].value).toEqual(0); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints[1].offset).toEqual(30); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints[1].value).toEqual(-4); + + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints[2].offset).toEqual(60); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[0].whammyBarPoints[2].value).toEqual(0); + + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints.length).toEqual(2); + + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints[0].offset).toEqual(0); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints[0].value).toEqual(-4); + + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints[1].offset).toEqual(60); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].whammyBarPoints[1].value).toEqual(0); + + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints.length).toEqual(3); + + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints[0].offset).toEqual(0); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints[0].value).toEqual(0); + + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints[1].offset).toEqual(30); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints[1].value).toEqual(-4); + + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints[2].offset).toEqual(60); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[0].whammyBarPoints[2].value).toEqual(-4); + + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints.length).toEqual(4); + + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints[0].offset).toEqual(0); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints[0].value).toEqual(-4); + + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints[1].offset).toEqual(15); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints[1].value).toEqual(-12); + + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints[2].offset).toEqual(30); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints[2].value).toEqual(-12); + + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints[3].offset).toEqual(45); + expect(score.tracks[0].staves[0].bars[3].voices[0].beats[0].whammyBarPoints[3].value).toEqual(0); + }); + + it('slides', async () => { + const reader = await prepareGpxImporterWithFile('guitarpro6/slides.gpx'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkSlides(score); + }); + + it('vibrato', async () => { + const reader = await prepareGpxImporterWithFile('guitarpro6/vibrato.gpx'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkVibrato(score, true); + }); + + it('trills', async () => { + const reader = await prepareGpxImporterWithFile('guitarpro6/trills.gpx'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkTrills(score); + }); + + it('other-effects', async () => { + const reader = await prepareGpxImporterWithFile('guitarpro6/other-effects.gpx'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkOtherEffects(score, true); + }); + + it('fingering', async () => { + const reader = await prepareGpxImporterWithFile('guitarpro6/fingering.gpx'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkFingering(score); + }); + + it('stroke', async () => { + const reader = await prepareGpxImporterWithFile('guitarpro6/strokes.gpx'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkStroke(score); + }); + + it('tuplets', async () => { + const reader = await prepareGpxImporterWithFile('guitarpro6/tuplets.gpx'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkTuplets(score); + }); + + it('ranges', async () => { + const reader = await prepareGpxImporterWithFile('guitarpro6/ranges.gpx'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkRanges(score); + }); + + it('effects', async () => { + const reader = await prepareGpxImporterWithFile('guitarpro6/effects.gpx'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkEffects(score); + }); + + it('serenade', async () => { + const reader = await prepareGpxImporterWithFile('guitarpro6/serenade.gpx'); + reader.readScore(); + // only Check reading + }); + + it('strings', async () => { + const reader = await prepareGpxImporterWithFile('guitarpro6/strings.gpx'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkStrings(score); + }); + + it('key-signatures', async () => { + const reader = await prepareGpxImporterWithFile('guitarpro6/key-signatures.gpx'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkKeySignatures(score); + }); + + it('chords', async () => { + const reader = await prepareGpxImporterWithFile('guitarpro6/chords.gpx'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkChords(score); + }); + + it('colors', async () => { + const reader = await prepareGpxImporterWithFile('guitarpro6/colors.gpx'); + let score: Score = reader.readScore(); + GpImporterTestHelper.checkColors(score); + }); +}); diff --git a/test/importer/MusicXmlImporterSamples.test.ts b/test/importer/MusicXmlImporterSamples.test.ts new file mode 100644 index 000000000..8917d61ea --- /dev/null +++ b/test/importer/MusicXmlImporterSamples.test.ts @@ -0,0 +1,71 @@ +import { MusicXmlImporterTestHelper } from '@test/importer/MusicXmlImporterTestHelper'; + +describe('MusicXmlImporterSamplesTests', () => { + it('BeetAnGeSample', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-samples/BeetAnGeSample.xml'); + }); + + it('Binchois', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-samples/Binchois.xml'); + }); + + it('BrahWiMeSample', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-samples/BrahWiMeSample.xml'); + }); + + it('BrookeWestSample', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-samples/BrookeWestSample.xml'); + }); + + it('Chant', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-samples/Chant.xml'); + }); + + it('DebuMandSample', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-samples/DebuMandSample.xml'); + }); + + it('Dichterliebe01', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-samples/Dichterliebe01.xml'); + }); + + it('Echigo', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-samples/Echigo.xml'); + }); + + it('FaurReveSample', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-samples/FaurReveSample.xml'); + }); + + it('MahlFaGe4Sample', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-samples/MahlFaGe4Sample.xml'); + }); + + it('MozaChloSample', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-samples/MozaChloSample.xml'); + }); + + it('MozartPianoSonata', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-samples/MozartPianoSonata.xml'); + }); + + it('MozartTrio', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-samples/MozartTrio.xml'); + }); + + it('MozaVeilSample', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-samples/MozaVeilSample.xml'); + }); + + it('Saltarello', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-samples/Saltarello.xml'); + }); + + it('SchbAvMaSample', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-samples/SchbAvMaSample.xml'); + }); + + it('Telemann', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-samples/Telemann.xml'); + }); +}); diff --git a/test/importer/MusicXmlImporterTestHelper.ts b/test/importer/MusicXmlImporterTestHelper.ts new file mode 100644 index 000000000..5457ab2ea --- /dev/null +++ b/test/importer/MusicXmlImporterTestHelper.ts @@ -0,0 +1,275 @@ +import { LayoutMode } from '@src/DisplaySettings'; +import { MusicXmlImporter } from '@src/importer/MusicXmlImporter'; +import { UnsupportedFormatError } from '@src/importer/UnsupportedFormatError'; +import { ByteBuffer } from '@src/io/ByteBuffer'; +import { Bar } from '@src/model/Bar'; +import { Beat } from '@src/model/Beat'; +import { BendPoint } from '@src/model/BendPoint'; +import { Chord } from '@src/model/Chord'; +import { MasterBar } from '@src/model/MasterBar'; +import { Note } from '@src/model/Note'; +import { PlaybackInformation } from '@src/model/PlaybackInformation'; +import { Score } from '@src/model/Score'; +import { Section } from '@src/model/Section'; +import { Staff } from '@src/model/Staff'; +import { Track } from '@src/model/Track'; +import { Voice } from '@src/model/Voice'; +import { Settings } from '@src/Settings'; +import { TestPlatform } from '@test/TestPlatform'; + +export class MusicXmlImporterTestHelper { + public static prepareImporterWithBytes(buffer: Uint8Array): MusicXmlImporter { + let readerBase: MusicXmlImporter = new MusicXmlImporter(); + readerBase.init(ByteBuffer.fromBuffer(buffer), new Settings()); + return readerBase; + } + + public static async testReferenceFile( + file: string, + renderLayout: LayoutMode = LayoutMode.Page, + renderAllTracks: boolean = false + ): Promise { + const fileData = await TestPlatform.loadFile(file); + try { + let importer: MusicXmlImporter = MusicXmlImporterTestHelper.prepareImporterWithBytes(fileData); + let score: Score = importer.readScore(); + return score; + } catch (e) { + if (e instanceof UnsupportedFormatError) { + fail(`Failed to load file ${file}: ${e}`); + } + throw e; + } + } + + protected static getHierarchy(node: unknown): string { + let note: Note | null = node instanceof Note ? node : null; + if (note) { + return `${MusicXmlImporterTestHelper.getHierarchy(note.beat)}Note[${note.index}]`; + } + let beat: Beat | null = node instanceof Beat ? node : null; + if (beat) { + return `${MusicXmlImporterTestHelper.getHierarchy(beat.voice)}Beat[${beat.index}]`; + } + let voice: Voice | null = node instanceof Voice ? node : null; + if (voice) { + return `${MusicXmlImporterTestHelper.getHierarchy(voice.bar)}Voice[${voice.index}]`; + } + let bar: Bar | null = node instanceof Bar ? node : null; + if (bar) { + return `${MusicXmlImporterTestHelper.getHierarchy(bar.staff)}Bar[${bar.index}]`; + } + let staff: Staff | null = node instanceof Staff ? node : null; + if (staff) { + return `${MusicXmlImporterTestHelper.getHierarchy(staff.track)}Staff[${staff.index}]`; + } + let track: Track | null = node instanceof Track ? node : null; + if (track) { + return `Track[${track.index}]`; + } + let mb: MasterBar | null = node instanceof MasterBar ? node : null; + if (mb) { + return `MasterBar[${mb.index}]`; + } + return 'unknown'; + } + + protected expectScoreEqual(expected: Score, actual: Score): void { + expect(actual.album).toEqual(expected.album, 'Mismatch on Album'); + expect(actual.artist).toEqual(expected.artist, 'Mismatch on Artist'); + expect(actual.copyright).toEqual(expected.copyright, 'Mismatch on Copyright'); + expect(actual.instructions).toEqual(expected.instructions, 'Mismatch on Instructions'); + expect(actual.music).toEqual(expected.music, 'Mismatch on Music'); + expect(actual.notices).toEqual(expected.notices, 'Mismatch on Notices'); + expect(actual.subTitle).toEqual(expected.subTitle, 'Mismatch on SubTitle'); + expect(actual.title).toEqual(expected.title, 'Mismatch on Title'); + expect(actual.words).toEqual(expected.words, 'Mismatch on Words'); + expect(actual.tab).toEqual(expected.tab, 'Mismatch on Tab'); + expect(actual.tempo).toEqual(expected.tempo, 'Mismatch on Tempo'); + expect(actual.tempoLabel).toEqual(expected.tempoLabel, 'Mismatch on TempoLabel'); + expect(actual.masterBars.length).toEqual(expected.masterBars.length, 'Mismatch on MasterBars.Count'); + for (let i: number = 0; i < expected.masterBars.length; i++) { + this.expectMasterBarEqual(expected.masterBars[i], actual.masterBars[i]); + } + expect(actual.tracks.length).toEqual(expected.tracks.length, 'Mismatch on Tracks.Count'); + for (let i: number = 0; i < expected.tracks.length; i++) { + this.expectTrackEqual(expected.tracks[i], actual.tracks[i]); + } + } + + protected expectTrackEqual(expected: Track, actual: Track): void { + expect(actual.index).toEqual(expected.index, 'Mismatch on Index'); + expect(actual.name).toEqual(expected.name, 'Mismatch on Name'); + this.expectPlaybackInformationEqual(expected.playbackInfo, actual.playbackInfo); + expect(actual.staves.length).toEqual(expected.staves.length, 'Mismatch on Staves.Count'); + for (let i: number = 0; i < expected.staves.length; i++) { + this.expectStaffEqual(expected.staves[i], actual.staves[i]); + } + } + + protected expectStaffEqual(expected: Staff, actual: Staff): void { + expect(actual.capo).toEqual(expected.capo, 'Mismatch on Capo'); + expect(actual.isPercussion).toEqual(expected.isPercussion, 'Mismatch on IsPercussion'); + expect(actual.showStandardNotation).toEqual(expected.showStandardNotation, 'Mismatch on ShowStandardNotation'); + expect(actual.showTablature).toEqual(expected.showTablature, 'Mismatch on ShowTablature'); + expect(actual.tuning.join(',')).toEqual(expected.tuning.join(',')); + expect(actual.tuning.length).toEqual(expected.tuning.length, 'Mismatch on Tuning.Length'); + expect(actual.index).toEqual(expected.index, 'Mismatch on Index'); + expect(actual.bars.length).toEqual(expected.bars.length, 'Mismatch on Bars.Count'); + for (let i: number = 0; i < expected.bars.length; i++) { + this.expectBarEqual(expected.bars[i], actual.bars[i]); + } + } + + protected expectBarEqual(expected: Bar, actual: Bar): void { + expect(actual.index).toEqual(expected.index, 'Mismatch on Index'); + expect(actual.clef).toEqual(expected.clef, 'Mismatch on Clef'); + expect(actual.clefOttava).toEqual(expected.clefOttava, 'Mismatch on ClefOttavia'); + // Assert.AreEqual(expected.Voices.Count, actual.Voices.Count, "Mismatch on Voices.Count"); + for (let i: number = 0; i < Math.min(expected.voices.length, actual.voices.length); i++) { + this.expectVoiceEqual(expected.voices[i], actual.voices[i]); + } + } + + protected expectVoiceEqual(expected: Voice, actual: Voice): void { + expect(actual.index).toEqual(expected.index, 'Mismatch on Index'); + expect(actual.beats.length).toEqual(expected.beats.length, 'Mismatch on Beats.Count'); + for (let i: number = 0; i < expected.beats.length; i++) { + this.expectBeatEqual(expected.beats[i], actual.beats[i]); + } + } + + protected expectBeatEqual(expected: Beat, actual: Beat): void { + expect(actual.index).toEqual(expected.index, 'Mismatch on Index'); + expect(actual.isEmpty).toEqual(expected.isEmpty, 'Mismatch on IsEmpty'); + expect(actual.isRest).toEqual(expected.isRest, 'Mismatch on IsRest'); + expect(actual.dots).toEqual(expected.dots, 'Mismatch on Dots'); + expect(actual.fadeIn).toEqual(expected.fadeIn, 'Mismatch on FadeIn'); + expect(actual.isLegatoOrigin).toEqual(expected.isLegatoOrigin, 'Mismatch on IsLegatoOrigin'); + if (!expected.lyrics) { + expect(actual.lyrics).toBeFalsy(); + } else { + expect(actual.lyrics!.join(',')).toEqual(expected.lyrics.join(',')); + } + expect(actual.pop).toEqual(expected.pop, 'Mismatch on Pop'); + expect(actual.hasChord).toEqual(expected.hasChord, 'Mismatch on HasChord'); + expect(actual.hasRasgueado).toEqual(expected.hasRasgueado, 'Mismatch on HasRasgueado'); + expect(actual.tap).toEqual(expected.tap); + expect(actual.slap).toEqual(expected.slap); + expect(actual.text).toEqual(expected.text, 'Mismatch on Text'); + expect(actual.brushType).toEqual(expected.brushType, 'Mismatch on BrushType'); + expect(actual.brushDuration).toEqual(expected.brushDuration, 'Mismatch on BrushDuration'); + expect(actual.tupletDenominator).toEqual(expected.tupletDenominator, 'Mismatch on TupletDenominator'); + expect(actual.tupletNumerator).toEqual(expected.tupletNumerator, 'Mismatch on TupletNumerator'); + this.expectBendPointsEqual(expected.whammyBarPoints, actual.whammyBarPoints); + expect(actual.vibrato).toEqual(expected.vibrato, 'Mismatch on Vibrato'); + if (expected.hasChord) { + this.expectChordEqual(expected.chord!, actual.chord!); + } + expect(actual.graceType).toEqual(expected.graceType, 'Mismatch on GraceType'); + expect(actual.pickStroke).toEqual(expected.pickStroke, 'Mismatch on PickStroke'); + expect(actual.tremoloSpeed).toEqual(expected.tremoloSpeed, 'Mismatch on TremoloSpeed'); + expect(actual.crescendo).toEqual(expected.crescendo, 'Mismatch on Crescendo'); + expect(actual.playbackStart).toEqual(expected.playbackStart, 'Mismatch on Start'); + expect(actual.displayStart).toEqual(expected.displayStart, 'Mismatch on Start'); + // Assert.AreEqual(expected.Dynamic, actual.Dynamic, "Mismatch on Dynamic"); + expect(actual.invertBeamDirection).toEqual(expected.invertBeamDirection, 'Mismatch on InvertBeamDirection'); + expect(actual.notes.length).toEqual(expected.notes.length, 'Mismatch on Notes.Count'); + for (let i: number = 0; i < expected.notes.length; i++) { + this.expectNoteEqual(expected.notes[i], actual.notes[i]); + } + } + + protected expectNoteEqual(expected: Note, actual: Note): void { + expect(actual.index).toEqual(expected.index, 'Mismatch on Index'); + expect(actual.accentuated).toEqual(expected.accentuated, 'Mismatch on Accentuated'); + this.expectBendPointsEqual(expected.bendPoints, actual.bendPoints); + expect(actual.isStringed).toEqual(expected.isStringed, 'Mismatch on IsStringed'); + if (actual.isStringed) { + expect(actual.fret).toEqual(expected.fret, 'Mismatch on Fret'); + expect(actual.string).toEqual(expected.string, 'Mismatch on String'); + } + expect(actual.isPiano).toEqual(expected.isPiano, 'Mismatch on IsPiano'); + if (actual.isPiano) { + expect(actual.octave).toEqual(expected.octave, 'Mismatch on Octave'); + expect(actual.tone).toEqual(expected.tone, 'Mismatch on Tone'); + } + expect(actual.variation).toEqual(expected.variation, 'Mismatch on Variation'); + expect(actual.element).toEqual(expected.element, 'Mismatch on Element'); + expect(actual.isHammerPullOrigin).toEqual(expected.isHammerPullOrigin, 'Mismatch on IsHammerPullOrigin'); + expect(actual.harmonicType).toEqual(expected.harmonicType, 'Mismatch on HarmonicType'); + expect(actual.harmonicValue).toEqual(expected.harmonicValue, 'Mismatch on HarmonicValue'); + expect(actual.isGhost).toEqual(expected.isGhost, 'Mismatch on IsGhost'); + expect(actual.isLetRing).toEqual(expected.isLetRing, 'Mismatch on IsLetRing'); + expect(actual.isPalmMute).toEqual(expected.isPalmMute, 'Mismatch on IsPalmMute'); + expect(actual.isDead).toEqual(expected.isDead, 'Mismatch on IsDead'); + expect(actual.isStaccato).toEqual(expected.isStaccato, 'Mismatch on IsStaccato'); + expect(actual.slideInType).toEqual(expected.slideInType, 'Mismatch on SlideInType'); + expect(actual.slideOutType).toEqual(expected.slideOutType, 'Mismatch on SlideOutType'); + expect(actual.vibrato).toEqual(expected.vibrato, 'Mismatch on Vibrato'); + expect(actual.isTieDestination).toEqual(expected.isTieDestination, 'Mismatch on IsTieDestination'); + expect(actual.isTieOrigin).toEqual(expected.isTieOrigin, 'Mismatch on IsTieOrigin'); + expect(actual.leftHandFinger).toEqual(expected.leftHandFinger, 'Mismatch on LeftHandFinger'); + expect(actual.isFingering).toEqual(expected.isFingering, 'Mismatch on IsFingering'); + expect(actual.trillValue).toEqual(expected.trillValue, 'Mismatch on TrillValue'); + expect(actual.trillSpeed).toEqual(expected.trillSpeed, 'Mismatch on TrillSpeed'); + expect(actual.durationPercent).toEqual(expected.durationPercent, 'Mismatch on DurationPercent'); + expect(actual.dynamics).toEqual(expected.dynamics, 'Mismatch on Dynamic'); + expect(actual.realValue).toEqual(expected.realValue, 'Mismatch on RealValue'); + } + + protected expectChordEqual(expected: Chord | null, actual: Chord): void { + expect(!expected).toEqual(!actual); + if (expected) { + // expect(actual.name).toEqual(expected.name, 'Mismatch on Name'); + } + } + + protected expectBendPointsEqual(expected: BendPoint[], actual: BendPoint[]): void { + expect(actual.length).toEqual(expected.length, 'Mismatch on Count'); + for (let i: number = 0; i < expected.length; i++) { + expect(actual[i].value).toEqual(actual[i].value); + expect(actual[i].offset).toEqual(actual[i].offset); + } + } + + protected expectPlaybackInformationEqual(expected: PlaybackInformation, actual: PlaybackInformation): void { + expect(actual.volume).toEqual(expected.volume, 'Mismatch on Volume'); + expect(actual.balance).toEqual(expected.balance, 'Mismatch on Balance'); + // expect(actual.port).toEqual(expected.port, "Mismatch on Port"); + expect(actual.program).toEqual(expected.program, 'Mismatch on Program'); + // expect(actual.primaryChannel).toEqual(expected.primaryChannel, "Mismatch on PrimaryChannel"); + // expect(actual.secondaryChannel).toEqual(expected.secondaryChannel, "Mismatch on SecondaryChannel"); + expect(actual.isMute).toEqual(expected.isMute, 'Mismatch on IsMute'); + expect(actual.isSolo).toEqual(expected.isSolo, 'Mismatch on IsSolo'); + } + + protected expectMasterBarEqual(expected: MasterBar, actual: MasterBar): void { + expect(actual.alternateEndings).toEqual(expected.alternateEndings, 'Mismatch on AlternateEndings'); + expect(actual.index).toEqual(expected.index, 'Mismatch on Index'); + expect(actual.keySignature).toEqual(expected.keySignature, 'Mismatch on KeySignature'); + expect(actual.keySignatureType).toEqual(expected.keySignatureType, 'Mismatch on KeySignatureType'); + expect(actual.isDoubleBar).toEqual(expected.isDoubleBar, 'Mismatch on IsDoubleBar'); + expect(actual.isRepeatStart).toEqual(expected.isRepeatStart, 'Mismatch on IsRepeatStart'); + expect(actual.repeatCount).toEqual(expected.repeatCount, 'Mismatch on RepeatCount'); + expect(actual.timeSignatureNumerator).toEqual( + expected.timeSignatureNumerator, + 'Mismatch on TimeSignatureNumerator' + ); + expect(actual.timeSignatureDenominator).toEqual( + expected.timeSignatureDenominator, + 'Mismatch on TimeSignatureDenominator' + ); + expect(actual.tripletFeel).toEqual(expected.tripletFeel, 'Mismatch on TripletFeel'); + this.expectSectionEqual(expected.section!, actual.section); + expect(actual.start).toEqual(expected.start, 'Mismatch on Start'); + } + + protected expectSectionEqual(expected: Section | null, actual: Section | null): void { + expect(!actual).toEqual(!expected); + if (expected && actual) { + expect(actual.text).toEqual(expected.text, 'Mismatch on Text'); + expect(actual.marker).toEqual(expected.marker, 'Mismatch on Marker'); + } + } +} diff --git a/test/importer/MusicXmlImporterTestSuite.test.ts b/test/importer/MusicXmlImporterTestSuite.test.ts new file mode 100644 index 000000000..4316c960f --- /dev/null +++ b/test/importer/MusicXmlImporterTestSuite.test.ts @@ -0,0 +1,609 @@ +import { MusicXmlImporterTestHelper } from '@test/importer/MusicXmlImporterTestHelper'; + +describe('MusicXmlImporterTestSuiteTests', () => { + it('01a_Pitches_Pitches', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/01a-Pitches-Pitches.xml'); + }); + + it('01b_Pitches_Intervals', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/01b-Pitches-Intervals.xml'); + }); + + it('01c_Pitches_NoVoiceElement', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/01c-Pitches-NoVoiceElement.xml' + ); + }); + + it('01d_Pitches_Microtones', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/01d-Pitches-Microtones.xml'); + }); + + it('01e_Pitches_ParenthesizedAccidentals', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/01e-Pitches-ParenthesizedAccidentals.xml' + ); + }); + + it('01f_Pitches_ParenthesizedMicrotoneAccidentals', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/01f-Pitches-ParenthesizedMicrotoneAccidentals.xml' + ); + }); + + it('02a_Rests_Durations', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/02a-Rests-Durations.xml'); + }); + + it('02b_Rests_PitchedRests', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/02b-Rests-PitchedRests.xml'); + }); + + it('02c_Rests_MultiMeasureRests', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/02c-Rests-MultiMeasureRests.xml' + ); + }); + + it('02d_Rests_Multimeasure_TimeSignatures', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/02d-Rests-Multimeasure-TimeSignatures.xml' + ); + }); + + it('02e_Rests_NoType', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/02e-Rests-NoType.xml'); + }); + + it('03a_Rhythm_Durations', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/03a-Rhythm-Durations.xml'); + }); + + it('03b_Rhythm_Backup', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/03b-Rhythm-Backup.xml'); + }); + + it('03c_Rhythm_DivisionChange', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/03c-Rhythm-DivisionChange.xml'); + }); + + it('03d_Rhythm_DottedDurations_Factors', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/03d-Rhythm-DottedDurations-Factors.xml' + ); + }); + + it('11a_TimeSignatures', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/11a-TimeSignatures.xml'); + }); + + it('11b_TimeSignatures_NoTime', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/11b-TimeSignatures-NoTime.xml'); + }); + + it('11c_TimeSignatures_CompoundSimple', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/11c-TimeSignatures-CompoundSimple.xml' + ); + }); + + it('11d_TimeSignatures_CompoundMultiple', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/11d-TimeSignatures-CompoundMultiple.xml' + ); + }); + + it('11e_TimeSignatures_CompoundMixed', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/11e-TimeSignatures-CompoundMixed.xml' + ); + }); + + it('11f_TimeSignatures_SymbolMeaning', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/11f-TimeSignatures-SymbolMeaning.xml' + ); + }); + + it('11g_TimeSignatures_SingleNumber', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/11g-TimeSignatures-SingleNumber.xml' + ); + }); + + it('11h_TimeSignatures_SenzaMisura', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/11h-TimeSignatures-SenzaMisura.xml' + ); + }); + + it('12a_Clefs', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/12a-Clefs.xml'); + }); + + it('12b_Clefs_NoKeyOrClef', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/12b-Clefs-NoKeyOrClef.xml'); + }); + + it('13a_KeySignatures', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/13a-KeySignatures.xml'); + }); + + it('13b_KeySignatures_ChurchModes', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/13b-KeySignatures-ChurchModes.xml' + ); + }); + + it('13c_KeySignatures_NonTraditional', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/13c-KeySignatures-NonTraditional.xml' + ); + }); + + it('13d_KeySignatures_Microtones', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/13d-KeySignatures-Microtones.xml' + ); + }); + + it('14a_StaffDetails_LineChanges', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/14a-StaffDetails-LineChanges.xml' + ); + }); + + it('21a_Chord_Basic', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/21a-Chord-Basic.xml'); + }); + + it('21b_Chords_TwoNotes', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/21b-Chords-TwoNotes.xml'); + }); + + it('21c_Chords_ThreeNotesDuration', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/21c-Chords-ThreeNotesDuration.xml' + ); + }); + + it('21d_Chords_SchubertStabatMater', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/21d-Chords-SchubertStabatMater.xml' + ); + }); + + it('21e_Chords_PickupMeasures', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/21e-Chords-PickupMeasures.xml'); + }); + + it('21f_Chord_ElementInBetween', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/21f-Chord-ElementInBetween.xml' + ); + }); + + it('22a_Noteheads', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/22a-Noteheads.xml'); + }); + + it('22b_Staff_Notestyles', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/22b-Staff-Notestyles.xml'); + }); + + it('22c_Noteheads_Chords', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/22c-Noteheads-Chords.xml'); + }); + + it('22d_Parenthesized_Noteheads', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/22d-Parenthesized-Noteheads.xml' + ); + }); + + it('23a_Tuplets', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/23a-Tuplets.xml'); + }); + + it('23b_Tuplets_Styles', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/23b-Tuplets-Styles.xml'); + }); + + it('23c_Tuplet_Display_NonStandard', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/23c-Tuplet-Display-NonStandard.xml' + ); + }); + + it('23d_Tuplets_Nested', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/23d-Tuplets-Nested.xml'); + }); + + it('23e_Tuplets_Tremolo', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/23e-Tuplets-Tremolo.xml'); + }); + + it('23f_Tuplets_DurationButNoBracket', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/23f-Tuplets-DurationButNoBracket.xml' + ); + }); + + it('24a_GraceNotes', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/24a-GraceNotes.xml'); + }); + + it('24b_ChordAsGraceNote', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/24b-ChordAsGraceNote.xml'); + }); + + it('24c_GraceNote_MeasureEnd', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/24c-GraceNote-MeasureEnd.xml'); + }); + + it('24d_AfterGrace', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/24d-AfterGrace.xml'); + }); + + it('24e_GraceNote_StaffChange', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/24e-GraceNote-StaffChange.xml'); + }); + + it('24f_GraceNote_Slur', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/24f-GraceNote-Slur.xml'); + }); + + it('31a_Directions', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/31a-Directions.xml'); + }); + + it('31c_MetronomeMarks', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/31c-MetronomeMarks.xml'); + }); + + it('32a_Notations', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/32a-Notations.xml'); + }); + + it('32b_Articulations_Texts', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/32b-Articulations-Texts.xml'); + }); + + it('32c_MultipleNotationChildren', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/32c-MultipleNotationChildren.xml' + ); + }); + + it('32d_Arpeggio', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/32d-Arpeggio.xml'); + }); + + it('33a_Spanners', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/33a-Spanners.xml'); + }); + + it('33b_Spanners_Tie', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/33b-Spanners-Tie.xml'); + }); + + it('33c_Spanners_Slurs', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/33c-Spanners-Slurs.xml'); + }); + + it('33d_Spanners_OctaveShifts', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/33d-Spanners-OctaveShifts.xml'); + }); + + it('33e_Spanners_OctaveShifts_InvalidSize', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/33e-Spanners-OctaveShifts-InvalidSize.xml' + ); + }); + + it('33f_Trill_EndingOnGraceNote', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/33f-Trill-EndingOnGraceNote.xml' + ); + }); + + it('33g_Slur_ChordedNotes', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/33g-Slur-ChordedNotes.xml'); + }); + + it('33h_Spanners_Glissando', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/33h-Spanners-Glissando.xml'); + }); + + it('33i_Ties_NotEnded', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/33i-Ties-NotEnded.xml'); + }); + + it('41a_MultiParts_Partorder', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/41a-MultiParts-Partorder.xml'); + }); + + it('41b_MultiParts_MoreThan10', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/41b-MultiParts-MoreThan10.xml'); + }); + + it('41c_StaffGroups', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/41c-StaffGroups.xml'); + }); + + it('41d_StaffGroups_Nested', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/41d-StaffGroups-Nested.xml'); + }); + + it('41e_StaffGroups_InstrumentNames_Linebroken', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/41e-StaffGroups-InstrumentNames-Linebroken.xml' + ); + }); + + it('41f_StaffGroups_Overlapping', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/41f-StaffGroups-Overlapping.xml' + ); + }); + + it('41g_PartNoId', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/41g-PartNoId.xml'); + }); + + it('41h_TooManyParts', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/41h-TooManyParts.xml'); + }); + + it('41i_PartNameDisplay_Override', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/41i-PartNameDisplay-Override.xml' + ); + }); + + it('42a_MultiVoice_TwoVoicesOnStaff_Lyrics', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/42a-MultiVoice-TwoVoicesOnStaff-Lyrics.xml' + ); + }); + + it('42b_MultiVoice_MidMeasureClefChange', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/42b-MultiVoice-MidMeasureClefChange.xml' + ); + }); + + it('43a_PianoStaff', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/43a-PianoStaff.xml'); + }); + + it('43b_MultiStaff_DifferentKeys', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/43b-MultiStaff-DifferentKeys.xml' + ); + }); + + it('43c_MultiStaff_DifferentKeysAfterBackup', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/43c-MultiStaff-DifferentKeysAfterBackup.xml' + ); + }); + + it('43d_MultiStaff_StaffChange', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/43d-MultiStaff-StaffChange.xml' + ); + }); + + it('43e_Multistaff_ClefDynamics', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/43e-Multistaff-ClefDynamics.xml' + ); + }); + + it('45a_SimpleRepeat', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/45a-SimpleRepeat.xml'); + }); + + it('45b_RepeatWithAlternatives', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/45b-RepeatWithAlternatives.xml' + ); + }); + + it('45c_RepeatMultipleTimes', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/45c-RepeatMultipleTimes.xml'); + }); + + it('45d_Repeats_Nested_Alternatives', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/45d-Repeats-Nested-Alternatives.xml' + ); + }); + + it('45e_Repeats_Nested_Alternatives', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/45e-Repeats-Nested-Alternatives.xml' + ); + }); + + it('45f_Repeats_InvalidEndings', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/45f-Repeats-InvalidEndings.xml' + ); + }); + + it('45g_Repeats_NotEnded', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/45g-Repeats-NotEnded.xml'); + }); + + it('46a_Barlines', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/46a-Barlines.xml'); + }); + + it('46b_MidmeasureBarline', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/46b-MidmeasureBarline.xml'); + }); + + it('46c_Midmeasure_Clef', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/46c-Midmeasure-Clef.xml'); + }); + + it('46d_PickupMeasure_ImplicitMeasures', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/46d-PickupMeasure-ImplicitMeasures.xml' + ); + }); + + it('46e_PickupMeasure_SecondVoiceStartsLater', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/46e-PickupMeasure-SecondVoiceStartsLater.xml' + ); + }); + + it('46f_IncompleteMeasures', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/46f-IncompleteMeasures.xml'); + }); + + it('46g_PickupMeasure_Chordnames_FiguredBass', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/46g-PickupMeasure-Chordnames-FiguredBass.xml' + ); + }); + + it('51b_Header_Quotes', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/51b-Header-Quotes.xml'); + }); + + it('51c_MultipleRights', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/51c-MultipleRights.xml'); + }); + + it('51d_EmptyTitle', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/51d-EmptyTitle.xml'); + }); + + it('52a_PageLayout', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/52a-PageLayout.xml'); + }); + + it('52b_Breaks', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/52b-Breaks.xml'); + }); + + it('61a_Lyrics', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/61a-Lyrics.xml'); + }); + + it('61b_MultipleLyrics', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/61b-MultipleLyrics.xml'); + }); + + it('61c_Lyrics_Pianostaff', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/61c-Lyrics-Pianostaff.xml'); + }); + + it('61d_Lyrics_Melisma', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/61d-Lyrics-Melisma.xml'); + }); + + it('61e_Lyrics_Chords', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/61e-Lyrics-Chords.xml'); + }); + + it('61f_Lyrics_GracedNotes', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/61f-Lyrics-GracedNotes.xml'); + }); + + it('61g_Lyrics_NameNumber', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/61g-Lyrics-NameNumber.xml'); + }); + + it('61h_Lyrics_BeamsMelismata', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/61h-Lyrics-BeamsMelismata.xml'); + }); + + it('61i_Lyrics_Chords', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/61i-Lyrics-Chords.xml'); + }); + + it('61j_Lyrics_Elisions', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/61j-Lyrics-Elisions.xml'); + }); + + it('61k_Lyrics_SpannersExtenders', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/61k-Lyrics-SpannersExtenders.xml' + ); + }); + + it('71a_Chordnames', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/71a-Chordnames.xml'); + }); + + it('71c_ChordsFrets', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/71c-ChordsFrets.xml'); + }); + + it('71d_ChordsFrets_Multistaff', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/71d-ChordsFrets-Multistaff.xml' + ); + }); + + it('71e_TabStaves', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/71e-TabStaves.xml'); + }); + + it('71f_AllChordTypes', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/71f-AllChordTypes.xml'); + }); + + it('71g_MultipleChordnames', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/71g-MultipleChordnames.xml'); + }); + + it('72a_TransposingInstruments', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/72a-TransposingInstruments.xml' + ); + }); + + it('72b_TransposingInstruments_Full', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/72b-TransposingInstruments-Full.xml' + ); + }); + + it('72c_TransposingInstruments_Change', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/72c-TransposingInstruments-Change.xml' + ); + }); + + it('73a_Percussion', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/73a-Percussion.xml'); + }); + + it('74a_FiguredBass', async () => { + await MusicXmlImporterTestHelper.testReferenceFile('test-data/musicxml-testsuite/74a-FiguredBass.xml'); + }); + + it('75a_AccordionRegistrations', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/75a-AccordionRegistrations.xml' + ); + }); + + it('99a_Sibelius5_IgnoreBeaming', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/99a-Sibelius5-IgnoreBeaming.xml' + ); + }); + + it('99b_Lyrics_BeamsMelismata_IgnoreBeams', async () => { + await MusicXmlImporterTestHelper.testReferenceFile( + 'test-data/musicxml-testsuite/99b-Lyrics-BeamsMelismata-IgnoreBeams.xml' + ); + }); +}); diff --git a/test/index.ts b/test/index.ts new file mode 100644 index 000000000..3007d2412 --- /dev/null +++ b/test/index.ts @@ -0,0 +1 @@ +import '**/*.test.js'; \ No newline at end of file diff --git a/test/model/Lyrics.test.ts b/test/model/Lyrics.test.ts new file mode 100644 index 000000000..1e7395b9b --- /dev/null +++ b/test/model/Lyrics.test.ts @@ -0,0 +1,137 @@ +import { GpxImporter } from '@src/importer/GpxImporter'; +import { ByteBuffer } from '@src/io/ByteBuffer'; +import { Lyrics } from '@src/model/Lyrics'; +import { Score } from '@src/model/Score'; +import { Settings } from '@src/Settings'; +import { TestPlatform } from '@test/TestPlatform'; + +describe('LyricsTests', () => { + const loadLyricsTemplateFile: () => Promise = async (): Promise => { + const path: string = 'test-data/lyrics/template.gpx'; + const data = await TestPlatform.loadFile(path); + let buffer: ByteBuffer = ByteBuffer.fromBuffer(data); + let importer: GpxImporter = new GpxImporter(); + importer.init(buffer, new Settings()); + return importer.readScore(); + }; + + const testLyrics: ((text: string, chunks: string[])=> void) = (text: string, chunks: string[]): void => { + let lyrics: Lyrics = new Lyrics(); + lyrics.text = text; + lyrics.finish(); + expect(lyrics.chunks.join(',')).toEqual(chunks.join(',')); + }; + + it('apply-single-line-first-bar', async () => { + const score = await loadLyricsTemplateFile(); + const lyrics = new Lyrics(); + lyrics.text = 'AAA BBB CCC DDD EEE'; + lyrics.startBar = 0; + score.tracks[0].applyLyrics([lyrics]); + expect(1).toEqual(1); + expect('AAA').toEqual('AAA'); + expect('BBB').toEqual('BBB'); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].lyrics).toBeFalsy(); + expect('CCC').toEqual('CCC'); + expect(1).toEqual(1); + expect('DDD').toEqual('DDD'); + expect('EEE').toEqual('EEE'); + }); + + it('apply-single-line-bar-offset', async () => { + const score = await loadLyricsTemplateFile(); + const lyrics = new Lyrics(); + lyrics.text = 'AAA BBB CCC DDD EEE'; + lyrics.startBar = 2; + score.tracks[0].applyLyrics([lyrics]); + + expect(1).toEqual(1); + expect('AAA').toEqual('AAA'); + expect('BBB').toEqual('BBB'); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[2].lyrics).toBeFalsy(); + expect('CCC').toEqual('CCC'); + expect(1).toEqual(1); + expect('DDD').toEqual('DDD'); + expect('EEE').toEqual('EEE'); + }); + + it('apply-multi-line-first-bar', async () => { + const score = await loadLyricsTemplateFile(); + const lyrics1 = new Lyrics(); + lyrics1.text = 'AAA BBB CCC DDD EEE'; + lyrics1.startBar = 0; + + const lyrics2 = new Lyrics(); + lyrics2.text = '111 222 333 444 555'; + lyrics2.startBar = 0; + + score.tracks[0].applyLyrics([lyrics1, lyrics2]); + expect(2).toEqual(2); + expect('AAA').toEqual('AAA'); + expect('111').toEqual('111'); + expect('BBB').toEqual('BBB'); + expect('222').toEqual('222'); + expect(score.tracks[0].staves[0].bars[0].voices[0].beats[2].lyrics).toBeFalsy(); + expect('CCC').toEqual('CCC'); + expect('333').toEqual('333'); + expect(2).toEqual(2); + expect('DDD').toEqual('DDD'); + expect('444').toEqual('444'); + expect('EEE').toEqual('EEE'); + expect('555').toEqual('555'); + }); + + it('apply-multi-line-bar-offset', async () => { + const score = await loadLyricsTemplateFile(); + const lyrics1 = new Lyrics(); + lyrics1.text = 'AAA BBB CCC DDD EEE'; + lyrics1.startBar = 2; + + const lyrics2 = new Lyrics(); + lyrics2.text = '111 222 333 444 555'; + lyrics2.startBar = 1; + + score.tracks[0].applyLyrics([lyrics1, lyrics2]); + expect(2).toEqual(2); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[0].lyrics![0]).toBeFalsy(); + expect('111').toEqual('111'); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[1].lyrics![0]).toBeFalsy(); + expect('222').toEqual('222'); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[2].lyrics).toBeFalsy(); + expect(score.tracks[0].staves[0].bars[1].voices[0].beats[3].lyrics![0]).toBeFalsy(); + expect('333').toEqual('333'); + expect(2).toEqual(2); + expect('AAA').toEqual('AAA'); + expect('444').toEqual('444'); + expect('BBB').toEqual('BBB'); + expect('555').toEqual('555'); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[2].lyrics).toBeFalsy(); + expect('CCC').toEqual('CCC'); + expect(score.tracks[0].staves[0].bars[2].voices[0].beats[3].lyrics![1]).toBeFalsy(); + }); + + it('spaces', async () => { + testLyrics('AAA BBB CCC DDD EEE', ['AAA', 'BBB', 'CCC', 'DDD', 'EEE']); + testLyrics('AAA BBB CCC', ['AAA', '', 'BBB', '', '', 'CCC']); + }); + + it('new-lines', async () => { + testLyrics('AAA\r\nBBB\rCCC\nDDD\r\nEEE', ['AAA', 'BBB', 'CCC', 'DDD', 'EEE']); + }); + + it('dash', async () => { + testLyrics('AAA-BBB CCC- DDD EEE--FFF', ['AAA-', 'BBB', 'CCC-', 'DDD', 'EEE--', 'FFF']); + }); + + it('plus', async () => { + testLyrics('AAA+BBB CCC++DDD EEE+ FFF', ['AAA BBB', 'CCC DDD', 'EEE ', 'FFF']); + }); + + it('comments', async () => { + testLyrics('[ABCD]AAA BBB', ['AAA', 'BBB']); + testLyrics('[ABCD] AAA BBB', ['', 'AAA', 'BBB']); + testLyrics('[AAA BBB\r\nCCC DDD]AAA BBB', ['AAA', 'BBB']); + testLyrics('[AAA BBB\r\nCCC DDD] AAA BBB', ['', 'AAA', 'BBB']); + testLyrics('[AAA] AAA [BBB] BBB [CCC] CCC [DDD] DDD', ['', 'AAA', '', 'BBB', '', 'CCC', '', 'DDD']); + }); +}); diff --git a/test/model/TuningParser.test.ts b/test/model/TuningParser.test.ts new file mode 100644 index 000000000..7b8c211a9 --- /dev/null +++ b/test/model/TuningParser.test.ts @@ -0,0 +1,17 @@ +import { Tuning } from '@src/model/Tuning'; +import { ModelUtils } from '@src/model/ModelUtils'; + +describe('TuningParserTest', () => { + it('standard', () => { + let standard: Tuning = Tuning.getDefaultTuningFor(6)!; + let tuningText: string[] = ['E4', 'B3', 'G3', 'D3', 'A2', 'E2']; + let tuning = new Array(tuningText.length); + let tuningText2: string[] = new Array(tuningText.length); + for (let i: number = 0; i < tuningText.length; i++) { + tuning[i] = ModelUtils.getTuningForText(tuningText[i]); + tuningText2[i] = Tuning.getTextForTuning(tuning[i], true); + } + expect(tuning.join(',')).toEqual(standard.tunings.join(',')); + expect(tuningText2.join(',')).toEqual(tuningText.join(',')); + }); +}); diff --git a/test/visualTests/PixelMatch.ts b/test/visualTests/PixelMatch.ts new file mode 100644 index 000000000..85a156116 --- /dev/null +++ b/test/visualTests/PixelMatch.ts @@ -0,0 +1,343 @@ +/** + * Based on https://github.com/mapbox/pixelmatch + * ISC License + * Copyright (c) 2019, Mapbox + * + * Permission to use, copy, modify, and/or distribute this software for any purpose + * with or without fee is hereby granted, provided that the above copyright notice + * and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + * THIS SOFTWARE. + */ +'use strict'; + +interface Options { + /** + * Matching threshold, ranges from 0 to 1. Smaller values make the comparison more sensitive. + * @default 0.1 + */ + readonly threshold?: number; + /** + * If true, disables detecting and ignoring anti-aliased pixels. + * @default false + */ + readonly includeAA?: boolean; + /** + * Blending factor of unchanged pixels in the diff output. + * Ranges from 0 for pure white to 1 for original brightness + * @default 0.1 + */ + alpha?: number; + /** + * The color of anti-aliased pixels in the diff output. + * @default [255, 255, 0] + */ + aaColor?: number[]; + /** + * The color of differing pixels in the diff output. + * @default [255, 0, 0] + */ + diffColor?: number[]; + /** + * An alternative color to use for dark on light differences to differentiate between "added" and "removed" parts. + * If not provided, all differing pixels use the color specified by `diffColor`. + * @default null + */ + diffColorAlt?: number[]; + /** + * Draw the diff over a transparent background (a mask), rather than over the original image. + * Will not draw anti-aliased pixels (if detected) + * @default false + */ + diffMask?: boolean; +} + +interface Result { + readonly totalPixels: number; + readonly differentPixels: number; + readonly transparentPixels: number; +} + +/** + * @target web + */ +export class PixelMatch { + static defaultOptions: Options = { + threshold: 0.1, // matching threshold (0 to 1); smaller is more sensitive + includeAA: false, // whether to skip anti-aliasing detection + alpha: 0.1, // opacity of original image in diff ouput + aaColor: [255, 255, 0], // color of anti-aliased pixels in diff output + diffColor: [255, 0, 0], // color of different pixels in diff output + diffMask: false // draw the diff over a transparent background (a mask) + }; + + static match(img1: Uint8Array, img2: Uint8Array, output: Uint8Array, width: number, height: number, options: Options): Result { + if ( + !PixelMatch.isPixelData(img1) || + !PixelMatch.isPixelData(img2) || + (output && !PixelMatch.isPixelData(output)) + ) { + throw new Error('Image data: Uint8Array, Uint8ClampedArray or Buffer expected.'); + } + + if (img1.length !== img2.length || (output && output.length !== img1.length)) { + throw new Error('Image sizes do not match.'); + } + + if (img1.length !== width * height * 4) throw new Error('Image data size does not match width/height.'); + + options = Object.assign({}, PixelMatch.defaultOptions, options); + + // check if images are identical + const len = width * height; + const a32 = new Uint32Array(img1.buffer, img1.byteOffset, len); + const b32 = new Uint32Array(img2.buffer, img2.byteOffset, len); + let identical = true; + let transparentPixels = 0; + + for (let i = 0; i < len; i++) { + if (a32[i] !== b32[i]) { + identical = false; + break; + } + if ((a32[i] & 0xff000000) === 0xff000000) { + transparentPixels++; + } + } + if (identical) { + // fast path if identical + if (output && !options.diffMask) { + for (let i = 0; i < len; i++) PixelMatch.drawGrayPixel(img1, 4 * i, options.alpha!, output); + } + return { + totalPixels: len, + differentPixels: 0, + transparentPixels: transparentPixels + }; + } + + transparentPixels = 0; + + // maximum acceptable square distance between two colors; + // 35215 is the maximum possible value for the YIQ difference metric + const maxDelta = 35215 * options.threshold! * options.threshold!; + + let diff = 0; + const [aaR, aaG, aaB] = options.aaColor!; + const [diffR, diffG, diffB] = options.diffColor!; + + // compare each pixel of one image against the other one + for (let y = 0; y < height; y++) { + for (let x = 0; x < width; x++) { + const pos = (y * width + x) * 4; + + if (img1[pos + 3] === 0) { + transparentPixels++; + } + // squared YUV distance between colors at this pixel position + const delta = PixelMatch.colorDelta(img1, img2, pos, pos); + + // the color difference is above the threshold + if (delta > maxDelta) { + // check it's a real rendering difference or just anti-aliasing + if ( + !options.includeAA && + (PixelMatch.antialiased(img1, x, y, width, height, img2) || + PixelMatch.antialiased(img2, x, y, width, height, img1)) + ) { + // one of the pixels is anti-aliasing; draw as yellow and do not count as difference + // note that we do not include such pixels in a mask + if (output && !options.diffMask) PixelMatch.drawPixel(output, pos, aaR, aaG, aaB); + } else { + // found substantial difference not caused by anti-aliasing; draw it as red + if (output) PixelMatch.drawPixel(output, pos, diffR, diffG, diffB); + diff++; + } + } else if (output) { + // pixels are similar; draw background as grayscale image blended with white + if (!options.diffMask) PixelMatch.drawGrayPixel(img1, pos, options.alpha!, output); + } + } + } + + // return the number of different pixels + return { + totalPixels: len, + transparentPixels: transparentPixels, + differentPixels: diff + }; + } + + static isPixelData(arr: object) { + // work around instanceof Uint8Array not working properly in some Jest environments + return ArrayBuffer.isView(arr) && (arr.constructor as any).BYTES_PER_ELEMENT === 1; + } + + // check if a pixel is likely a part of anti-aliasing; + // based on "Anti-aliased Pixel and Intensity Slope Detector" paper by V. Vysniauskas, 2009 + + static antialiased(img: Uint8Array, x1: number, y1: number, width: number, height: number, img2: Uint8Array) { + const distance = 1; + const x0 = Math.max(x1 - distance, 0); + const y0 = Math.max(y1 - distance, 0); + const x2 = Math.min(x1 + distance, width - 1); + const y2 = Math.min(y1 + distance, height - 1); + const pos = (y1 * width + x1) * 4; + let zeroes = x1 === x0 || x1 === x2 || y1 === y0 || y1 === y2 ? 1 : 0; + let min = 0; + let max = 0; + let minX = 0; + let minY = 0; + let maxX = 0; + let maxY = 0; + + // go through 8 adjacent pixels + for (let x = x0; x <= x2; x++) { + for (let y = y0; y <= y2; y++) { + if (x === x1 && y === y1) continue; + + // brightness delta between the center pixel and adjacent one + const delta = PixelMatch.colorDelta(img, img, pos, (y * width + x) * 4, true); + + // count the number of equal, darker and brighter adjacent pixels + if (delta === 0) { + zeroes++; + // if found more than 2 equal siblings, it's definitely not anti-aliasing + if (zeroes > 2) return false; + + // remember the darkest pixel + } else if (delta < min) { + min = delta; + minX = x; + minY = y; + + // remember the brightest pixel + } else if (delta > max) { + max = delta; + maxX = x; + maxY = y; + } + } + } + + // if there are no both darker and brighter pixels among siblings, it's not anti-aliasing + if (min === 0 || max === 0) return false; + + // if either the darkest or the brightest pixel has 3+ equal siblings in both images + // (definitely not anti-aliased), this pixel is anti-aliased + return ( + (PixelMatch.hasManySiblings(img, minX, minY, width, height) && + PixelMatch.hasManySiblings(img2, minX, minY, width, height)) || + (PixelMatch.hasManySiblings(img, maxX, maxY, width, height) && + PixelMatch.hasManySiblings(img2, maxX, maxY, width, height)) + ); + } + + // check if a pixel has 3+ adjacent pixels of the same color. + static hasManySiblings(img: Uint8Array, x1: number, y1: number, width: number, height: number) { + const distance = 1; + const x0 = Math.max(x1 - distance, 0); + const y0 = Math.max(y1 - distance, 0); + const x2 = Math.min(x1 + distance, width - 1); + const y2 = Math.min(y1 + distance, height - 1); + const pos = (y1 * width + x1) * 4; + let zeroes = x1 === x0 || x1 === x2 || y1 === y0 || y1 === y2 ? 1 : 0; + + // go through 8 adjacent pixels + for (let x = x0; x <= x2; x++) { + for (let y = y0; y <= y2; y++) { + if (x === x1 && y === y1) continue; + + const pos2 = (y * width + x) * 4; + if ( + img[pos] === img[pos2] && + img[pos + 1] === img[pos2 + 1] && + img[pos + 2] === img[pos2 + 2] && + img[pos + 3] === img[pos2 + 3] + ) { + zeroes++; + } + + if (zeroes > 2) return true; + } + } + + return false; + } + + // calculate color difference according to the paper "Measuring perceived color difference + // using YIQ NTSC transmission color space in mobile applications" by Y. Kotsarenko and F. Ramos + + static colorDelta(img1: Uint8Array, img2: Uint8Array, k: number, m: number, yOnly: boolean = false) { + let r1 = img1[k + 0]; + let g1 = img1[k + 1]; + let b1 = img1[k + 2]; + let a1 = img1[k + 3]; + + let r2 = img2[m + 0]; + let g2 = img2[m + 1]; + let b2 = img2[m + 2]; + let a2 = img2[m + 3]; + + if (a1 === a2 && r1 === r2 && g1 === g2 && b1 === b2) return 0; + + if (a1 < 255) { + a1 /= 255; + r1 = PixelMatch.blend(r1, a1); + g1 = PixelMatch.blend(g1, a1); + b1 = PixelMatch.blend(b1, a1); + } + + if (a2 < 255) { + a2 /= 255; + r2 = PixelMatch.blend(r2, a2); + g2 = PixelMatch.blend(g2, a2); + b2 = PixelMatch.blend(b2, a2); + } + + const y = PixelMatch.rgb2y(r1, g1, b1) - PixelMatch.rgb2y(r2, g2, b2); + + if (yOnly) return y; // brightness difference only + + const i = PixelMatch.rgb2i(r1, g1, b1) - PixelMatch.rgb2i(r2, g2, b2); + const q = PixelMatch.rgb2q(r1, g1, b1) - PixelMatch.rgb2q(r2, g2, b2); + + return 0.5053 * y * y + 0.299 * i * i + 0.1957 * q * q; + } + + static rgb2y(r: number, g: number, b: number) { + return r * 0.29889531 + g * 0.58662247 + b * 0.11448223; + } + static rgb2i(r: number, g: number, b: number) { + return r * 0.59597799 - g * 0.2741761 - b * 0.32180189; + } + static rgb2q(r: number, g: number, b: number) { + return r * 0.21147017 - g * 0.52261711 + b * 0.31114694; + } + + // blend semi-transparent color with white + static blend(c: number, a: number) { + return 255 + (c - 255) * a; + } + + static drawPixel(output: Uint8Array, pos: number, r: number, g: number, b: number) { + output[pos + 0] = r; + output[pos + 1] = g; + output[pos + 2] = b; + output[pos + 3] = 255; + } + + static drawGrayPixel(img: Uint8Array, i: number, alpha: number, output: Uint8Array) { + const r = img[i + 0]; + const g = img[i + 1]; + const b = img[i + 2]; + const val = PixelMatch.blend(PixelMatch.rgb2y(r, g, b), (alpha * img[i + 3]) / 255); + PixelMatch.drawPixel(output, i, val, val, val); + } +} diff --git a/test/visualTests/VisualTestHelper.ts b/test/visualTests/VisualTestHelper.ts new file mode 100644 index 000000000..2c73301f6 --- /dev/null +++ b/test/visualTests/VisualTestHelper.ts @@ -0,0 +1,472 @@ +import { ScoreLoader } from '@src/importer/ScoreLoader'; +import { Score } from '@src/model/Score'; +import { Settings } from '@src/Settings'; +import { TestPlatform } from '@test/TestPlatform'; +import { AlphaTabApi } from '@src/platform/javascript/AlphaTabApi'; +import { CoreSettings } from '@src/CoreSettings'; +import { Environment } from '@src/Environment'; +import { RenderFinishedEventArgs } from '@src/rendering/RenderFinishedEventArgs'; +import { AlphaTexImporter } from '@src/importer/AlphaTexImporter'; +import { ByteBuffer } from '@src/io/ByteBuffer'; +import { PixelMatch } from './PixelMatch'; + +/** + * @partial + * @target web + */ +export class VisualTestHelper { + public static async runVisualTest( + inputFile: string, + settings?: Settings, + tracks?: number[], + message?: string + ): Promise { + try { + const inputFileData = await TestPlatform.loadFile(`test-data/visual-tests/${inputFile}`); + const referenceFileName = TestPlatform.changeExtension(inputFile, '.png'); + let score: Score = ScoreLoader.loadScoreFromBytes(inputFileData, settings); + + await VisualTestHelper.runVisualTestScore(score, referenceFileName, settings, tracks, message); + } catch (e) { + fail(`Failed to run visual test ${e}`); + } + } + + public static async runVisualTestTex( + tex: string, + referenceFileName: string, + settings?: Settings, + tracks?: number[], + message?: string + ): Promise { + try { + if (!settings) { + settings = new Settings(); + } + + const importer = new AlphaTexImporter(); + importer.init(ByteBuffer.fromString(tex), settings); + let score: Score = importer.readScore(); + + await VisualTestHelper.runVisualTestScore(score, referenceFileName, settings, tracks, message); + } catch (e) { + fail(`Failed to run visual test ${e}`); + } + } + + public static async runVisualTestScore( + score: Score, + referenceFileName: string, + settings?: Settings, + tracks?: number[], + message?: string + ): Promise { + try { + if (!settings) { + settings = new Settings(); + } + if (!tracks) { + tracks = [0]; + } + + settings.core.fontDirectory = CoreSettings.ensureFullUrl('/base/font/bravura/'); + settings.core.engine = 'html5'; + settings.core.enableLazyLoading = false; + + const referenceFileData = await TestPlatform.loadFile(`test-data/visual-tests/${referenceFileName}`); + + const renderElement = document.createElement('div'); + renderElement.style.width = '1300px'; + renderElement.style.position = 'absolute'; + renderElement.style.visibility = 'hidden'; + document.body.appendChild(renderElement); + + // here we need to trick a little bit, normally SVG does not require the font to be loaded + // before rendering starts, but in our case we need it to convert it later for diffing to raster. + // so we initiate the bravura load and wait for it before proceeding with rendering. + Environment.createStyleElement(document, settings.core.fontDirectory); + await Promise.race([ + new Promise((resolve, reject) => { + if (Environment.bravuraFontChecker.isFontLoaded) { + resolve(); + } else { + Environment.bravuraFontChecker.fontLoaded.on(() => { + resolve(); + }); + Environment.bravuraFontChecker.checkForFontAvailability(); + } + }), + new Promise((_, reject) => { + setTimeout(() => { + reject(new Error('Font loading did not complete in time')); + }, 2000); + }) + ]); + + let result: RenderFinishedEventArgs[] = []; + let totalWidth: number = 0; + let totalHeight: number = 0; + let render = new Promise((resolve, reject) => { + const api = new AlphaTabApi(renderElement, settings); + api.renderer.partialRenderFinished.on(e => { + if (e) { + result.push(e); + } + }); + api.renderer.renderFinished.on(e => { + totalWidth = e.totalWidth; + totalHeight = e.totalHeight; + result.push(e); + resolve(); + }); + api.error.on(e => { + reject(`Failed to render image: ${e}`); + }); + api.renderScore(score, tracks); + }); + + await Promise.race([ + render, + new Promise((_, reject) => { + setTimeout(() => { + reject(new Error('Rendering did not complete in time')); + }, 2000); + }) + ]); + + await VisualTestHelper.compareVisualResult( + totalWidth, + totalHeight, + result, + referenceFileName, + referenceFileData, + message + ); + } catch (e) { + fail(`Failed to run visual test ${e}`); + } + } + + private static convertPngToCanvas( + data: Uint8Array, + filename: string, + className: string + ): Promise { + return new Promise((resolve, reject) => { + const img = new Image(); + img.src = 'data:image/png;base64,' + btoa(data.reduce((p, d) => p + String.fromCharCode(d), '')); + img.onload = function () { + const canvas = document.createElement('canvas'); + canvas.classList.add(className); + canvas.width = img.naturalWidth; + canvas.height = img.naturalHeight; + canvas.dataset.filename = filename.split('/').slice(-1)[0]; + const context = canvas.getContext('2d')!; + context.drawImage(img, 0, 0); + + resolve(canvas); + }; + img.onerror = function (e) { + reject(e); + }; + }); + } + + private static convertSvgToImage(svg: string): Promise { + return new Promise((resolve, reject) => { + const img = new Image(); + img.src = 'data:image/svg+xml;base64,' + btoa(svg); + img.onload = function () { + resolve(img); + }; + img.onerror = function (e) { + reject(e); + }; + }); + } + + public static async compareVisualResult( + totalWidth: number, + totalHeight: number, + result: RenderFinishedEventArgs[], + referenceFileName: string, + referenceFileData: Uint8Array, + message?: string + ): Promise { + // create final full image + const actual = document.createElement('canvas'); + actual.classList.add('actual-image'); + actual.width = totalWidth; + actual.height = totalHeight; + const actualImageContext = actual.getContext('2d')!; + + const point = { + x: 0, + y: 0 + }; + let rowHeight = 0; + for (const partialResult of result) { + const partialCanvas = partialResult.renderResult; + + let imageSource: CanvasImageSource | null = null; + if (partialCanvas instanceof HTMLCanvasElement) { + imageSource = partialCanvas; + } else if (typeof partialCanvas === 'string') { + imageSource = await VisualTestHelper.convertSvgToImage(partialCanvas); + } + + if (imageSource) { + actualImageContext.drawImage(imageSource, point.x, point.y); + + if (partialResult.height > rowHeight) { + rowHeight = partialResult.height; + } + + point.x += partialResult.width; + + if (point.x >= totalWidth) { + point.x = 0; + point.y += rowHeight | 0; + rowHeight = 0; + } + } + } + + // convert reference image to canvas + const expected = await VisualTestHelper.convertPngToCanvas( + referenceFileData, + referenceFileName, + 'expected-image' + ); + + jasmine.addAsyncMatchers({ + toEqualVisually: VisualTestHelper.toEqualVisually + }); + + await (expectAsync(actual) as any).toEqualVisually(expected, referenceFileName, message); + } + + private static toEqualVisually( + _utils: jasmine.MatchersUtil, + _customEqualityTesters: ReadonlyArray + ): jasmine.CustomAsyncMatcher { + return { + async compare( + actual: HTMLCanvasElement, + expected: HTMLCanvasElement, + expectedFileName: string, + message?: string + ): Promise { + const sizeMismatch = expected.width !== actual.width || expected.height !== actual.height; + const oldActual = actual; + if (sizeMismatch) { + const newActual = document.createElement('canvas'); + newActual.width = expected.width; + newActual.height = expected.height; + + const newActualContext = newActual.getContext('2d')!; + newActualContext.drawImage(actual, 0, 0); + newActualContext.strokeStyle = 'red'; + newActualContext.lineWidth = 2; + newActualContext.strokeRect(0, 0, newActual.width, newActual.height); + + actual = newActual; + } + + const actualImageData = actual.getContext('2d')!.getImageData(0, 0, actual.width, actual.height); + + const expectedImageData = expected + .getContext('2d')! + .getImageData(0, 0, expected.width, expected.height); + + // do visual comparison + const diff = document.createElement('canvas'); + diff.width = expected.width; + diff.height = expected.height; + const diffContext = diff.getContext('2d')!; + const diffImageData = diffContext.createImageData(diff.width, diff.height); + const result: jasmine.CustomMatcherResult = { + pass: true, + message: '' + }; + + try { + let match = PixelMatch.match( + new Uint8Array(expectedImageData.data.buffer), + new Uint8Array(actualImageData.data.buffer), + new Uint8Array(diffImageData.data.buffer), + expected.width, + expected.height, + { + threshold: 0.3, + includeAA: false, + diffMask: true, + alpha: 1 + } + ); + + diffContext.putImageData(diffImageData, 0, 0); + + // only pixels that are not transparent are relevant for the diff-ratio + let totalPixels = match.totalPixels - match.transparentPixels; + let percentDifference = (match.differentPixels / totalPixels) * 100; + result.pass = percentDifference < 1; + + if (!result.pass) { + let percentDifferenceText = percentDifference.toFixed(2); + result.message = `Difference between original and new image is too big: ${match.differentPixels}/${totalPixels} (${percentDifferenceText}%)`; + await VisualTestHelper.saveFiles(expectedFileName, expected, actual, diff); + } else if (sizeMismatch) { + result.message = `Image sizes do not match: ${expected.width}/${expected.height} vs ${oldActual.width}/${oldActual.height}`; + result.pass = false; + } + } catch (e) { + result.pass = false; + result.message = `Error comparing images: ${e}, ${message}`; + } + + const jasmineRequire = Environment.globalThis.jasmineRequire; + if (!result.pass && jasmineRequire.html) { + const dom = document.createElement('div'); + dom.innerHTML = ` + Error: ${result.message} (${message})
    + Expected: +

    + Actual: +
    + Diff: +
    + `; + + actual.ondblclick = () => { + const a = document.createElement('a'); + a.href = oldActual.toDataURL('image/png'); + a.download = expected.dataset.filename ?? 'reference.png'; + document.body.appendChild(a); + a.click(); + }; + + dom.querySelector('.expected')!.appendChild(expected); + dom.querySelector('.actual')!.appendChild(actual); + dom.querySelector('.diff')!.appendChild(diff); + (dom as any).toString = function () { + return result.message; + }; + (result as any).message = dom; + } + + return result; + } + }; + } + + static async saveFiles( + name: string, + expected: HTMLCanvasElement, + actual: HTMLCanvasElement, + diff: HTMLCanvasElement + ): Promise { + const expectedData = await VisualTestHelper.toPngBlob(expected); + const actualData = await VisualTestHelper.toPngBlob(actual); + const diffData = await VisualTestHelper.toPngBlob(diff); + + return new Promise((resolve, reject) => { + let x: XMLHttpRequest = new XMLHttpRequest(); + x.open('POST', 'http://localhost:8090/save-visual-error/', true); + x.onload = () => { + resolve(); + }; + x.onerror = () => { + reject(); + }; + const data = new FormData(); + data.append('name', name); + data.append('expected', expectedData, VisualTestHelper.createFileName(name, 'expected')); + data.append('actual', actualData, VisualTestHelper.createFileName(name, 'actual')); + data.append('diff', diffData, VisualTestHelper.createFileName(name, 'diff')); + x.send(data); + }); + } + + static async toPngBlob(canvas: HTMLCanvasElement): Promise { + return new Promise((resolve, reject) => { + canvas.toBlob(blob => { + if (blob) { + resolve(blob); + } else { + reject(); + } + }, 'image/png'); + }); + } + + static createFileName(oldName: string, part: string) { + oldName = oldName.split('\\').join('/'); + let i = oldName.lastIndexOf('/'); + if (i >= 0) { + oldName = oldName.substr(i + 1); + } + + i = oldName.lastIndexOf('.'); + if (i >= 0) { + oldName = oldName.substr(0, i) + '-' + part + oldName.substr(i); + } else { + oldName += '-' + part; + } + return oldName; + } + + static base64ArrayBuffer(bytes: Uint8Array) { + let base64 = ''; + const encodings = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; + + const byteLength = bytes.byteLength; + const byteRemainder = byteLength % 3; + const mainLength = byteLength - byteRemainder; + + let a; + let b; + let c; + let d; + let chunk; + + // Main loop deals with bytes in chunks of 3 + for (let i = 0; i < mainLength; i += 3) { + // Combine the three bytes into a single integer + chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2]; + + // Use bitmasks to extract 6-bit segments from the triplet + a = (chunk & 16515072) >> 18; // 16515072 = (2^6 - 1) << 18 + b = (chunk & 258048) >> 12; // 258048 = (2^6 - 1) << 12 + c = (chunk & 4032) >> 6; // 4032 = (2^6 - 1) << 6 + d = chunk & 63; // 63 = 2^6 - 1 + + // Convert the raw binary segments to the appropriate ASCII encoding + base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d]; + } + + // Deal with the remaining bytes and padding + if (byteRemainder === 1) { + chunk = bytes[mainLength]; + + a = (chunk & 252) >> 2; // 252 = (2^6 - 1) << 2 + + // Set the 4 least significant bits to zero + b = (chunk & 3) << 4; // 3 = 2^2 - 1 + + base64 += `${encodings[a]}${encodings[b]}==`; + } else if (byteRemainder === 2) { + chunk = (bytes[mainLength] << 8) | bytes[mainLength + 1]; + + a = (chunk & 64512) >> 10; // 64512 = (2^6 - 1) << 10 + b = (chunk & 1008) >> 4; // 1008 = (2^6 - 1) << 4 + + // Set the 2 least significant bits to zero + c = (chunk & 15) << 2; // 15 = 2^4 - 1 + + base64 += `${encodings[a]}${encodings[b]}${encodings[c]}=`; + } + + return base64; + } +} diff --git a/test/visualTests/features/EffectsAndAnnotations.test.ts b/test/visualTests/features/EffectsAndAnnotations.test.ts new file mode 100644 index 000000000..50a246f6f --- /dev/null +++ b/test/visualTests/features/EffectsAndAnnotations.test.ts @@ -0,0 +1,87 @@ +import { VisualTestHelper } from '@test/visualTests/VisualTestHelper'; + +describe('EffectsAndAnnotationsTests', () => { + it('markers', async () => { + await VisualTestHelper.runVisualTest('features/effects-and-annotations/markers.gp5'); + }); + + it('tempo', async () => { + await VisualTestHelper.runVisualTest('features/effects-and-annotations/tempo.gp5'); + }); + + it('text', async () => { + await VisualTestHelper.runVisualTest('features/effects-and-annotations/text.gp5'); + }); + + it('chords', async () => { + await VisualTestHelper.runVisualTest('features/effects-and-annotations/chords.gp5'); + }); + + it('vibrato', async () => { + await VisualTestHelper.runVisualTest('features/effects-and-annotations/vibrato.gp5'); + }); + + it('dynamics', async () => { + await VisualTestHelper.runVisualTest('features/effects-and-annotations/dynamics.gp5'); + }); + + it('tap', async () => { + await VisualTestHelper.runVisualTest('features/effects-and-annotations/tap.gp5'); + }); + + it('fade-in', async () => { + await VisualTestHelper.runVisualTest('features/effects-and-annotations/fade-in.gp5'); + }); + + it('let-ring', async () => { + await VisualTestHelper.runVisualTest('features/effects-and-annotations/let-ring.gp5'); + }); + + it('palm-mute', async () => { + await VisualTestHelper.runVisualTest('features/effects-and-annotations/palm-mute.gp5'); + }); + + it('bends', async () => { + await VisualTestHelper.runVisualTest('features/effects-and-annotations/bends.gp'); + }); + + it('tremolo-bar', async () => { + await VisualTestHelper.runVisualTest('features/effects-and-annotations/tremolo-bar.gp'); + }); + + it('tremolo-picking', async () => { + await VisualTestHelper.runVisualTest('features/effects-and-annotations/tremolo-picking.gp5'); + }); + + it('brush', async () => { + await VisualTestHelper.runVisualTest('features/effects-and-annotations/brush.gp5'); + }); + + it('slides', async () => { + await VisualTestHelper.runVisualTest('features/effects-and-annotations/slides.gp5'); + }); + + it('trill', async () => { + await VisualTestHelper.runVisualTest('features/effects-and-annotations/trill.gp5'); + }); + + it('pickStroke', async () => { + await VisualTestHelper.runVisualTest('features/effects-and-annotations/pick-stroke.gp5'); + }); + + it('tuplets', async () => { + await VisualTestHelper.runVisualTest('features/effects-and-annotations/tuplets.gp5'); + }); + + it('tuplets-advanced', async () => { + await VisualTestHelper.runVisualTest('features/effects-and-annotations/tuplets-advanced.gp', undefined, [0,1,2]); + }); + + it('fingering', async () => { + await VisualTestHelper.runVisualTest('features/effects-and-annotations/fingering.gpx'); + }); + + it('triplet-feel', async () => { + await VisualTestHelper.runVisualTest('features/effects-and-annotations/triplet-feel.gpx'); + }); +}); diff --git a/test/visualTests/features/General.test.ts b/test/visualTests/features/General.test.ts new file mode 100644 index 000000000..dce115dac --- /dev/null +++ b/test/visualTests/features/General.test.ts @@ -0,0 +1,48 @@ +import { StaveProfile, LayoutMode } from '@src/DisplaySettings'; +import { Settings } from '@src/Settings'; +import { VisualTestHelper } from '@test/visualTests/VisualTestHelper'; +import { TestPlatform } from '@test/TestPlatform'; +import { ScoreLoader } from '@src/importer/ScoreLoader'; +import { Score } from '@src/model/Score'; + +describe('GeneralTests', () => { + it('song-details', async () => { + await VisualTestHelper.runVisualTest('features/general/song-details.gp5'); + }); + + it('repeats', async () => { + let settings: Settings = new Settings(); + settings.display.staveProfile = StaveProfile.Score; + await VisualTestHelper.runVisualTest('features/general/repeats.gp5', settings); + }); + + it('alternate-endings', async () => { + let settings: Settings = new Settings(); + settings.display.staveProfile = StaveProfile.Score; + await VisualTestHelper.runVisualTest('features/general/alternate-endings.gp5', settings); + }); + + it('tuning', async () => { + await VisualTestHelper.runVisualTest('features/general/tuning.gp5'); + }); + + it('notation-legend-default', async () => { + let settings: Settings = new Settings(); + settings.display.layoutMode = LayoutMode.Horizontal; + const inputFileData = await TestPlatform.loadFile(`test-data/visual-tests/features/general/notation-legend.gp`); + const referenceFileName = `features/general/notation-legend-default.png`; + let score: Score = ScoreLoader.loadScoreFromBytes(inputFileData, settings); + + await VisualTestHelper.runVisualTestScore(score, referenceFileName, settings, [0]); + }); + + it('notation-legend-songbook', async () => { + let settings: Settings = new Settings(); + settings.display.layoutMode = LayoutMode.Horizontal; + settings.setSongBookModeSettings(); + const inputFileData = await TestPlatform.loadFile(`test-data/visual-tests/features/general/notation-legend.gp`); + const referenceFileName = `features/general/notation-legend-songbook.png`; + let score: Score = ScoreLoader.loadScoreFromBytes(inputFileData, settings); + await VisualTestHelper.runVisualTestScore(score, referenceFileName, settings, [0]); + }); +}); diff --git a/test/visualTests/features/GuitarTabs.test.ts b/test/visualTests/features/GuitarTabs.test.ts new file mode 100644 index 000000000..450015149 --- /dev/null +++ b/test/visualTests/features/GuitarTabs.test.ts @@ -0,0 +1,27 @@ +import { StaveProfile } from '@src/DisplaySettings'; +import { TabRhythmMode } from '@src/NotationSettings'; +import { Settings } from '@src/Settings'; +import { VisualTestHelper } from '@test/visualTests/VisualTestHelper'; + +describe('GuitarTabsTests', () => { + it('rhythm', async () => { + let settings: Settings = new Settings(); + settings.display.staveProfile = StaveProfile.Tab; + settings.notation.rhythmMode = TabRhythmMode.ShowWithBars; + await VisualTestHelper.runVisualTest('features/guitar-tabs/rhythm.gp5', settings); + }); + + it('rhythm-with-beams', async () => { + let settings: Settings = new Settings(); + settings.display.staveProfile = StaveProfile.Tab; + settings.notation.rhythmMode = TabRhythmMode.ShowWithBeams; + await VisualTestHelper.runVisualTest('features/guitar-tabs/rhythm-with-beams.gp5', settings); + }); + + it('string-variations', async () => { + let settings: Settings = new Settings(); + settings.display.staveProfile = StaveProfile.Tab; + await VisualTestHelper.runVisualTest('features/guitar-tabs/string-variations.gp5', settings); + }); + +}); diff --git a/test/visualTests/features/Layout.test.ts b/test/visualTests/features/Layout.test.ts new file mode 100644 index 000000000..0206fe6d1 --- /dev/null +++ b/test/visualTests/features/Layout.test.ts @@ -0,0 +1,46 @@ +import { LayoutMode } from '@src/DisplaySettings'; +import { Settings } from '@src/Settings'; +import { VisualTestHelper } from '@test/visualTests/VisualTestHelper'; + +describe('LayoutTests', () => { + it('pageLayout', async () => { + await VisualTestHelper.runVisualTest('features/layout/page-layout.gp5'); + }); + + it('multi-track', async () => { + await VisualTestHelper.runVisualTest('features/layout/multi-track.gp5', undefined, [0, 3]); + }); + + it('multi-voice', async () => { + await VisualTestHelper.runVisualTest('features/layout/multi-voice.gp5'); + }); + + it('page-layout-5barsperrow', async () => { + let settings: Settings = new Settings(); + settings.display.layoutMode = LayoutMode.Page; + settings.display.barsPerRow = 5; + await VisualTestHelper.runVisualTest('features/layout/page-layout-5barsperrow.gp5', settings); + }); + + it('page-layout-bar5to8', async () => { + let settings: Settings = new Settings(); + settings.display.layoutMode = LayoutMode.Page; + settings.display.startBar = 5; + settings.display.barCount = 4; + await VisualTestHelper.runVisualTest('features/layout/page-layout-5to8.gp5', settings); + }); + + it('horizontal-layout', async () => { + let settings: Settings = new Settings(); + settings.display.layoutMode = LayoutMode.Horizontal; + await VisualTestHelper.runVisualTest('features/layout/horizontal-layout.gp5', settings); + }); + + it('horizontal-layout-bar5to8', async () => { + let settings: Settings = new Settings(); + settings.display.layoutMode = LayoutMode.Horizontal; + settings.display.startBar = 5; + settings.display.barCount = 4; + await VisualTestHelper.runVisualTest('features/layout/horizontal-layout-5to8.gp5', settings); + }); +}); diff --git a/test/visualTests/features/MusicNotation.test.ts b/test/visualTests/features/MusicNotation.test.ts new file mode 100644 index 000000000..a806a24ea --- /dev/null +++ b/test/visualTests/features/MusicNotation.test.ts @@ -0,0 +1,45 @@ +import { LayoutMode, StaveProfile } from '@src/DisplaySettings'; +import { Settings } from '@src/Settings'; +import { VisualTestHelper } from '@test/visualTests/VisualTestHelper'; +import { NotationElement } from '@src/NotationSettings'; + +describe('MusicNotationTests', () => { + it('clefs', async () => { + let settings: Settings = new Settings(); + settings.display.staveProfile = StaveProfile.Score; + settings.display.layoutMode = LayoutMode.Page; + settings.notation.elements.set(NotationElement.ScoreAlbum, false); + settings.notation.elements.set(NotationElement.ScoreArtist, false); + settings.notation.elements.set(NotationElement.ScoreCopyright, false); + settings.notation.elements.set(NotationElement.ScoreMusic, false); + settings.notation.elements.set(NotationElement.ScoreSubTitle, false); + settings.notation.elements.set(NotationElement.ScoreTitle, false); + settings.notation.elements.set(NotationElement.ScoreWords, false); + settings.notation.elements.set(NotationElement.ScoreWordsAndMusic, false); + await VisualTestHelper.runVisualTest('features/music-notation/clefs.gpx', settings); + }); + + it('key-signatures', async () => { + let settings: Settings = new Settings(); + settings.display.staveProfile = StaveProfile.Score; + await VisualTestHelper.runVisualTest('features/music-notation/key-signatures.gp5', settings); + }); + + it('time-signatures', async () => { + let settings: Settings = new Settings(); + settings.display.staveProfile = StaveProfile.Score; + await VisualTestHelper.runVisualTest('features/music-notation/time-signatures.gp5', settings); + }); + + it('notes-rests-beams', async () => { + let settings: Settings = new Settings(); + settings.display.staveProfile = StaveProfile.Score; + await VisualTestHelper.runVisualTest('features/music-notation/notes-rests-beams.gp5', settings); + }); + + it('accidentals', async () => { + let settings: Settings = new Settings(); + settings.display.staveProfile = StaveProfile.Score; + await VisualTestHelper.runVisualTest('features/music-notation/accidentals.gp5', settings); + }); +}); diff --git a/test/visualTests/features/NotationElements.test.ts b/test/visualTests/features/NotationElements.test.ts new file mode 100644 index 000000000..fe97fe1c1 --- /dev/null +++ b/test/visualTests/features/NotationElements.test.ts @@ -0,0 +1,241 @@ +import { LayoutMode } from '@src/DisplaySettings'; +import { Settings } from '@src/Settings'; +import { VisualTestHelper } from '@test/visualTests/VisualTestHelper'; +import { NotationElement } from '@src/NotationSettings'; + +describe('NotationElements', () => { + it('score-info', async () => { + const tex = `\\album "Album" \\artist "Artist" \\copyright "Copyright" \\music "Music" \\subtitle "Subtitle" \\title "Title" \\words "Words" . 3.3*4`; + + const allKeys = [ + NotationElement.ScoreAlbum, + NotationElement.ScoreArtist, + NotationElement.ScoreCopyright, + NotationElement.ScoreMusic, + NotationElement.ScoreSubTitle, + NotationElement.ScoreTitle, + NotationElement.ScoreWords, + NotationElement.ScoreWordsAndMusic + ]; + + let settings: Settings = new Settings(); + settings.display.layoutMode = LayoutMode.Page; + + for (const k of allKeys) { + settings.notation.elements.set(k, false); + } + + const testCases = new Map(); + testCases.set(NotationElement.ScoreAlbum, 'album'); + testCases.set(NotationElement.ScoreArtist, 'artist'); + testCases.set(NotationElement.ScoreCopyright, 'copyright'); + testCases.set(NotationElement.ScoreMusic, 'music'); + testCases.set(NotationElement.ScoreSubTitle, 'subtitle'); + testCases.set(NotationElement.ScoreTitle, 'title'); + testCases.set(NotationElement.ScoreWords, 'words'); + + for (const element of allKeys.filter(k => testCases.has(k))) { + for (const k of allKeys) { + settings.notation.elements.set(k, false); + } + + settings.notation.elements.set(element, true); + const referenceName = testCases.get(element)!; + await VisualTestHelper.runVisualTestTex( + tex, + `features/notation-elements/score-info-${referenceName}.png`, + settings, + undefined, + referenceName + ); + } + + for (const k of allKeys) { + settings.notation.elements.set(k, false); + } + settings.notation.elements.set(NotationElement.ScoreWordsAndMusic, true); + await VisualTestHelper.runVisualTestTex( + `\\album "Album" \\artist "Artist" \\copyright "Copyright" \\music "WordsAndMusic" \\subtitle "Subtitle" \\title "Title" \\words "WordsAndMusic" . 3.3*4`, + `features/notation-elements/score-info-words-and-music.png`, + settings, + undefined, + 'words-and-music' + ); + + // default is all true + settings.notation.elements.clear(); + await VisualTestHelper.runVisualTestTex( + tex, + `features/notation-elements/score-info-all.png`, + settings, + undefined, + 'all' + ); + }); + + it('guitar-tuning-on', async () => { + const tex = `\\tuning e5 b4 g4 d4 a3 d3 . 3.3*4`; + + let settings: Settings = new Settings(); + settings.display.layoutMode = LayoutMode.Page; + + settings.notation.elements.set(NotationElement.GuitarTuning, true); + await VisualTestHelper.runVisualTestTex(tex, `features/notation-elements/guitar-tuning-on.png`, settings); + }); + + it('guitar-tuning-off', async () => { + const tex = `\\tuning d5 b4 g4 d4 a3 d3 . 3.3*4`; + + let settings: Settings = new Settings(); + settings.display.layoutMode = LayoutMode.Page; + + settings.notation.elements.set(NotationElement.GuitarTuning, false); + await VisualTestHelper.runVisualTestTex(tex, `features/notation-elements/guitar-tuning-off.png`, settings); + }); + + it('track-names-off', async () => { + const tex = `\\track "Track Name" 3.3*4`; + + let settings: Settings = new Settings(); + settings.display.layoutMode = LayoutMode.Page; + + settings.notation.elements.set(NotationElement.TrackNames, false); + await VisualTestHelper.runVisualTestTex(tex, `features/notation-elements/track-names-off.png`, settings); + }); + + it('track-names-on', async () => { + const tex = `\\track "Track Name" 3.3*4`; + + let settings: Settings = new Settings(); + settings.display.layoutMode = LayoutMode.Page; + + settings.notation.elements.set(NotationElement.TrackNames, true); + await VisualTestHelper.runVisualTestTex(tex, `features/notation-elements/track-names-on.png`, settings); + }); + + it('chord-diagrams-off', async () => { + const tex = `\\chord "C" 0 1 0 2 3 x . (0.1 1.2 0.3 2.4 3.5){ch "C"}`; + + let settings: Settings = new Settings(); + settings.display.layoutMode = LayoutMode.Page; + + settings.notation.elements.set(NotationElement.ChordDiagrams, false); + await VisualTestHelper.runVisualTestTex(tex, `features/notation-elements/chord-diagrams-off.png`, settings); + }); + + it('chord-diagrams-on', async () => { + const tex = `\\chord "C" 0 1 0 2 3 x . (0.1 1.2 0.3 2.4 3.5){ch "C"}`; + + let settings: Settings = new Settings(); + settings.display.layoutMode = LayoutMode.Page; + + settings.notation.elements.set(NotationElement.ChordDiagrams, true); + await VisualTestHelper.runVisualTestTex(tex, `features/notation-elements/chord-diagrams-on.png`, settings); + }); + + it('parenthesis-on-tied-bends-off', async () => { + const tex = `3.3{b (0 4 )} -.3`; + + let settings: Settings = new Settings(); + settings.display.layoutMode = LayoutMode.Page; + + settings.notation.elements.set(NotationElement.ParenthesisOnTiedBends, false); + await VisualTestHelper.runVisualTestTex( + tex, + `features/notation-elements/parenthesis-on-tied-bends-off.png`, + settings + ); + }); + + it('parenthesis-on-tied-bends-on', async () => { + const tex = `3.3{b (0 4 )} -.3`; + + let settings: Settings = new Settings(); + settings.display.layoutMode = LayoutMode.Page; + + settings.notation.elements.set(NotationElement.ParenthesisOnTiedBends, true); + await VisualTestHelper.runVisualTestTex( + tex, + `features/notation-elements/parenthesis-on-tied-bends-on.png`, + settings + ); + }); + + it('tab-notes-on-tied-bends-off', async () => { + const tex = `3.3{b (0 4 )} -.3{b (4 8)}`; + + let settings: Settings = new Settings(); + settings.display.layoutMode = LayoutMode.Page; + + settings.notation.elements.set(NotationElement.TabNotesOnTiedBends, false); + await VisualTestHelper.runVisualTestTex( + tex, + `features/notation-elements/tab-notes-on-tied-bends-off.png`, + settings + ); + }); + + it('tab-notes-on-tied-bends-on', async () => { + const tex = `3.3{b (0 4 )} -.3{b (4 8)}`; + + let settings: Settings = new Settings(); + settings.display.layoutMode = LayoutMode.Page; + + settings.notation.elements.set(NotationElement.TabNotesOnTiedBends, true); + await VisualTestHelper.runVisualTestTex( + tex, + `features/notation-elements/tab-notes-on-tied-bends-on.png`, + settings + ); + }); + + it('zeros-on-dive-whammys-off', async () => { + const tex = `3.3.1{tb (0 -4)}`; + + let settings: Settings = new Settings(); + settings.display.layoutMode = LayoutMode.Page; + + settings.notation.elements.set(NotationElement.ZerosOnDiveWhammys, false); + await VisualTestHelper.runVisualTestTex( + tex, + `features/notation-elements/zeros-on-dive-whammys-off.png`, + settings + ); + }); + + it('zeros-on-dive-whammys-on', async () => { + const tex = `3.3.1{tb (0 -4)}`; + + let settings: Settings = new Settings(); + settings.display.layoutMode = LayoutMode.Page; + + settings.notation.elements.set(NotationElement.ZerosOnDiveWhammys, true); + await VisualTestHelper.runVisualTestTex( + tex, + `features/notation-elements/zeros-on-dive-whammys-on.png`, + settings + ); + }); + + it('effects-off', async () => { + const tex = `. \\tempo 180 \\tf t16 3.3*4 | \\tempo 60 \\tf d16 3.3*4`; + + let settings: Settings = new Settings(); + settings.display.layoutMode = LayoutMode.Page; + + settings.notation.elements.set(NotationElement.EffectTempo, false); + settings.notation.elements.set(NotationElement.EffectTripletFeel, false); + await VisualTestHelper.runVisualTestTex(tex, `features/notation-elements/effects-off.png`, settings); + }); + + it('effects-on', async () => { + const tex = `. \\tempo 180 \\tf t16 3.3*4 | \\tempo 60 \\tf d16 3.3*4`; + + let settings: Settings = new Settings(); + settings.display.layoutMode = LayoutMode.Page; + + settings.notation.elements.set(NotationElement.EffectTempo, true); + settings.notation.elements.set(NotationElement.EffectTripletFeel, true); + await VisualTestHelper.runVisualTestTex(tex, `features/notation-elements/effects-on.png`, settings); + }); +}); diff --git a/test/visualTests/features/SpecialNotes.test.ts b/test/visualTests/features/SpecialNotes.test.ts new file mode 100644 index 000000000..e40184f27 --- /dev/null +++ b/test/visualTests/features/SpecialNotes.test.ts @@ -0,0 +1,23 @@ +import { VisualTestHelper } from '@test/visualTests/VisualTestHelper'; + +describe('SpecialNotesTests', () => { + it('tied-notes', async () => { + await VisualTestHelper.runVisualTest('features/special-notes/tied-notes.gp5'); + }); + + it('grace-notes', async () => { + await VisualTestHelper.runVisualTest('features/special-notes/grace-notes.gp5'); + }); + + it('grace-notes-advanced', async () => { + await VisualTestHelper.runVisualTest('features/special-notes/grace-notes-advanced.gp', undefined, [0, 1]); + }); + + it('dead-notes', async () => { + await VisualTestHelper.runVisualTest('features/special-notes/dead-notes.gp5'); + }); + + it('ghost-notes', async () => { + await VisualTestHelper.runVisualTest('features/special-notes/ghost-notes.gp5'); + }); +}); diff --git a/test/visualTests/features/SpecialTracks.test.ts b/test/visualTests/features/SpecialTracks.test.ts new file mode 100644 index 000000000..2bf78d9d5 --- /dev/null +++ b/test/visualTests/features/SpecialTracks.test.ts @@ -0,0 +1,11 @@ +import { VisualTestHelper } from '@test/visualTests/VisualTestHelper'; + +describe('SpecialTracksTests', () => { + it('drum-tabs', async () => { + await VisualTestHelper.runVisualTest('features/special-tracks/drum-tabs.gp5'); + }); + + it('grand-staff', async () => { + await VisualTestHelper.runVisualTest('features/special-tracks/grand-staff.gpx'); + }); +}); diff --git a/test/xml/XmlParse.test.ts b/test/xml/XmlParse.test.ts new file mode 100644 index 000000000..f7233df38 --- /dev/null +++ b/test/xml/XmlParse.test.ts @@ -0,0 +1,113 @@ +import { XmlDocument } from '@src/xml/XmlDocument'; +import { XmlNodeType } from '@src/xml/XmlNode'; +import { TestPlatform } from '@test/TestPlatform'; + +describe('XmlParseTest', () => { + it('parseSimple', () => { + let s: string = ''; + let xml: XmlDocument = new XmlDocument(s); + expect(xml.documentElement).toBeTruthy(); + expect(xml.documentElement!.localName).toEqual('root'); + expect(xml.documentElement!.childNodes.length).toEqual(0); + }); + + it('parseShorthand', () => { + let s: string = ''; + let xml: XmlDocument = new XmlDocument(s); + expect(xml.documentElement).toBeTruthy(); + expect(xml.documentElement!.localName).toEqual('root'); + expect(xml.documentElement!.childNodes.length).toEqual(0); + }); + + it('parseSingleAttribute', () => { + let s: string = ''; + let xml: XmlDocument = new XmlDocument(s); + expect(xml.documentElement).toBeTruthy(); + expect(xml.documentElement!.localName).toEqual('root'); + expect(xml.documentElement!.getAttribute('att')).toEqual('v'); + expect(xml.documentElement!.childNodes.length).toEqual(0); + }); + + it('parseMultipleAttributes', () => { + let s: string = ''; + let xml: XmlDocument = new XmlDocument(s); + expect(xml.documentElement).toBeTruthy(); + expect(xml.documentElement!.localName).toEqual('root'); + expect(xml.documentElement!.getAttribute('att')).toEqual('v'); + expect(xml.documentElement!.getAttribute('att2')).toEqual('v2'); + expect(xml.documentElement!.childNodes.length).toEqual(0); + }); + + it('parseSimpleText', () => { + let s: string = 'Text'; + let xml: XmlDocument = new XmlDocument(s); + expect(xml.documentElement).toBeTruthy(); + expect(xml.documentElement!.localName).toEqual('root'); + expect(xml.documentElement!.childNodes.length).toEqual(1); + expect(xml.documentElement!.childNodes[0].nodeType).toEqual(XmlNodeType.Text); + expect(xml.documentElement!.childNodes[0].value).toEqual('Text'); + }); + + it('parseChild', () => { + let s: string = ''; + let xml: XmlDocument = new XmlDocument(s); + expect(xml.documentElement).toBeTruthy(); + expect(xml.documentElement!.localName).toEqual('root'); + expect(xml.documentElement!.childNodes.length).toEqual(1); + expect(xml.documentElement!.childNodes[0].nodeType).toEqual(XmlNodeType.Element); + expect(xml.documentElement!.childNodes[0].localName).toEqual('cc'); + }); + + it('parseMultiChild', () => { + let s: string = ''; + let xml: XmlDocument = new XmlDocument(s); + expect(xml.documentElement).toBeTruthy(); + expect(xml.documentElement!.localName).toEqual('root'); + expect(xml.documentElement!.childNodes.length).toEqual(2); + expect(xml.documentElement!.childNodes[0].nodeType).toEqual(XmlNodeType.Element); + expect(xml.documentElement!.childNodes[0].localName).toEqual('cc'); + expect(xml.documentElement!.childNodes[1].nodeType).toEqual(XmlNodeType.Element); + expect(xml.documentElement!.childNodes[1].localName).toEqual('cc'); + }); + + it('parseComments', () => { + let s: string = 'value'; + let xml: XmlDocument = new XmlDocument(s); + expect(xml.documentElement).toBeTruthy(); + expect(xml.documentElement!.localName).toEqual('test'); + expect(xml.documentElement!.childNodes.length).toEqual(2); + expect(xml.documentElement!.childNodes[0].nodeType).toEqual(XmlNodeType.Element); + expect(xml.documentElement!.childNodes[0].localName).toEqual('cc'); + expect(xml.documentElement!.childNodes[0].getAttribute('c')).toEqual('d'); + expect(xml.documentElement!.childNodes[1].nodeType).toEqual(XmlNodeType.Element); + expect(xml.documentElement!.childNodes[1].localName).toEqual('cc'); + expect(xml.documentElement!.childNodes[1].childNodes.length).toEqual(1); + expect(xml.documentElement!.childNodes[1].childNodes[0].nodeType).toEqual(XmlNodeType.Text); + expect(xml.documentElement!.childNodes[1].childNodes[0].value).toEqual('value'); + }); + + it('parseDoctype', () => { + let s: string = ''; + let xml: XmlDocument = new XmlDocument(s); + expect(xml.documentElement).toBeTruthy(); + expect(xml.documentElement!.localName).toEqual('test'); + expect(xml.documentElement!.childNodes.length).toEqual(2); + expect(xml.documentElement!.childNodes[0].nodeType).toEqual(XmlNodeType.Element); + expect(xml.documentElement!.childNodes[0].localName).toEqual('cc'); + expect(xml.documentElement!.childNodes[1].nodeType).toEqual(XmlNodeType.Element); + expect(xml.documentElement!.childNodes[1].localName).toEqual('cc'); + }); + + it('parseXmlHeadTest', () => { + let s: string = ''; + let xml: XmlDocument = new XmlDocument(s); + expect(xml.documentElement).toBeTruthy(); + expect(xml.documentElement!.localName).toEqual('root'); + }); + + it('parseFull', async () => { + const s = await TestPlatform.loadFileAsString('test-data/xml/GPIF.xml'); + let xml: XmlDocument = new XmlDocument(s); + expect(xml.documentElement).toBeTruthy(); + }); +}); diff --git a/tsconfig.base.json b/tsconfig.base.json new file mode 100644 index 000000000..fb3744d17 --- /dev/null +++ b/tsconfig.base.json @@ -0,0 +1,48 @@ +{ + "compilerOptions": { + "moduleResolution": "node", + "target": "es5", + "module": "es6", + "lib": [ + "es2015", + "es2016", + "es2017", + "dom" + ], + "strict": true, + "allowSyntheticDefaultImports": true, + "strictNullChecks": true, + "strictPropertyInitialization": true, + "emitDecoratorMetadata": false, + "noImplicitAny": true, + "noImplicitThis": true, + "noImplicitReturns": true, + "noUnusedLocals": true, + "sourceMap": true, + "declaration": true, + "incremental": true, + "declarationDir": "dist/types", + "outDir": "dist/lib", + "baseUrl": ".", + "paths": { + "@src/*": [ + "src/*" + ] + }, + "typeRoots": [ + "types", + "node_modules/@types" + ], + "plugins": [ + { + "transform": "./src.compiler/JsonSerializationBuilder.ts" + }, + ] + }, + "include": [ + "src" + ], + "exclude": [ + "**/node_modules", + ] +} \ No newline at end of file diff --git a/tsconfig.build-csharp.json b/tsconfig.build-csharp.json new file mode 100644 index 000000000..71c1ec600 --- /dev/null +++ b/tsconfig.build-csharp.json @@ -0,0 +1,35 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "target": "es6", + "outDir": "dist/lib.csharp", + "module": "commonjs", + "declaration": false, + "declarationDir": null, + "sourceMap": false, + "incremental": false, + "noImplicitAny": false, + "noImplicitThis": false, + "noImplicitReturns": false, + "noUnusedLocals": false, + + "paths": { + "@src*": [ + "src*" + ], + "@test*": [ + "test*" + ] + }, + "plugins": [ + { + "transform": "./src.compiler/csharp/CSharpTranspilerPlugin.ts", + "after": true + }, + ] + }, + "include": [ + "src", + "test" + ] +} \ No newline at end of file diff --git a/tsconfig.build.json b/tsconfig.build.json new file mode 100644 index 000000000..b84c26ae2 --- /dev/null +++ b/tsconfig.build.json @@ -0,0 +1,3 @@ +{ + "extends": "./tsconfig.base.json" +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 000000000..46a31f0ed --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "outDir": "dist/lib.test", + "declarationDir": "dist/types.test", + "paths": { + "@src*": [ + "src*" + ], + "@test*": [ + "test*" + ] + } + }, + "include": [ + "src", + "test" + ] +} \ No newline at end of file diff --git a/tslint.json b/tslint.json new file mode 100644 index 000000000..5eba66cb8 --- /dev/null +++ b/tslint.json @@ -0,0 +1,7 @@ +{ + "extends": ["tslint-config-standard", "tslint-config-prettier"], + "rules": { + "one-variable-per-declaration": [true, "ignore-for-loop"], + "radix": false + } +}