Browse Source

Merge branch 'feature/VexFlowIntegration' into develop

Oliver 9 năm trước cách đây
mục cha
commit
f37a866d5c
100 tập tin đã thay đổi với 15141 bổ sung349 xóa
  1. 33 0
      .editorconfig
  2. 4 1
      .gitignore
  3. 2 1
      .travis.yml
  4. 78 73
      Gruntfile.js
  5. 2 1
      README.md
  6. 0 20
      build.js
  7. 31 0
      experiments/canvas_memory_test.html
  8. 91 10
      experiments/canvas_performance.html
  9. 138 0
      external/vexflow/vexflow.d.ts
  10. 181 0
      extras/macrogen.py
  11. 27 10
      karma.conf.js
  12. 58 56
      package.json
  13. 25 0
      src/Common/DataObjects/MusicSheetErrors.ts
  14. 19 0
      src/Common/DataObjects/PointF2D.ts
  15. 26 0
      src/Common/DataObjects/RectangleF2D.ts
  16. 9 0
      src/Common/DataObjects/SizeF2D.ts
  17. 68 51
      src/Common/DataObjects/fraction.ts
  18. 59 0
      src/Common/DataObjects/osmdColor.ts
  19. 74 64
      src/Common/DataObjects/pitch.ts
  20. 7 0
      src/Common/Enums/FontStyles.ts
  21. 4 0
      src/Common/Enums/Fonts.ts
  22. 11 0
      src/Common/Enums/TextAlignment.ts
  23. 55 0
      src/Common/FileIO/Mxl.ts
  24. 58 59
      src/Common/FileIO/Xml.ts
  25. 13 0
      src/Common/logging.ts
  26. 83 0
      src/MusicSheetAPI.ts
  27. 107 3
      src/MusicalScore/Calculation/MeasureSizeCalculator.ts
  28. 29 0
      src/MusicalScore/Exceptions.ts
  29. 16 0
      src/MusicalScore/Graphical/AbstractGraphicalInstruction.ts
  30. 79 0
      src/MusicalScore/Graphical/AccidentalCalculator.ts
  31. 566 0
      src/MusicalScore/Graphical/BoundingBox.ts
  32. 5 0
      src/MusicalScore/Graphical/Clickable.ts
  33. 61 0
      src/MusicalScore/Graphical/DrawingEnums.ts
  34. 26 0
      src/MusicalScore/Graphical/DrawingMode.ts
  35. 47 0
      src/MusicalScore/Graphical/DrawingParameters.ts
  36. 1123 0
      src/MusicalScore/Graphical/EngravingRules.ts
  37. 30 0
      src/MusicalScore/Graphical/GraphicalChordSymbolContainer.ts
  38. 9 0
      src/MusicalScore/Graphical/GraphicalComment.ts
  39. 96 0
      src/MusicalScore/Graphical/GraphicalLabel.ts
  40. 35 0
      src/MusicalScore/Graphical/GraphicalLine.ts
  41. 48 0
      src/MusicalScore/Graphical/GraphicalLyricEntry.ts
  42. 39 0
      src/MusicalScore/Graphical/GraphicalLyricWord.ts
  43. 15 0
      src/MusicalScore/Graphical/GraphicalMarkedArea.ts
  44. 70 0
      src/MusicalScore/Graphical/GraphicalMusicPage.ts
  45. 835 0
      src/MusicalScore/Graphical/GraphicalMusicSheet.ts
  46. 41 0
      src/MusicalScore/Graphical/GraphicalNote.ts
  47. 11 0
      src/MusicalScore/Graphical/GraphicalObject.ts
  48. 43 0
      src/MusicalScore/Graphical/GraphicalOctaveShift.ts
  49. 15 0
      src/MusicalScore/Graphical/GraphicalRectangle.ts
  50. 294 0
      src/MusicalScore/Graphical/GraphicalStaffEntry.ts
  51. 54 0
      src/MusicalScore/Graphical/GraphicalStaffEntryLink.ts
  52. 28 0
      src/MusicalScore/Graphical/GraphicalTie.ts
  53. 1518 0
      src/MusicalScore/Graphical/MusicSheetCalculator.ts
  54. 499 0
      src/MusicalScore/Graphical/MusicSheetDrawer.ts
  55. 87 0
      src/MusicalScore/Graphical/MusicSymbol.ts
  56. 432 0
      src/MusicalScore/Graphical/MusicSystem.ts
  57. 798 0
      src/MusicalScore/Graphical/MusicSystemBuilder.ts
  58. 13 0
      src/MusicalScore/Graphical/OctaveShiftParams.ts
  59. 68 0
      src/MusicalScore/Graphical/SelectionEndSymbol.ts
  60. 52 0
      src/MusicalScore/Graphical/SelectionStartSymbol.ts
  61. 110 0
      src/MusicalScore/Graphical/StaffLine.ts
  62. 17 0
      src/MusicalScore/Graphical/StaffLineActivitySymbol.ts
  63. 265 0
      src/MusicalScore/Graphical/StaffMeasure.ts
  64. 48 0
      src/MusicalScore/Graphical/SystemLine.ts
  65. 4 0
      src/MusicalScore/Graphical/SystemLinePosition.ts
  66. 9 0
      src/MusicalScore/Graphical/SystemLinesEnum.ts
  67. 65 0
      src/MusicalScore/Graphical/VerticalGraphicalStaffEntryContainer.ts
  68. 204 0
      src/MusicalScore/Graphical/VexFlow/VexFlowConverter.ts
  69. 46 0
      src/MusicalScore/Graphical/VexFlow/VexFlowGraphicalNote.ts
  70. 174 0
      src/MusicalScore/Graphical/VexFlow/VexFlowGraphicalSymbolFactory.ts
  71. 301 0
      src/MusicalScore/Graphical/VexFlow/VexFlowMeasure.ts
  72. 222 0
      src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetCalculator.ts
  73. 100 0
      src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetDrawer.ts
  74. 68 0
      src/MusicalScore/Graphical/VexFlow/VexFlowMusicSystem.ts
  75. 15 0
      src/MusicalScore/Graphical/VexFlow/VexFlowStaffEntry.ts
  76. 10 0
      src/MusicalScore/Graphical/VexFlow/VexFlowStaffLine.ts
  77. 20 0
      src/MusicalScore/Graphical/VexFlow/VexFlowTextMeasurer.ts
  78. 241 0
      src/MusicalScore/Instrument.ts
  79. 28 0
      src/MusicalScore/InstrumentalGroup.ts
  80. 33 0
      src/MusicalScore/Interfaces/IGraphicalSymbolFactory.ts
  81. 8 0
      src/MusicalScore/Interfaces/IQualityFeedbackTone.ts
  82. 6 0
      src/MusicalScore/Interfaces/ITextMeasurer.ts
  83. 13 0
      src/MusicalScore/Interfaces/ITextTranslation.ts
  84. 8 0
      src/MusicalScore/Interfaces/ITransposeCalculator.ts
  85. 21 0
      src/MusicalScore/Label.ts
  86. 120 0
      src/MusicalScore/MusicParts/MusicPartManager.ts
  87. 536 0
      src/MusicalScore/MusicParts/MusicPartManagerIterator.ts
  88. 496 0
      src/MusicalScore/MusicSheet.ts
  89. 56 0
      src/MusicalScore/MusicSource/MappingSourceMusicPart.ts
  90. 30 0
      src/MusicalScore/MusicSource/PartListEntry.ts
  91. 168 0
      src/MusicalScore/MusicSource/Repetition.ts
  92. 43 0
      src/MusicalScore/MusicSource/SourceMusicPart.ts
  93. 1015 0
      src/MusicalScore/ScoreIO/InstrumentReader.ts
  94. 833 0
      src/MusicalScore/ScoreIO/MusicSheetReader.ts
  95. 865 0
      src/MusicalScore/ScoreIO/VoiceGenerator.ts
  96. 114 0
      src/MusicalScore/SubInstrument.ts
  97. 36 0
      src/MusicalScore/VoiceData/Beam.ts
  98. 246 0
      src/MusicalScore/VoiceData/ChordSymbolContainer.ts
  99. 76 0
      src/MusicalScore/VoiceData/Expressions/ContinuousExpressions/Slur.ts
  100. 126 0
      src/MusicalScore/VoiceData/Expressions/ContinuousExpressions/continuousDynamicExpression.ts

+ 33 - 0
.editorconfig

@@ -0,0 +1,33 @@
+# EditorConfig is awesome: http://EditorConfig.org
+
+# top-most EditorConfig file
+root = true
+
+# Unix-style newlines with a newline ending every file
+[*]
+end_of_line = lf
+insert_final_newline = true
+
+# Matches multiple files with brace expansion notation
+# Set default charset
+[*.{js,py,ts}]
+charset = utf-8
+
+# 4 space indentation
+[*.py,ts]
+indent_style = space
+indent_size = 4
+
+# Tab indentation (no size specified)
+[Makefile]
+indent_style = tab
+
+# Indentation override for all JS under lib directory
+[lib/**.js]
+indent_style = space
+indent_size = 2
+
+# Matches the exact files either package.json or .travis.yml
+[{package.json,.travis.yml}]
+indent_style = space
+indent_size = 2

+ 4 - 1
.gitignore

@@ -31,7 +31,9 @@ build/Release
 
 # Dependency directory
 # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git
-node_modules
+node_modules/*
+!node_modules/karma-musicxml2js-preprocessor/
+
 
 # Optional npm cache directory
 .npm
@@ -50,3 +52,4 @@ typings
 .vscode/
 .idea/encodings.xml
 *.md
+node_modules/

+ 2 - 1
.travis.yml

@@ -3,7 +3,8 @@ language: node_js
 node_js:
   - "5"
 before_script:
-  - npm install
   - npm start
 notifications:
   email: false
+  slack:
+    secure: ZLC7oJ5BcdVfLX+R7Bg6NUaFrrOG5heX9t8lpCZAWtHG4Uag/z5hCAtr/pfdaoEZ4AFJ7SS0yubE3EltwoXdx/zeGlF7gV5JxjDtDyNpkqFa38XTSorP/0FYjaahecFnxUYG2oNQWTcnyeE6BMak+RQ4+ciLC1dQrzC84FNE4R28tV5SVwgM+O1JAFg67Z2Xu497tNuLG6aptyRAov6G0mo9e1oLW4apuiV4CnV+p2nMYbLEyHT5TJiQ8/c7ar7jM7Ia8bL6WGHGjOmEmy71DyWWQXBlE+RSS8uBRlF7BvGX7/fleCUa4jE5ieP+IKCENfa+9+SCE6i8YEAc8Wyfqdl/f5A7NqPDNYxWxU1w8iSM4/FJn6hJKJ3vnogAdQUlPtNYssMio2U06bkvtZ+hu961f6qcGaR10fcX8EHi1UwFDHQ+9uha+9U5vF/+EQHXAG5WGSKrpbH3CFypdJ8g3U1eW5qJn76W9Um4COSj26KI+pBTD9gZwaZCmDas0g2bECIClUKK4y1utsYf/KiJcJaIOEE+QvFNyhuXwdAmTFi8OZ784yrbXmpQZqol2ckgfvWNQZnwqY8h3A8RDhXxvbv6UbNOfE8p/BgJCRaSZAkaqU7b9+D0kSaNIWVPbPad3+Plgkg/gvyC07l8GR5+9tMysz669VQXUs2vzIMIzfA=

+ 78 - 73
Gruntfile.js

@@ -1,14 +1,27 @@
+/*global module*/
 module.exports = function (grunt) {
     'use strict';
-    /*var BANNER = '**\n' +
-        ' * Open Sheet Music Display library <%= pkg.version %> built on <%= grunt.template.today("yyyy-mm-dd") %>.\n' +
+    // The banner on top of the build
+    var banner = '/**\n' +
+        ' * Open Sheet Music Display <%= pkg.version %> built on <%= grunt.template.today("yyyy-mm-dd") %>.\n' +
         ' * Copyright (c) 2016 PhonicScore\n' +
         ' *\n' +
         ' * https://github.com/opensheetmusicdisplay/opensheetmusicdisplay\n' +
-        ' *\n';
-    */
+        ' */\n',
+        typings = [
+            'typings/index.d.ts',
+            // Additional manual typings:
+            'external/vexflow/vexflow.d.ts'
+            // 'typings/fft.d.ts'
+        ],
+    // Paths
+        src = ['src/**/*.ts'],
+        test = ['test/**/*.ts'];
+
+    // Grunt configuration following:
     grunt.initConfig({
         pkg: grunt.file.readJSON('package.json'),
+        banner: '',
         // Build output directories
         outputDir: {
             build: 'build',
@@ -17,84 +30,67 @@ module.exports = function (grunt) {
         // Browserify
         browserify: {
             dist: {
-                src: [
-                    'typings/browser.d.ts',
-                    'typings/vexflow.d.ts',
-                    'src/**/*.ts'
-                ],
-                dest: '<%= outputDir.build %>/osmd.js'
+                src: [].concat(typings, src),
+                dest: '<%= outputDir.build %>/osmd.js',
+                options: {
+                    banner: "<%= banner %>"
+                }
             },
             debug: {
-                src: [
-                    'typings/browser.d.ts', 'typings/vexflow.d.ts',
-                    'src/**/*.ts', 'test/**/*.ts'
-                ],
+                src: [].concat(typings, src, test),
                 dest: '<%= outputDir.build %>/osmd-debug.js',
                 options: {
+                    banner: "<%= banner %>",
                     browserifyOptions: {
                         debug: true
                     }
                 }
             },
             options: {
-                plugin: ['tsify']
+                plugin: ['tsify']//,
+                // browserifyOptions: {
+                //     standalone: 'MeasureSizeCalculator'
+                // }
             }
         },
         // Uglify
-        /*uglify: {
-          options: {
-            compress: {
-              drop_console: true
-            }
-          },
-          my_target: {
-            files: {
-              'build/osmd.js': ['src/input.js']
+        uglify: {
+            options: {
+                compress: {
+                    drop_console: true
+                },
+                banner: banner,
+                mangle: true,
+                mangleProperties: true,
+                preserveComments: 'all'
+            },
+            bundle: {
+                files: {
+                    'build/osmd.min.js': ['build/osmd.js']
+                }
             }
-          }
-        },*/
-        // Settings for tests
+        },
+        // Karma setup
         karma: {
             // For continuous integration
             ci: {
                 configFile: 'karma.conf.js',
                 options: {
-                    browsers: ['PhantomJS'],
-                    files: [
-                        '<%= browserify.debug.dest %>'
-                    ]
+                    browsers: ['PhantomJS']
                 }
             },
             debugWithFirefox: {
                 configFile: 'karma.conf.js',
                 options: {
                     singleRun: false,
-                    browsers: ['Firefox'],
-                    files: [
-                        '<%= browserify.debug.dest %>', {
-                            pattern: 'src/**/*.ts',
-                            included: false
-                        }, {
-                            pattern: 'test/**/*.ts',
-                            included: false
-                        }
-                    ]
+                    browsers: ['Firefox']
                 }
             },
             debugWithChrome: {
                 configFile: 'karma.conf.js',
                 options: {
                     singleRun: false,
-                    browsers: ['Chrome'],
-                    files: [
-                        '<%= browserify.debug.dest %>', {
-                            pattern: 'src/**/*.ts',
-                            included: false
-                        }, {
-                            pattern: 'test/**/*.ts',
-                            included: false
-                        }
-                    ]
+                    browsers: ['Chrome']
                 }
             }
         },
@@ -104,21 +100,21 @@ module.exports = function (grunt) {
                 configuration: 'tslint.json'
             },
             all: {
-                src: ['<%= browserify.dist.src %>', '<%= browserify.debug.src %>']
+                src: [].concat(src, test)
             }
         },
-        // TypeScript type definitions
+        // JsHint setup
+        jshint: {
+            all: [
+                'Gruntfile.js', 'karma.conf.js',
+                'submodules/**/*.json', 'submodules/**/*.js'
+            ]
+        },
+        // TypeScript Type Definitions
         typings: {
             install: {}
         },
-        docco: {
-            src: ['src/**/*.ts'],
-            options: {
-                layout: 'linear',
-                output: 'build/docs'
-            }
-        },
-        // Settings for clean task
+        // Cleaning task setup
         clean: {
             options: {
                 force: true
@@ -127,28 +123,37 @@ module.exports = function (grunt) {
                 src: [
                     '<%= outputDir.build %>',
                     '<%= outputDir.dist %>',
-                    '.tscache'
+                    'node-modules',
+                    'typings',
+                    '.tscache',
+                    'src/**/*.js', 'test/**/*.js'
                 ]
             }
         }
     });
 
     // Load Npm tasks
-    grunt.loadNpmTasks('grunt-browserify');
-    grunt.loadNpmTasks('grunt-contrib-clean');
-    grunt.loadNpmTasks('grunt-contrib-watch');
-    //grunt.loadNpmTasks('grunt-jscs');
     grunt.loadNpmTasks('grunt-karma');
     grunt.loadNpmTasks('grunt-tslint');
     grunt.loadNpmTasks('grunt-typings');
+    grunt.loadNpmTasks('grunt-browserify');
+    grunt.loadNpmTasks('grunt-contrib-clean');
+    grunt.loadNpmTasks('grunt-contrib-watch');
+    grunt.loadNpmTasks('grunt-contrib-jshint');
+    grunt.loadNpmTasks('grunt-contrib-uglify');
 
     // Register tasks
-    grunt.registerTask('default', [ /*'tslint',*/ 'browserify', 'karma:ci']);
-    //grunt.registerTask('lint', ['tslint', 'jscs']);
-    grunt.registerTask('test', ['browserify:debug', 'karma:ci']);
-    //grunt.registerTask('test debug Firefox', ['browserify:debug', 'karma:debugWithFirefox']);
-    //grunt.registerTask('test debug Chrome', ['browserify:debug', 'karma:debugWithChrome']);
+    grunt.registerTask('all',     ['typings', 'default']);
+    grunt.registerTask('start',   ['typings']);
+    grunt.registerTask('default', ['browserify', 'lint', 'karma:ci', 'uglify']);
+    grunt.registerTask('npmtest', ['typings', 'test']);
+    grunt.registerTask('test',    ['browserify:debug', 'lint', 'karma:ci']);
+    grunt.registerTask('fasttest', ['browserify:debug', 'karma:ci']);
     grunt.registerTask('rebuild', ['clean', 'default']);
-    grunt.registerTask('publish', ['clean', 'browserify:dist']);
-    grunt.registerTask('all', ['typings', 'default']);
+    grunt.registerTask('publish', ['clean', 'typings', 'browserify:dist', 'uglify:bundle']);
+    grunt.registerTask('lint',    ['jshint', 'tslint']);
+
+    // Fix these in the future:
+    // grunt.registerTask('test debug Firefox', ['browserify:debug', 'karma:debugWithFirefox']);
+    // grunt.registerTask('test debug Chrome', ['browserify:debug', 'karma:debugWithChrome']);
 };

+ 2 - 1
README.md

@@ -2,9 +2,10 @@
   <img alt="OSMD logo" src="http://opensheetmusicdisplay.org/wp-content/uploads/2015/03/OSMD_3_icon.png" width="200"/>
 </p>
 
-# Open Sheet Music Display library
+# Open Sheet Music Display
 
 [![Build Status](https://travis-ci.org/opensheetmusicdisplay/opensheetmusicdisplay.svg?branch=master)](https://travis-ci.org/opensheetmusicdisplay/opensheetmusicdisplay)
+[![Dependency Status](https://david-dm.org/opensheetmusicdisplay/opensheetmusicdisplay.png)](https://david-dm.org/opensheetmusicdisplay/opensheetmusicdisplay)
 
 * Website: [opensheetmusicdisplay.org](http://opensheetmusicdisplay.org)
 * How to build: [wiki/Build-Instructions](https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/wiki/Build-Instructions)

+ 0 - 20
build.js

@@ -1,20 +0,0 @@
-#!/usr/bin/env node
-
-var fs = require("fs");
-var path = require("path");
-var browserify = require("browserify");
-var args = require('minimist')(process.argv.slice(2));
-
-var b = browserify({ debug: args.debug || false });
-
-var path_to_vexflow = args.path || path.join(__dirname, "node_modules/vexflow/releases/");
-
-console.log("Path to vexflow: " + path_to_vexflow);
-
-if (args["include-vexflow"]) {
-	b.add(path.join(path_to_vexflow, 'vexflow-debug.js'));
-}
-
-b.add('./src/file1.js');
-
-b.bundle().pipe(fs.createWriteStream("osmd.js"));

+ 31 - 0
experiments/canvas_memory_test.html

@@ -0,0 +1,31 @@
+<!--This file executes some benchmarks with HTML Canvas and VexFlow rendering-->
+<!DOCTYPE html><html><head><meta charset="UTF-8"><script defer="defer">(function(){
+//----------------------------------------------------------------------------->
+
+window.onload = function() {
+  window.title = "[OSMD] Canvas Memory Test"
+  window.setTimeout(test2, 1);
+}
+
+var test1 = function() {
+  var canvas = document.createElement("canvas");
+  canvas.style.border = "1px solid red";
+  canvas.width = 1920;
+  canvas.height = 1080 * 30;
+  document.body.appendChild(canvas);
+}
+
+var test2 = function() {
+  var canvas = document.createElement("canvas");
+  canvas.style.border = "1px solid red";
+  canvas.width = 1920;
+  canvas.height = 1080 * 30;
+  var ctx = canvas.getContext("2d");
+  ctx.fillStyle = "red";
+  ctx.fillRect(0, 0, canvas.width, canvas.height);
+  document.body.appendChild(canvas);
+}
+
+//----------------------------------------------------------------------------->
+}());</script><!--------------------------------------------------------------->
+</head></html><!--------------------------------------------------------------->

+ 91 - 10
experiments/canvas_performance.html

@@ -1,18 +1,13 @@
 <!--This file executes some benchmarks with HTML Canvas and VexFlow rendering-->
-<!doctype html><html><head><script defer="defer">(function(){//---------------->
+<!DOCTYPE html><html><head><meta charset="UTF-8"><script defer="defer">(function(){
 //----------------------------------------------------------------------------->
 
 // TODO
 // * More complex VexFlow systems
 
-// Init function
-window.onload = function () {
-  window.setTimeout(start, 0);
-}
-
 var start = function () {
   document.title = "HTML Canvas Performance Tests";
-  document.write("<h1>[OSMD] HTML Canvas Performance Tests</h1>");
+  write("<h1>[OSMD] HTML Canvas Performance Tests</h1>");
   var t = timer();
   // Run the tests
   repeat(
@@ -327,11 +322,91 @@ var start = function () {
       tie2.setContext(ctx).draw();
     }
   );
+  var tmp_c1 = document.createElement("canvas");
+  tmp_c1.width = 1920;
+  tmp_c1.height = 1080 * 30;
+  repeat(
+    "[VexFlow | huge] Format & draw 20 staves with 5 voices each, reusing the canvas.", 50,
+    "...",
+    function(){
+      var canvas = tmp_c1;
+      var renderer = new Vex.Flow.Renderer(canvas, Vex.Flow.Renderer.Backends.CANVAS);
+      var ctx = renderer.getContext();
+      for (var j=0; j < 20; j++) {
+        var stave = new Vex.Flow.Stave(
+          Math.floor(Math.random()*canvas.width),
+          Math.floor(Math.random()*canvas.height),
+          Math.floor(Math.random()*canvas.width)
+        );
+        stave.addClef("treble");
+        stave.setContext(ctx).draw();
+        var voices = [];
+        for (var i = 0, voice; i < 5; i++) {
+          voice = new Vex.Flow.Voice({
+            num_beats: 4,
+            beat_value: 4,
+            resolution: Vex.Flow.RESOLUTION
+          });
+          voice.addTickables(randomNotes());
+          voices.push(voice);
+        }
+        var formatter = new Vex.Flow.Formatter().
+        joinVoices(voices).format(voices, 500);
+        for (var i = 0; i < 5; i++) {
+          voices[i].draw(ctx, stave);
+        }
+      }
+    }
+  );
+
+  var tmp_c1 = document.createElement("canvas");
+  tmp_c1.width = 1920;
+  tmp_c1.height = 100;
+  repeat(
+    "[VexFlow | normal] Format & draw 20 staves with 5 voices each, reusing the canvas.", 50,
+    "...",
+    function(){
+      var canvas = tmp_c1;
+      var renderer = new Vex.Flow.Renderer(canvas, Vex.Flow.Renderer.Backends.CANVAS);
+      var ctx = renderer.getContext();
+      for (var j=0; j < 20; j++) {
+        var stave = new Vex.Flow.Stave(
+          Math.floor(Math.random()*canvas.width),
+          Math.floor(Math.random()*canvas.height),
+          Math.floor(Math.random()*canvas.width)
+        );
+        stave.addClef("treble");
+        stave.setContext(ctx).draw();
+        var voices = [];
+        for (var i = 0, voice; i < 5; i++) {
+          voice = new Vex.Flow.Voice({
+            num_beats: 4,
+            beat_value: 4,
+            resolution: Vex.Flow.RESOLUTION
+          });
+          voice.addTickables(randomNotes());
+          voices.push(voice);
+        }
+        var formatter = new Vex.Flow.Formatter().
+        joinVoices(voices).format(voices, 500);
+        for (var i = 0; i < 5; i++) {
+          voices[i].draw(ctx, stave);
+        }
+      }
+    }
+  );
+
   finish(t);
 }
 
 //----------------------------------------------------------------------------->
 // Helper functions
+
+// Init function
+window.onload = function () {
+  window.setTimeout(start, 0);
+}
+
 var randomNotes = function () {
   var notes = [];
   for (var i = 0, note, key; i < 8; i ++) {
@@ -346,7 +421,7 @@ var randomNotes = function () {
 //----------------------------------------------------------------------------->
 var finish = function (t) {
   window.setTimeout(function(){
-    document.write("<hr><p><b>Total Elapsed Time: ~" + Math.floor(t()/1000) + " seconds.</b></p>");
+    write("<hr><p><b>Total Elapsed Time: ~" + Math.floor(t()/1000) + " seconds.</b></p>");
     document.close();
   }, 0);
 }
@@ -354,8 +429,8 @@ var finish = function (t) {
 
 // Display the result of a test on the page
 function formatResult(label, descr, times, elapsed) {
-  document.write("<p><b>" + label + "</b>: " + descr + "<br>");
-  document.write("<i>Elapsed time: " + (elapsed/times) + "ms each (" + times + " iterations)</i></Op>");
+  write("<p><b>" + label + "</b>: " + descr + "<br>");
+  write("<i>Elapsed time: " + (elapsed/times) + "ms each (" + times + " iterations)</i></Op>");
 }
 
 // A simple timer
@@ -368,6 +443,7 @@ var timer = function() {
 
 // Repeat a test
 var repeat0 = function(label, times, descr, func) {
+  //debugger;
   var t = timer();
   for (var i = 0; i < times; i++) func();
   formatResult(label, descr, times, t());
@@ -378,6 +454,11 @@ var repeat = function(label, times, descr, func) {
   }, 0);
 }
 
+var write = function(x) {
+  var div = document.createElement("div");
+  div.innerHTML = x;
+  document.body.appendChild(div);
+}
 //----------------------------------------------------------------------------->
 }());</script><!--------------------------------------------------------------->
 <script src="../node_modules/vexflow/releases/vexflow-min.js"></script><!------>

+ 138 - 0
external/vexflow/vexflow.d.ts

@@ -0,0 +1,138 @@
+declare namespace Vex {
+  export module Flow {
+    const RESOLUTION: any;
+
+    export class Formatter {
+      constructor();
+
+      public hasMinTotalWidth: boolean;
+      public minTotalWidth: number;
+      public joinVoices(voices: Voice[]): void;
+      public format(voices: Voice[], width: number): void;
+      public preCalculateMinTotalWidth(voices: Voice[]): number;
+    }
+
+    export class BoundingBox {
+      constructor(x: number, y: number, w: number, h: number);
+
+      public mergeWith(bb: BoundingBox): BoundingBox;
+      public getX(): number;
+      public getY(): number;
+      public getW(): number;
+      public getH(): number;
+    }
+
+    export class Voice {
+      constructor(time: any);
+      public static Mode: any;
+
+      public getBoundingBox(): BoundingBox;
+      public setStave(stave: Stave): Voice;
+      public addTickables(notes: StaveNote[]): Voice;
+      public addTickable(note: StaveNote): Voice;
+      public setMode(mode: any): Voice;
+      public draw(ctx: any, stave: Stave): void;
+    }
+
+    export class StaveNote {
+      constructor(note_struct: any);
+
+      public getNoteHeadBounds(): any;
+      public getNoteHeadBeginX(): number;
+      public getNoteHeadEndX(): number;
+      public addAccidental(index: number, accidental: Accidental): StaveNote;
+    }
+
+    export class Stave {
+      constructor(x: number, y: number, width: number);
+
+      public x: number;
+      public start_x: number;
+      public end_x: number;
+
+      public setX(x: number): Stave;
+      public setY(y: number): Stave;
+      public addClef(clefSpec: string, size: any, annotation: any, position: any): void;
+      public setEndClef(clefSpec: string, size: any, annotation: any): void;
+      public getModifiers(): StaveModifier[];
+      public getYForGlyphs(): number;
+      public getWidth(): number;
+      public setWidth(width: number): Stave;
+      public getNoteStartX(): number;
+      public getNoteEndX(): number;
+      public format(): void;
+      public getSpacingBetweenLines(): number;
+      public getNumLines(): number;
+      public getLineForY(y: number): number;
+      public getModifiers(pos: any, cat: any): Clef[]; // FIXME
+      public setContext(ctx: CanvasContext): Stave;
+      public addModifier(mod: any, pos: any): void;
+      public draw(): void;
+      public addTimeSignature(sig: string): void;
+    }
+
+    export class Modifier {
+      public static Position: any;
+      public getCategory(): string;
+      public getWidth(): number;
+      public getPadding(index: number): number;
+    }
+
+    export class StaveModifier extends Modifier {}
+
+    export class Clef extends StaveModifier {
+      constructor(type: any);
+
+      public static category: string;
+      public static types: { [type: string]: any; } ;
+      public glyph: any;
+      public x: number;
+      public stave: Stave;
+
+      public getBoundingBox(): BoundingBox;
+      public setStave(stave: Stave): void;
+    }
+
+    export class Renderer {
+      constructor(canvas: HTMLCanvasElement, backend: any);
+
+      public static Backends: any;
+      public resize(a: number, b: number): void;
+      public getContext(): CanvasContext;
+    }
+
+    export class TimeSignature {
+      constructor(timeSpec: string, customPadding?: any);
+    }
+    export class KeySignature {
+      constructor(keySpec: string, cancelKeySpec: string, alterKeySpec?: string);
+    }
+
+    export class Accidental {
+      constructor(type: string);
+    }
+
+    export class Beam {
+      constructor(notes: StaveNote[], auto_stem: boolean);
+      public setContext(ctx: CanvasContext): Beam;
+      public draw(): void;
+    }
+
+    export class CanvasContext {
+      public scale(x: number, y: number): CanvasContext;
+    }
+
+    export class StaveConnector {
+      constructor(top: Stave, bottom: Stave);
+      public static type: any;
+      public setType(type: any): StaveConnector;
+      public setContext(ctx: CanvasContext): StaveConnector;
+      public draw(): void;
+    }
+
+  }
+}
+
+declare module "vexflow" {
+    export = Vex;
+}

+ 181 - 0
extras/macrogen.py

@@ -0,0 +1,181 @@
+import xml.etree.ElementTree as ET
+import re, sys, os
+
+replace = (
+    ("Math\\.Max\\(", "Math.max("),
+    ("Math\\.Min\\(", "Math.min("),
+    ("Math\\.Sqrt\\(", "Math.sqrt("),
+    ("Math\\.Abs\\(", "Math.abs("),
+    ("Math\\.Pow\\(", "Math.pow("),
+    ("Math\\.Ceiling", "Math.ceil"),
+    ("Math\\.Floor", "Math.floor"),
+
+    ("var ", "let "),
+
+    # Lists --> Arrays
+    ("new List<List<([a-zA-Z]*)>>\(\)", "[]"),
+    ("List<List<([a-zA-Z]*)>>", "$1[][]"),
+    ("new List<([a-zA-Z]*)>\(\)", "[]"),
+    ("List<([a-zA-Z]*)>", "$1[]"),
+
+    # Dictionaries:
+    ("new Dictionary<number, ([a-zA-Z0-9\\[\\]\\.]*)>\\(\\)", "{}"),
+    ("Dictionary<number, ([a-zA-Z0-9\\[\\]\\.]*)>", "{ [_: number]: $1; }"),
+    ("new Dictionary<string, ([a-zA-Z0-9\\[\\]\\.]*)>\\(\\)", "{}"),
+    ("Dictionary<string, ([a-zA-Z0-9\\[\\]\\.]*)>", "{ [_: string]: $1; }"),
+
+    ("IEnumerable<([a-zA-Z0-9]+)>", "$1[]"),
+
+    ("\\.Count", ".length"),
+    ("\\.Length", ".length"),
+    ("\\.Add\(", ".push("),
+    ("\\.First\(\)", "[0]"),
+
+    ("\\.Insert\((.*), (.*)\)", ".splice($1, 0, $2)"),
+    ("\\.RemoveAt\(([a-z|0-9]+)\)", ".splice($1, 1)"),
+    ("\\.Clear\(\);", ".length = 0;"),
+    ("\\.IndexOf", ".indexOf"),
+    ("\\.ToArray\\(\\)", ""),
+    ("\\.ContainsKey", ".hasOwnProperty"),
+
+    ("\\.Contains\(([a-zA-Z0-9.]+)\)", ".indexOf($1) !== -1"),
+
+    ("for each(?:[ ]*)\(([a-z|0-9]+) ([a-z|0-9]+) in ([a-z|0-9]+)\)", "for ($2 of $3)"),
+    (", len([0-9]*) = ", ", len$1: number = "),
+
+    (" == ", " === "),
+    (" != ", " !== "),
+    ("null", "undefined"),
+
+    ("\\.ToLower\(\)", ".toLowerCase()"),
+
+    ("Logger\\.DefaultLogger\\.LogError\(LogLevel\\.DEBUG,(?:[ ]*)", "Logging.debug("),
+    ("Logger\\.DefaultLogger\\.LogError\(LogLevel\\.NORMAL,(?:[ ]*)", "Logging.log("),
+    ("Logger\\.DefaultLogger\\.LogError\(PhonicScore\\.Common\\.Enums\\.LogLevel\\.NORMAL,(?:[ ]*)", "Logging.log("),
+
+    ("Fraction\\.CreateFractionFromFraction\(([a-z|0-9]+)\)", "$1.clone()"),
+
+    ("(\d{1})f([,;)\]} ])", "$1$2"),
+    ("number\\.MaxValue", "Number.MAX_VALUE"),
+    ("Int32\\.MaxValue", "Number.MAX_VALUE"),
+    ("number\\.MinValue", "Number.MIN_VALUE"),
+
+    ("__as__<([A-Za-z|0-9]+)>\(([A-Za-z|0-9.]+), ([A-Za-z|0-9]+)\)", "($2 as $3)"),
+
+    ("new Dictionary<number, number>\(\)", "{}"),
+    (": Dictionary<number, number>", ": {[_: number]: number; }"),
+
+    ("String\\.Empty", '""'),
+    ("return\\n", "return;\n"),
+
+    ("}(\n[ ]*)else ", "} else "),
+
+    ("\\.IdInMusicSheet", ".idInMusicSheet"),
+    ("F_2D", "F2D"),
+    ("List<Tuple<Object\\[\\], Object>>", "[Object[], Object][]")
+)
+
+def checkForIssues(filename, content):
+    if ".Last()" in content:
+        print("      !!! Warning: .Last() found !!!")
+
+def applyAll():
+    root = sys.argv[1]
+    filenames = []
+    recurse(root, filenames)
+    # if os.path.isdir(root):
+    #     recurse(root, filenames)
+    # else:
+    #     filenames.append(root)
+
+    print("Apply replacements to:")
+    for filename in filenames:
+        print("  >>> " + os.path.basename(filename))
+        content = None
+        with open(filename) as f:
+            content = f.read()
+        checkForIssues(filename, content)
+        for rep in replace:
+            content = re.sub(rep[0], pythonic(rep[1]), content)
+        with open(filename, "w") as f:
+            f.write(content)
+    print("Done.")
+
+def recurse(folder, files):
+    if os.path.isfile(folder):
+        files.append(folder)
+    if os.path.isdir(folder):
+        for i in os.listdir(folder):
+            recurse(os.path.join(folder, i), files)
+
+def keycode(c):
+    if len(c) > 1:
+        return ";".join(keycode(i) for i in c)
+    if c.isalpha():
+        return str(ord(c.upper())) + ":" + ("1" if c.isupper() else "0")
+    if c.isdigit():
+        return str(48 + int(c)) + ":0"
+    return {'!': '49:1', '#': '51:1', '%': '53:1', '$': '52:1', "'": '222:0', '&': '55:1', ')': '48:1', '(': '57:1', '+': '61:1', '*': '56:1', '-': '45:0', ',': '44:0', '/': '47:0', '.': '46:0', ';': '59:0', ':': '59:1', '=': '61:0', '@': '50:1', '[': '91:0', ']': '93:0', '\\': '92:0', '_': '45:1', '^': '54:1', 'a': '65:0', '<': '44:1', '>': '46', ' ': "32:0", "|": "92:1", "?": "47:1", "{": "91:1", "}": "93:1"}[c]
+
+def escape(s):
+    return s
+    return s.replace("&", "&amp;").replace(">", "&gt;").replace("<", "&lt;")
+
+def generate():
+    N = 0
+    macroName = "TypeScript'ing Replacements"
+    macro = ET.Element("macro")
+    macro.set("name", macroName)
+    replace = (("ABCDEfGHIJKL", "ABCDEfGHIJKL"),) + replace
+    for rep in replace:
+        N += 1
+        # result.append('<macro name="%d">' % N)
+        ET.SubElement(macro, "action").set("id", "$SelectAll")
+        ET.SubElement(macro, "action").set("id", "Replace")
+        for s in rep[0]:
+            t = ET.SubElement(macro, "typing")
+            t.set("text-keycode", keycode(s))
+            t.text = escape(s)
+        ET.SubElement(macro, "shortuct").set("text", "TAB")
+        for s in rep[1]:
+            t = ET.SubElement(macro, "typing")
+            t.set("text-keycode", keycode(s))
+            t.text = escape(s)
+        # result.append('<action id="EditorEnter" />' * 50)
+        for i in range(50):
+            ET.SubElement(macro, "shortuct").set("text", "ENTER")
+        ET.SubElement(macro, "shortuct").set("text", "ESCAPE")
+
+
+    path = '/Users/acondolu/Library/Preferences/WebStorm11/options/macros.xml'
+    tree = None
+    try:
+        tree = ET.parse(path)
+    except IOError:
+        print("Cannot find macro file.")
+        sys.exit(1)
+    component = tree.getroot().find("component")
+    assert component.get("name") == "ActionMacroManager"
+    found = None
+    for m in component.iter("macro"):
+        if m.get("name") == macroName:
+            found = m
+            break
+    if found is not None:
+        component.remove(found)
+
+    component.append(macro)
+
+    tree.write(path)
+    print("Macro written on " + path)
+
+def pythonic(s):
+    return s.replace("$", "\\")
+
+if __name__ == "__main__":
+    if len(sys.argv) != 2:
+        print("Usage: python macrogen.py path/to/files")
+        sys.exit(1)
+        # generate()
+    else:
+        applyAll()

+ 27 - 10
karma.conf.js

@@ -1,9 +1,9 @@
 // Karma configuration
 // Generated on Fri Feb 05 2016 12:36:08 GMT+0100 (CET)
-
-module.exports = function(config) {
+/*globals module*/
+module.exports = function (config) {
+    'use strict';
     config.set({
-
         // base path that will be used to resolve all patterns (eg. files, exclude)
         basePath: '',
 
@@ -11,18 +11,35 @@ module.exports = function(config) {
         // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
         frameworks: ['mocha', 'chai'],
 
-        // list of files / patterns to load in the browser
-        files: [
-          'build/**/*.js'
-        ],
-
         // list of files to exclude
-        exclude: [
-        ],
+        exclude: [],
+
+        files: [{
+            pattern: 'build/osmd-debug.js'
+        }, {
+            pattern: 'src/**/*.ts',
+            included: false
+        }, {
+            pattern: 'test/**/*.ts',
+            included: false
+        }, {
+            pattern: 'test/data/*.xml',
+            included: true
+        }, {
+            pattern: 'test/data/*.mxl.base64',
+            included: true
+        }, {
+            pattern: 'test/data/*.mxl',
+            included: false,
+            watched: false,
+            served: true
+        }],
 
         // preprocess matching files before serving them to the browser
         // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
         preprocessors: {
+            'test/data/*.xml': ['xml2js'],
+            'test/data/*.mxl.base64': ['base64-to-js']
         },
 
         // test results reporter to use

+ 58 - 56
package.json

@@ -1,58 +1,60 @@
 {
-    "name": "opensheetmusicdisplay",
-    "version": "0.0.0",
-    "description": "Open Sheet Music Display library",
-    "main": "build.js",
-    "directories": {
-        "doc": "docs",
-        "test": "test"
-    },
-    "scripts": {
-        "start": "grunt typings",
-        "test": "grunt test"
-    },
-    "repository": {
-        "type": "git",
-        "url": "https://github.com/opensheetmusicdisplay/opensheetmusicdisplay"
-    },
-    "keywords": [
-        "sheet",
-        "music",
-        "vexflow",
-        "musicxml"
-    ],
-    "author": "PhonicScore",
-    "license": "MIT",
-    "bugs": {
-        "url": "https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues"
-    },
-    "homepage": "http://opensheetmusicdisplay.org",
-    "dependencies": {
-        "browserify": "",
-        "tsify": "",
-        "tslint": "",
-        "chai": "",
-        "mocha": "",
-        "typescript": "",
-        "phantomjs-prebuilt": "",
-
-        "grunt": "",
-        "grunt-browserify": "",
-        "grunt-contrib-clean": "",
-        "grunt-contrib-watch": "",
-        "grunt-tslint": "",
-        "grunt-contrib-uglify": "",
-        "grunt-karma": "",
-        "grunt-typings": "",
-
-        "karma": "",
-        "karma-chai": "",
-        "karma-chrome-launcher": "",
-        "karma-firefox-launcher": "",
-        "karma-mocha": "",
-        "karma-mocha-reporter": "",
-        "karma-phantomjs-launcher": "",
-
-        "vexflow": ""
-    }
+  "name": "opensheetmusicdisplay",
+  "version": "0.0.0",
+  "description": "Open Sheet Music Display library",
+  "main": "build/osmd.min.js",
+  "scripts": {
+    "start": "echo",
+    "test": "$(npm bin)/grunt npmtest",
+    "prepublish": "$(npm bin)/grunt publish"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/opensheetmusicdisplay/opensheetmusicdisplay"
+  },
+  "keywords": [
+    "sheet",
+    "music",
+    "vexflow",
+    "musicxml"
+  ],
+  "author": "PhonicScore",
+  "license": "MIT",
+  "bugs": {
+    "url": "https://github.com/opensheetmusicdisplay/opensheetmusicdisplay/issues"
+  },
+  "homepage": "http://opensheetmusicdisplay.org",
+  "dependencies": {
+    "es6-promise": "",
+    "jszip": "",
+    "vexflow": ""
+  },
+  "devDependencies": {
+    "browserify": "",
+    "chai": "^3.4.1",
+    "grunt": "^1.0.1",
+    "grunt-browserify": "",
+    "grunt-contrib-clean": "",
+    "grunt-contrib-jshint": "",
+    "grunt-contrib-uglify": "",
+    "grunt-contrib-watch": "",
+    "grunt-karma": "",
+    "grunt-tslint": "",
+    "grunt-typings": "",
+    "karma": "",
+    "karma-base64-to-js-preprocessor": "",
+    "karma-chai": "",
+    "karma-chrome-launcher": "",
+    "karma-firefox-launcher": "",
+    "karma-mocha": "",
+    "karma-mocha-reporter": "",
+    "karma-phantomjs-launcher": "",
+    "karma-xml2js-preprocessor": "",
+    "mocha": "^2.5.2",
+    "phantomjs-prebuilt": "",
+    "tsify": "",
+    "tslint": "3.8.0",
+    "typescript": "",
+    "typescript-collections": ""
+  }
 }

+ 25 - 0
src/Common/DataObjects/MusicSheetErrors.ts

@@ -0,0 +1,25 @@
+// skeleton by Andrea
+
+export class MusicSheetErrors {
+    public measureErrors: { [n: number]: string[] } = {};
+
+    private errors: string[] = [];
+    private tempErrors: string[] = [];
+
+    public finalizeMeasure(measureNumber: number): void {
+        let list: string[] = this.measureErrors[measureNumber];
+        if (list === undefined) {
+            list = [];
+        }
+        this.measureErrors[measureNumber] = list.concat(this.tempErrors);
+        this.tempErrors = [];
+    }
+
+    public pushMeasureError(errorMsg: string): void {
+        this.tempErrors.push(errorMsg);
+    }
+
+    public push(errorMsg: string): void {
+        this.errors.push(errorMsg);
+    }
+}

+ 19 - 0
src/Common/DataObjects/PointF2D.ts

@@ -0,0 +1,19 @@
+export class PointF2D {
+    public x: number = 0;
+    public y: number = 0;
+
+    constructor(x: number = 0, y: number = 0) {
+        this.x = x;
+        this.y = y;
+    }
+
+    public static get Empty(): PointF2D {
+        return new PointF2D();
+    }
+    public static pointsAreEqual(p1: PointF2D, p2: PointF2D): boolean {
+        return (p1.x === p2.x && p1.y === p2.y);
+    }
+    public ToString(): string {
+        return "[" + this.x + ", " + this.y + "]";
+    }
+}

+ 26 - 0
src/Common/DataObjects/RectangleF2D.ts

@@ -0,0 +1,26 @@
+import {SizeF2D} from "./SizeF2D";
+import {PointF2D} from "./PointF2D";
+
+export class RectangleF2D {
+    public x: number = 0;
+    public y: number = 0;
+    public width: number = 0;
+    public height: number = 0;
+
+    constructor(x: number, y: number, width: number, height: number) {
+        this.x = x;
+        this.y = y;
+        this.width = width;
+        this.height = height;
+    }
+
+    public static createFromLocationAndSize(location: PointF2D, size: SizeF2D): RectangleF2D {
+        return new RectangleF2D(location.x, location.y, size.width, size.height);
+    }
+    public get Location(): PointF2D {
+        return new PointF2D(this.x, this.y);
+    }
+    public get Size(): SizeF2D {
+        return new SizeF2D(this.width, this.height);
+    }
+}

+ 9 - 0
src/Common/DataObjects/SizeF2D.ts

@@ -0,0 +1,9 @@
+export class SizeF2D {
+    public width: number;
+    public height: number;
+
+    constructor(width: number = 0, height: number = 0) {
+        this.width = width;
+        this.height = height;
+    }
+}

+ 68 - 51
src/Common/DataObjects/fraction.ts

@@ -1,33 +1,37 @@
-// TODO: implement operators!
-"use strict";
-export class Fraction /*implements IComparable, IComparer<Fraction> */{
-   constructor(numerator: number = 0, denominator: number = 1, simplify: boolean = true) {
-       this.numerator = numerator;
-       this.denominator = denominator;
-
-       if (simplify) {
-            this.simplify();
-       }
-       this.setRealValue();
-   }
+// FIXME: Check the operators' names
+// FIXME: This class should probably be immutable?
 
+export class Fraction {
     private static maximumAllowedNumber: number = 46340;
     private numerator: number = 0;
     private denominator: number = 1;
     private realValue: number;
 
-    public static CreateFractionFromFraction(fraction: Fraction): Fraction {
+    public static max(f1: Fraction, f2: Fraction): Fraction {
+        if (f1.RealValue > f2.RealValue) {
+            return f1;
+        } else {
+            return f2;
+        }
+    }
+
+    public static Equal(f1: Fraction, f2: Fraction): boolean {
+        // FIXME
+        return f1.Denominator === f2.Denominator && f1.Numerator === f2.Numerator;
+    }
+
+    public static createFromFraction(fraction: Fraction): Fraction {
         return new Fraction(fraction.numerator, fraction.denominator);
     }
 
-    public static plus (f1: Fraction , f2: Fraction): Fraction {
-        let sum: Fraction = Fraction.CreateFractionFromFraction(f1);
+    public static plus (f1: Fraction, f2: Fraction): Fraction {
+        let sum: Fraction = f1.clone();
         sum.Add(f2);
         return sum;
     }
 
-    public static minus (f1: Fraction , f2: Fraction): Fraction {
-        let sum: Fraction = Fraction.CreateFractionFromFraction(f1);
+    public static minus(f1: Fraction , f2: Fraction): Fraction {
+        let sum: Fraction = f1.clone();
         sum.Sub(f2);
         return sum;
     }
@@ -52,6 +56,22 @@ export class Fraction /*implements IComparable, IComparer<Fraction> */{
         return a;
     }
 
+    constructor(numerator: number = 0, denominator: number = 1, simplify: boolean = true) {
+        this.numerator = numerator;
+        this.denominator = denominator;
+
+        if (simplify) { this.simplify(); }
+        this.setRealValue();
+    }
+
+    public toString(): string {
+        return this.numerator + "/" + this.denominator;
+    }
+
+    public clone(): Fraction {
+        return new Fraction(this.numerator, this.denominator, false);
+    }
+
     public get Numerator(): number {
         return this.numerator;
     }
@@ -132,23 +152,33 @@ export class Fraction /*implements IComparable, IComparer<Fraction> */{
         return upTestFraction;
     }
 
-    //public Equals(obj: Object): boolean {
-    //    if (ReferenceEquals(obj, null)) {
-    //        return false;
-    //    }
-    //    return this.Equals(__as__<Fraction>(obj, Fraction));
-    //}
+    public Equals(obj: Fraction): boolean {
+        return this.RealValue === obj.RealValue;
+    }
+
+    public CompareTo(obj: Fraction): number {
+        let diff: number = this.numerator * obj.Denominator - this.denominator * obj.Numerator;
+        // Return the sign of diff
+        return diff ? diff < 0 ? -1 : 1 : 0;
+    }
+
+    public lt(frac: Fraction): boolean {
+        return (this.numerator * frac.Denominator - this.denominator * frac.Numerator) < 0;
+    }
+    public lte(frac: Fraction): boolean {
+        return (this.numerator * frac.Denominator - this.denominator * frac.Numerator) <= 0;
+    }
 
     //public Equals(f: Fraction): boolean {
     //    if (ReferenceEquals(this, f))
     //        return true;
-    //    if (ReferenceEquals(f, null))
+    //    if (ReferenceEquals(f, undefined))
     //        return false;
     //    return <number>this.numerator * f.denominator === <number>f.numerator * this.denominator;
     //}
 
     public GetInversion(): Fraction {
-        return new Fraction(this.Denominator, this.Numerator);
+        return new Fraction(this.denominator, this.numerator);
     }
 
     private setRealValue(): void {
@@ -205,13 +235,7 @@ export class Fraction /*implements IComparable, IComparer<Fraction> */{
     //        return m1;
     //    else return m2;
     //}
-    //public CompareTo(obj: Object): number {
-    //    if (this > <Fraction>obj)
-    //        return 1;
-    //    else if (this <<Fraction>obj)
-    //        return -1;
-    //    return 0;
-    //}
+
     //public static getFraction(value: number, denominatorPrecision: number): Fraction {
     //    let numerator: number = <number>Math.round(value / (1.0 / denominatorPrecision));
     //    return new Fraction(numerator, denominatorPrecision);
@@ -221,11 +245,7 @@ export class Fraction /*implements IComparable, IComparer<Fraction> */{
     //        return f1;
     //    else return f2;
     //}
-    //public static fractionMax(f1: Fraction, f2: Fraction): Fraction {
-    //    if (f1 > f2)
-    //        return f1;
-    //    else return f2;
-    //}
+
     //public static GetMaxValue(): Fraction {
     //    return new Fraction(Fraction.maximumAllowedNumber, 1);
     //}
@@ -235,9 +255,6 @@ export class Fraction /*implements IComparable, IComparer<Fraction> */{
     //public static get MaxAllowedDenominator(): number {
     //    return Fraction.maximumAllowedNumber;
     //}
-    //public ToString(): string {
-    //    return this.numerator.ToString() + "/" + this.denominator.ToString();
-    //}
     //public ToFloatingString(): string {
     //    return this.RealValue.ToString();
     //}
@@ -284,16 +301,16 @@ export class Fraction /*implements IComparable, IComparer<Fraction> */{
     //}
     //
     //// operator overload ==
-    //public static bool operator == (Fraction f1, Fraction f2)
+    //public static bool operator === (Fraction f1, Fraction f2)
     //{
     //    // code enhanced for performance
-    //    // System.Object.ReferenceEquals(f1, null) is better than if (f1 == null)
+    //    // System.Object.ReferenceEquals(f1, undefined) is better than if (f1 === undefined)
     //    // and comparisons between booleans are quick
-    //    bool f1IsNull = System.Object.ReferenceEquals(f1, null);
-    //    bool f2IsNull = System.Object.ReferenceEquals(f2, null);
+    //    bool f1IsNull = System.Object.ReferenceEquals(f1, undefined);
+    //    bool f2IsNull = System.Object.ReferenceEquals(f2, undefined);
     //
-    //    // method returns true when both are null, false when only the first is null, otherwise the result of equals
-    //    if (f1IsNull != f2IsNull)
+    //    // method returns true when both are undefined, false when only the first is undefined, otherwise the result of equals
+    //    if (f1IsNull !== f2IsNull)
     //        return false;
     //
     //    if (f1IsNull /*&& f2IsNull*/)
@@ -303,9 +320,9 @@ export class Fraction /*implements IComparable, IComparer<Fraction> */{
     //}
     //
     //// operator overload !=
-    //public static bool operator != (Fraction f1, Fraction f2)
+    //public static bool operator !== (Fraction f1, Fraction f2)
     //{
-    //    return (!(f1 == f2));
+    //    return (!(f1 === f2));
     //}
     //
     //// operator overload >=
@@ -327,8 +344,8 @@ export class Fraction /*implements IComparable, IComparer<Fraction> */{
     //
     //public static Fraction operator / (Fraction f1, Fraction f2)
     //{
-    //    var res = new Fraction(f1.Numerator*f2.Denominator, f1.Denominator*f2.Numerator);
-    //    return res.Denominator == 0 ? new Fraction(0, 1) : res;
+    //    let res = new Fraction(f1.Numerator*f2.Denominator, f1.Denominator*f2.Numerator);
+    //    return res.Denominator === 0 ? new Fraction(0, 1) : res;
     //}
     //
     //public static Fraction operator * (Fraction f1, Fraction f2)
@@ -338,7 +355,7 @@ export class Fraction /*implements IComparable, IComparer<Fraction> */{
     //
     //public static Fraction operator % (Fraction f1, Fraction f2)
     //{
-    //    var a = f1/f2;
+    //    let a = f1/f2;
     //    return new Fraction(a.Numerator%a.Denominator, a.Denominator)*f2;
     //}
     //

+ 59 - 0
src/Common/DataObjects/osmdColor.ts

@@ -0,0 +1,59 @@
+export class OSMDColor {
+    public alpha: number;
+    public red: number;
+    public green: number;
+    public blue: number;
+
+    // FIXME:
+    /*constructor(alpha: number, red: number, green: number, blue: number) {
+        this.alpha = alpha;
+        this.red = red;
+        this.green = green;
+        this.blue = blue;
+    }*/
+    constructor(red: number, green: number, blue: number) {
+        this.alpha = 255;
+        this.red = red;
+        this.green = green;
+        this.blue = blue;
+    }
+    public static get Black(): OSMDColor {
+        return new OSMDColor(0, 0, 0);
+    }
+    public static get DeepSkyBlue(): OSMDColor {
+        return new OSMDColor(0, 191, 255);
+    }
+    public static get Green(): OSMDColor {
+        return new OSMDColor(20, 160, 20);
+    }
+    public static get Magenta(): OSMDColor {
+        return new OSMDColor(255, 0, 255);
+    }
+    public static get Orange(): OSMDColor {
+        return new OSMDColor(255, 128, 0);
+    }
+    public static get Red(): OSMDColor {
+        return new OSMDColor(240, 20, 20);
+    }
+    public static get Disabled(): OSMDColor {
+        return new OSMDColor(225, 225, 225);
+    }
+    public static get DarkBlue(): OSMDColor {
+        return new OSMDColor(0, 0, 140);
+    }
+    // For debugging:
+    public static get Debug1(): OSMDColor {
+        return new OSMDColor(200, 0, 140);
+    }
+    public static get Debug2(): OSMDColor {
+        return new OSMDColor(100, 100, 200);
+    }
+    public static get Debug3(): OSMDColor {
+        return new OSMDColor(0, 50, 140);
+    }
+
+    public toString(): string {
+        // FIXME RGBA
+        return "rgb(" + this.red + "," + this.green + "," + this.blue + ")";
+    }
+}

+ 74 - 64
src/Common/DataObjects/pitch.ts

@@ -1,6 +1,3 @@
-import {PSMath} from "../..//Util/psMath";
-import {CollectionUtil} from "../../Util/collectionUtil";
-
 export enum NoteEnum {
     C = 0,
     D = 2,
@@ -20,19 +17,12 @@ export enum AccidentalEnum {
 }
 
 export class Pitch {
+    public static pitchEnumValues: NoteEnum[] = [
+        NoteEnum.C, NoteEnum.D, NoteEnum.E, NoteEnum.F, NoteEnum.G, NoteEnum.A, NoteEnum.B,
+    ];
 
-    constructor(fundamentalNote: NoteEnum, octave: number, accidental: AccidentalEnum) {
-        this.fundamentalNote = fundamentalNote;
-        this.octave = octave;
-        this.accidental = accidental;
-        this.halfTone = <number>(fundamentalNote) + (octave + Pitch.octXmlDiff) * 12 + <number>accidental;
-        this.frequency = Pitch.calcFrequency(this);
-    }
-
-    public static pitchEnumValues: NoteEnum[] = [NoteEnum.C, NoteEnum.D, NoteEnum.E, NoteEnum.F, NoteEnum.G, NoteEnum.A, NoteEnum.B];
-
-    private static halftoneFactor: number = 12 / PSMath.log10(2);
-    private static octXmlDiff: number = 3;  // Pitch.octXmlDiff
+    private static halftoneFactor: number = 12 / (Math.LN2 / Math.LN10);
+    private static octXmlDiff: number = 3;
 
     // private _sourceOctave: number;
     // private _sourceFundamentalNote: NoteEnum;
@@ -43,6 +33,27 @@ export class Pitch {
     private frequency: number;
     private halfTone: number;
 
+    public static getNoteEnumString(note: NoteEnum): string {
+        switch (note) {
+            case NoteEnum.C:
+                return "C";
+            case NoteEnum.D:
+                return "D";
+            case NoteEnum.E:
+                return "E";
+            case NoteEnum.F:
+                return "F";
+            case NoteEnum.G:
+                return "G";
+            case NoteEnum.A:
+                return "A";
+            case NoteEnum.B:
+                return "B";
+            default:
+                return "";
+        }
+    }
+
     /**
      * @param the input pitch
      * @param the number of halftones to transpose with
@@ -50,12 +61,12 @@ export class Pitch {
      *          ret[1] = the octave shift (not the new octave!)
      * @constructor
      */
-    public static CalculateTransposedHalfTone(pitch: Pitch, transpose: number): number[] {
+    public static CalculateTransposedHalfTone(pitch: Pitch, transpose: number): { value: number; overflow: number; } {
         let newHalfTone: number = <number>pitch.fundamentalNote + <number>pitch.accidental + transpose;
         return Pitch.WrapAroundCheck(newHalfTone, 12);
     }
 
-    public static WrapAroundCheck(value: number, limit: number): number[] {
+    public static WrapAroundCheck(value: number, limit: number): { value: number; overflow: number; } {
         let overflow: number = 0;
 
         while (value < 0) {
@@ -66,50 +77,54 @@ export class Pitch {
             value -= limit;
             overflow++; // the octave change
         }
-        return [value, overflow];
+        return {overflow: overflow, value: value};
     }
 
-    public static calcFrequency(pitch: Pitch): number;
-
-    public static calcFrequency(fractionalKey: number): number;
-
-    public static calcFrequency(pitch: any): number {
-        if (pitch instanceof Pitch) {
-            let octaveSteps: number = pitch.octave - 1;
-            let halftoneSteps: number = <number>pitch.fundamentalNote - <number>NoteEnum.A + <number>pitch.accidental;
-            let frequency: number = <number>(440.0 * Math.pow(2, octaveSteps) * Math.pow(2, halftoneSteps / 12.0));
-            return frequency;
-        } else if (typeof pitch === "number") {
-            let fractionalKey: number = pitch;
-            let frequency: number = <number>(440.0 * Math.pow(2, (fractionalKey - 57.0) / 12));
-            return frequency;
+    //public static calcFrequency(pitch: Pitch): number;
+
+    //public static calcFrequency(fractionalKey: number): number;
+
+    public static calcFrequency(obj: Pitch|number): number {
+        let octaveSteps: number = 0;
+        let halftoneSteps: number;
+        if (obj instanceof Pitch) {
+            // obj is a pitch
+            let pitch: Pitch = obj;
+            octaveSteps = pitch.octave - 1;
+            halftoneSteps = <number>pitch.fundamentalNote - <number>NoteEnum.A + <number>pitch.accidental;
+        } else if (typeof obj === "number") {
+            // obj is a fractional key
+            let fractionalKey: number = obj;
+            halftoneSteps = fractionalKey - 57.0;
         }
+        // Return frequency:
+        return 440.0 * Math.pow(2, octaveSteps) * Math.pow(2, halftoneSteps / 12.0);
     }
 
     public static calcFractionalKey(frequency: number): number {
-        let halftoneFrequency: number = <number>((PSMath.log10(frequency / 440.0) * Pitch.halftoneFactor) + 57.0);
-        return halftoneFrequency;
+        // Return half-tone frequency:
+        return Math.log(frequency / 440.0) / Math.LN10 * Pitch.halftoneFactor + 57.0;
     }
 
-    public static getPitchFromFrequency(frequency: number): Pitch {
+    public static fromFrequency(frequency: number): Pitch {
         let key: number = Pitch.calcFractionalKey(frequency) + 0.5;
-        let octave: number = <number>Math.floor(key / 12) - Pitch.octXmlDiff;
-        let halftone: number = Math.floor(<number>(key)) % 12;
+        let octave: number = Math.floor(key / 12) - Pitch.octXmlDiff;
+        let halftone: number = Math.floor(key) % 12;
         let fundamentalNote: NoteEnum = <NoteEnum>halftone;
         let accidental: AccidentalEnum = AccidentalEnum.NONE;
-        if (!CollectionUtil.contains(this.pitchEnumValues, fundamentalNote)) {
+        if (this.pitchEnumValues.indexOf(fundamentalNote) === -1) {
             fundamentalNote = <NoteEnum>(halftone - 1);
             accidental = AccidentalEnum.SHARP;
         }
-        return new Pitch(fundamentalNote, <number>octave, accidental);
+        return new Pitch(fundamentalNote, octave, accidental);
     }
 
-    public static getPitchFromHalftone(halftone: number): Pitch {
+    public static fromHalftone(halftone: number): Pitch {
         let octave: number = <number>Math.floor(<number>halftone / 12) - Pitch.octXmlDiff;
         let halftoneInOctave: number = halftone % 12;
         let fundamentalNote: NoteEnum = <NoteEnum>halftoneInOctave;
         let accidental: AccidentalEnum = AccidentalEnum.NONE;
-        if (!CollectionUtil.contains(this.pitchEnumValues, fundamentalNote)) {
+        if (this.pitchEnumValues.indexOf(fundamentalNote) === -1) {
             fundamentalNote = <NoteEnum>(halftoneInOctave - 1);
             accidental = AccidentalEnum.SHARP;
         }
@@ -119,21 +134,29 @@ export class Pitch {
     public static ceiling(halftone: number): NoteEnum {
         halftone = <number>(halftone) % 12;
         let fundamentalNote: NoteEnum = <NoteEnum>halftone;
-        if (!CollectionUtil.contains(this.pitchEnumValues, fundamentalNote)) {
+        if (this.pitchEnumValues.indexOf(fundamentalNote) === -1) {
             fundamentalNote = <NoteEnum>(halftone + 1);
         }
         return fundamentalNote;
     }
 
     public static floor(halftone: number): NoteEnum {
-        halftone = <number>(halftone) % 12;
+        halftone = halftone % 12;
         let fundamentalNote: NoteEnum = <NoteEnum>halftone;
-        if (!CollectionUtil.contains(this.pitchEnumValues, fundamentalNote)) {
+        if (this.pitchEnumValues.indexOf(fundamentalNote) === -1) {
             fundamentalNote = <NoteEnum>(halftone - 1);
         }
         return fundamentalNote;
     }
 
+    constructor(fundamentalNote: NoteEnum, octave: number, accidental: AccidentalEnum) {
+        this.fundamentalNote = fundamentalNote;
+        this.octave = octave;
+        this.accidental = accidental;
+        this.halfTone = <number>(fundamentalNote) + (octave + Pitch.octXmlDiff) * 12 + <number>accidental;
+        this.frequency = Pitch.calcFrequency(this);
+    }
+
     public get Octave(): number {
         return this.octave;
     }
@@ -238,30 +261,17 @@ export class Pitch {
     }
 
     private getNextFundamentalNote(fundamental: NoteEnum): NoteEnum {
-        let i: number = 0;
-        for (; i < Pitch.pitchEnumValues.length; i++) {
-            let note: NoteEnum = Pitch.pitchEnumValues[i];
-            if (note === fundamental) {
-                break;
-            }
-        }
-        i = ++i % Pitch.pitchEnumValues.length;
+        let i: number = Pitch.pitchEnumValues.indexOf(fundamental);
+        i = (i + 1) % Pitch.pitchEnumValues.length;
         return Pitch.pitchEnumValues[i];
     }
 
     private getPreviousFundamentalNote(fundamental: NoteEnum): NoteEnum {
-        let i: number = 0;
-        for (; i < Pitch.pitchEnumValues.length; i++) {
-            let note: NoteEnum = Pitch.pitchEnumValues[i];
-            if (note === fundamental) {
-                break;
-            }
-        }
-        i--;
-        if (i < 0) {
-            i += Pitch.pitchEnumValues.length;
+        let i: number = Pitch.pitchEnumValues.indexOf(fundamental);
+        if (i > 0) {
+            return Pitch.pitchEnumValues[i - 1];
+        } else {
+            return Pitch.pitchEnumValues[Pitch.pitchEnumValues.length - 1];
         }
-        return Pitch.pitchEnumValues[i];
     }
 }
-

+ 7 - 0
src/Common/Enums/FontStyles.ts

@@ -0,0 +1,7 @@
+export enum FontStyles {
+    Regular = 0,
+    Bold = 1,
+    Italic = 2,
+    BoldItalic = 3,
+    Underlined = 4
+}

+ 4 - 0
src/Common/Enums/Fonts.ts

@@ -0,0 +1,4 @@
+export enum Fonts {
+    TimesNewRoman,
+    Kokila
+}

+ 11 - 0
src/Common/Enums/TextAlignment.ts

@@ -0,0 +1,11 @@
+export enum TextAlignment {
+    LeftTop,
+    LeftCenter,
+    LeftBottom,
+    CenterTop,
+    CenterCenter,
+    CenterBottom,
+    RightTop,
+    RightCenter,
+    RightBottom
+}

+ 55 - 0
src/Common/FileIO/Mxl.ts

@@ -0,0 +1,55 @@
+import { IXmlElement } from "./Xml";
+import { Promise } from "es6-promise";
+import JSZip = require("jszip");
+
+// Usage for extractSheetMusicFromMxl:
+// extractSheetFromMxl(" *** binary content *** ").then(
+//   (score: IXmlElement) => {
+//     // Success! use the score here!
+//   },
+//   (error: any) => {
+//     // There was an error.
+//     // Handle it here.
+//   }
+// )
+export function extractSheetFromMxl(data: string): Promise<any> {
+  "use strict";
+  // _zip_ must be of type 'any' for now, since typings for JSZip are not up-to-date
+  let zip: any = new JSZip();
+  // asynchronously load zip file and process it - with Promises
+  return zip.loadAsync(data).then(
+    (_: any) => {
+      return zip.file("META-INF/container.xml").async("string");
+    },
+    (err: any) => {
+      throw err;
+    }
+  ).then(
+    (content: string) => {
+      let parser: DOMParser = new DOMParser();
+      let doc: Document = parser.parseFromString(content, "text/xml");
+      let rootFile: string = doc.getElementsByTagName("rootfile")[0].getAttribute("full-path");
+      return zip.file(rootFile).async("string");
+    },
+    (err: any) => {
+      throw err;
+    }
+  ).then(
+    (content: string) => {
+      let parser: DOMParser = new DOMParser();
+      let xml: Document = parser.parseFromString(content, "text/xml");
+      let doc: IXmlElement = new IXmlElement(xml.documentElement);
+      return Promise.resolve(doc);
+    },
+    (err: any) => {
+      throw err;
+    }
+  ).then(
+    (content: IXmlElement) => {
+      return Promise.resolve(content);
+    },
+    (err: any) => {
+      throw new Error("extractSheetFromMxl: " + err.message);
+    }
+  );
+}

+ 58 - 59
src/Common/FileIO/Xml.ts

@@ -1,72 +1,71 @@
-export class XmlAttribute {
-  public Name: string;
-  public Value: string;
+export type IXmlAttribute = Attr;
 
-  constructor(attr: Attr) {
-    this.Name = attr.name;
-    this.Value = attr.value;
-  };
-}
-
-export class XmlElement {
-  public Name: string;
-  public Value: string;
-  public HasAttributes: boolean = false;
-  public FirstAttribute: XmlAttribute;
-  public HasElements: boolean;
+export class IXmlElement {
+    public name: string;
+    public value: string;
+    public hasAttributes: boolean = false;
+    public firstAttribute: IXmlAttribute;
+    public hasElements: boolean;
 
-  private _attrs: XmlAttribute[];
-  private _elem: Element;
+    private attrs: IXmlAttribute[];
+    private elem: Element;
 
-  constructor(elem: Element) {
-    this._elem = elem;
-    this.Name = elem.nodeName;
+    constructor(elem: Element) {
+        this.elem = elem;
+        this.name = elem.nodeName.toLowerCase();
 
-    if (elem.hasAttributes()) {
-      this.HasAttributes = true;
-      this.FirstAttribute = new XmlAttribute(elem.attributes[0]);
-      }
-    this.HasElements = elem.hasChildNodes();
-    // Look for a value
-    if (
-      elem.childNodes.length === 1 &&
-      elem.childNodes[0].nodeType === Node.TEXT_NODE
-    ) {
-      this.Value = elem.childNodes[0].nodeValue;
+        if (elem.hasAttributes()) {
+            this.hasAttributes = true;
+            this.firstAttribute = elem.attributes[0];
+        }
+        this.hasElements = elem.hasChildNodes();
+        // Look for a value
+        if (elem.childNodes.length === 1 && elem.childNodes[0].nodeType === Node.TEXT_NODE) {
+            this.value = elem.childNodes[0].nodeValue;
+        } else {
+            this.value = "";
+        }
     }
-  }
 
-  public Attribute(attributeName: string): XmlAttribute {
-    return new XmlAttribute(this._elem.attributes.getNamedItem(attributeName));
-  }
+    public attribute(attributeName: string): IXmlAttribute {
+        return this.elem.attributes.getNamedItem(attributeName);
+    }
 
-  public Attributes(): XmlAttribute[] {
-    if (typeof this._attrs === "undefined") {
-      let attributes: NamedNodeMap = this._elem.attributes;
-      let attrs: XmlAttribute[] = new Array();
-      for (let i: number = 0; i < attributes.length; i += 1) {
-        attrs.push(new XmlAttribute(attributes[i]));
-      }
-      this._attrs = attrs;
+    public attributes(): IXmlAttribute[] {
+        if (typeof this.attrs === "undefined") {
+            let attributes: NamedNodeMap = this.elem.attributes;
+            let attrs: IXmlAttribute[] = [];
+            for (let i: number = 0; i < attributes.length; i += 1) {
+                attrs.push(attributes[i]);
+            }
+            this.attrs = attrs;
+        }
+        return this.attrs;
     }
-    return this._attrs;
-  }
 
-  public Element(elementName: string): XmlElement {
-    return this.Elements(elementName)[0];
-  }
+    public element(elementName: string): IXmlElement {
+        return this.elements(elementName)[0];
+    }
 
-  public Elements(nodeName: string): XmlElement[] {
-    let nodes: NodeList = this._elem.childNodes;
-    let ret: XmlElement[] = new Array();
-    let nameUnset: boolean = typeof nodeName === "undefined";
-    for (let i: number = 0; i < nodes.length; i += 1) {
-      let node: Node = nodes[i];
-      if (node.nodeType === Node.ELEMENT_NODE &&
-        (nameUnset || node.nodeName === nodeName)) {
-          ret.push(new XmlElement(<Element> node));
+    public elements(nodeName?: string): IXmlElement[] {
+        let nodes: NodeList = this.elem.childNodes;
+        let ret: IXmlElement[] = [];
+        let nameUnset: boolean = nodeName === undefined;
+        if (!nameUnset) {
+            nodeName = nodeName.toLowerCase();
+        }
+        // console.log("-", nodeName, nodes.length, this.elem.childElementCount, this.elem.getElementsByTagName(nodeName).length);
+        // if (nodeName === "measure") {
+        //   console.log(this.elem);
+        // }
+        for (let i: number = 0; i < nodes.length; i += 1) {
+            let node: Node = nodes[i];
+            //console.log("node: ", this.elem.nodeName, ">>", node.nodeName, node.nodeType === Node.ELEMENT_NODE);
+            if (node.nodeType === Node.ELEMENT_NODE &&
+                (nameUnset || node.nodeName.toLowerCase() === nodeName)) {
+                ret.push(new IXmlElement(node as Element));
+            }
         }
+        return ret;
     }
-    return ret;
-  }
 }

+ 13 - 0
src/Common/logging.ts

@@ -0,0 +1,13 @@
+/**
+ * Created by acondolu on 26/04/16.
+ */
+
+
+export class Logging {
+    public static debug(...args: any[]): void {
+        console.log("[OSMD] DEBUG: ", args.join(" "));
+    }
+    public static log(...args: any[]): void {
+        console.log("[OSMD] ", args.join(" "));
+    }
+}

+ 83 - 0
src/MusicSheetAPI.ts

@@ -0,0 +1,83 @@
+import {IXmlElement} from "./Common/FileIO/Xml";
+import {VexFlowMusicSheetCalculator} from "./MusicalScore/Graphical/VexFlow/VexFlowMusicSheetCalculator";
+import {MusicSheetReader} from "./MusicalScore/ScoreIO/MusicSheetReader";
+import {GraphicalMusicSheet} from "./MusicalScore/Graphical/GraphicalMusicSheet";
+import {MusicSheetCalculator} from "./MusicalScore/Graphical/MusicSheetCalculator";
+import {VexFlowMusicSheetDrawer} from "./MusicalScore/Graphical/VexFlow/VexFlowMusicSheetDrawer";
+import {MusicSheet} from "./MusicalScore/MusicSheet";
+import {VexFlowTextMeasurer} from "./MusicalScore/Graphical/VexFlow/VexFlowTextMeasurer";
+
+export class MusicSheetAPI {
+    constructor() {
+        return;
+    }
+
+    private canvas: HTMLCanvasElement;
+    private sheet: MusicSheet;
+    private drawer: VexFlowMusicSheetDrawer;
+    private graphic: GraphicalMusicSheet;
+    private width: number;
+    private zoom: number = 1.0;
+    private unit: number = 10;
+
+    public load(sheet: Element): void {
+        let score: IXmlElement = new IXmlElement(sheet.getElementsByTagName("score-partwise")[0]);
+        let calc: MusicSheetCalculator = new VexFlowMusicSheetCalculator();
+        let reader: MusicSheetReader = new MusicSheetReader();
+        this.sheet = reader.createMusicSheet(score, "path missing");
+        this.graphic = new GraphicalMusicSheet(this.sheet, calc);
+        this.display();
+    }
+
+    public setCanvas(canvas: HTMLCanvasElement): void {
+        this.canvas = canvas;
+        this.drawer = new VexFlowMusicSheetDrawer(canvas, new VexFlowTextMeasurer());
+    }
+
+    public setWidth(width: number): void {
+        if (width === this.width) {
+            return;
+        }
+        this.width = width;
+        this.display();
+    }
+
+    public scale(k: number): void {
+        this.zoom = k;
+        this.display();
+    }
+
+    public display(): void {
+        if (this.width === undefined) {
+            return;
+        }
+        if (this.canvas === undefined) {
+            return;
+        }
+        if (this.sheet === undefined) {
+            return;
+        }
+        this.sheet.pageWidth = this.width / this.zoom / this.unit;
+        this.graphic.reCalculate();
+        // Update Sheet Page
+        let height: number = this.graphic.MusicPages[0].PositionAndShape.BorderBottom * this.unit * this.zoom;
+        this.drawer.resize(
+            this.width,
+            height
+        );
+        // Fix the label problem
+        this.drawer.translate(0, 100);
+        this.drawer.scale(this.zoom);
+        this.drawer.drawSheet(this.graphic);
+    }
+
+    public free(): void {
+        this.canvas = undefined;
+        this.sheet = undefined;
+        return;
+    }
+}
+
+(<any>window).osmd = {
+    "MusicSheet":  MusicSheetAPI,
+};

+ 107 - 3
src/MusicalScore/Calculation/MeasureSizeCalculator.ts

@@ -1,7 +1,12 @@
 import Vex = require("vexflow");
+import StaveNote = Vex.Flow.StaveNote;
+
+// The type PositionAndShapeInfo is still to be ported in TypeScript
+type PositionAndShapeInfo = any;
+declare var PositionAndShapeInfo: any;
 
 /* TODO
- * Take into account StaveModifiers
+ * Complete support for StaveModifiers
  * Take into account Ties and Slurs
  */
 
@@ -48,6 +53,94 @@ export class MeasureSizeCalculator {
     this.format();
   }
 
+  // Returns the shape of the note head at position _index_ inside _note_.
+  // Remember: in VexFlow, StaveNote correspond to PhonicScore's VoiceEntries.
+  //  public static getVexFlowNoteHeadShape(note: StaveNote, index: number): PositionAndShapeInfo {
+  //  // note_heads is not public in StaveNote, but we access it anyway...
+  //  let bb = note.note_heads[index].getBoundingBox();
+  //  let info: any = new PositionAndShapeInfo();
+  //  let x: number = bb.getX();
+  //  let y: number = bb.getY();
+  //  let w: number = bb.getW();
+  //  let h: number = bb.getH();
+  //  info.Left = info.Right = bb.getW() / 2;
+  //  info.Top = info.Bottom = bb.getH() / 2;
+  //  info.X = bb.getX() + info.Left;
+  //  info.Y = bb.getY() + info.Bottom;
+  //  return info;
+  //}
+
+  // Returns the shape of all the note heads inside a StaveNote.
+  // Remember: in VexFlow, StaveNote correspond to PhonicScore's VoiceEntries.
+  public static getVexFlowStaveNoteShape(note: StaveNote): PositionAndShapeInfo {
+    let info: any = new PositionAndShapeInfo();
+    let bounds: any = note.getNoteHeadBounds();
+    let beginX: number = note.getNoteHeadBeginX();
+    let endX: number = note.getNoteHeadEndX();
+
+    info.Left = info.Right = (endX - beginX) / 2;
+    info.Top = info.Bottom = (bounds.y_top - bounds.y_bottom) / 2;
+    info.X = beginX + info.Left;
+    info.Y = bounds.y_bottom + info.Bottom;
+    return info;
+  }
+
+
+  public static getClefBoundingBox(clef: Vex.Flow.Clef): Vex.Flow.BoundingBox {
+    let clef2: any = clef;
+    clef2.placeGlyphOnLine(clef2.glyph, clef2.stave, clef2.clef.line);
+    let glyph: any = clef.glyph;
+    let posX: number = clef.x + glyph.x_shift;
+    let posY: number = clef.stave.getYForGlyphs() + glyph.y_shift;
+    let scale: number = glyph.scale;
+    let outline: any[] = glyph.metrics.outline;
+    let xmin: number = 0, xmax: number = 0, ymin: number = 0, ymax: number = 0;
+
+    function update(i: number): void {
+      let x: number = outline[i + 1];
+      let y: number = outline[i + 2];
+      xmin = Math.min(xmin, x);
+      xmax = Math.max(xmax, x);
+      ymin = Math.min(ymin, y);
+      ymax = Math.max(ymax, y);
+    }
+
+    for (let i: number = 0, len: number = outline.length; i < len; i += 3) {
+      switch (outline[i] as string) {
+        case "m": update(i); break;
+        case "l": update(i); break;
+        case "q": i += 2; update(i); break;
+        case "b": i += 4; update(i); break;
+        default: break;
+      }
+
+    }
+    return new Vex.Flow.BoundingBox(
+        posX + xmin * scale,
+        posY - ymin * scale,
+        (xmax - xmin) * scale,
+        (ymin - ymax) * scale
+    );
+  }
+
+
+  public static getKeySignatureBoundingBox(sig: any): Vex.Flow.BoundingBox {
+    // FIXME: Maybe use Vex.Flow.keySignature(this.keySpec);
+    let stave: Vex.Flow.Stave = sig.getStave();
+    let width: number = sig.getWidth();
+    let maxLine: number = 1;
+    let minLine: number = 1;
+    for (let acc of sig.accList) {
+      maxLine = Math.max(acc.line, maxLine);
+      minLine = Math.min(acc.line, minLine);
+    }
+    let y: number = sig.getStave().getYForLine(minLine);
+    let height: number = stave.getSpacingBetweenLines() * (maxLine - minLine);
+    let x: number = 0; // FIXME
+    return new Vex.Flow.BoundingBox(x, y, width, height);
+  }
+
+
   public getWidth(): number {
     // begin_modifiers + voices + end_modifiers
     return this.offsetLeft + this.voicesWidth + this.offsetRight;
@@ -98,9 +191,19 @@ export class MeasureSizeCalculator {
     // TODO voicesBoundingBox.getW() should be similar to this.voicesWidth?
     //console.log("this.width", this.voicesWidth);
     //console.log("voicesBB", voicesBoundingBox.getW());
-
-    // FIXME the following: should consider stave modifiers
     //this.height = voicesBoundingBox.getH(); FIXME
+
+    // Consider clefs
+    let clefs: Vex.Flow.Clef[] = stave.getModifiers(
+      Vex.Flow.Modifier.Position.LEFT,
+      Vex.Flow.Clef.category
+    );
+    for (let clef of clefs) {
+      voicesBoundingBox = voicesBoundingBox.mergeWith(
+        MeasureSizeCalculator.getClefBoundingBox(clef)
+      );
+    }
+
     this.topBorder = Math.min(
       0,
       Math.floor(stave.getLineForY(voicesBoundingBox.getY()))
@@ -110,4 +213,5 @@ export class MeasureSizeCalculator {
       Math.ceil(stave.getLineForY(voicesBoundingBox.getY() + voicesBoundingBox.getH()))
     );
   }
+
 }

+ 29 - 0
src/MusicalScore/Exceptions.ts

@@ -0,0 +1,29 @@
+export class MusicSheetReadingException implements Error {
+    public name: string;
+    public message: string;
+    constructor(message: string, e?: Error) {
+        //super(message);
+        this.message = message;
+        if (e !== undefined) {
+            this.message += " " + e.toString();
+        }
+    }
+}
+
+
+export class ArgumentOutOfRangeException implements Error {
+    public name: string;
+    public message: string;
+    constructor(message: string) {
+        //super(message);
+        this.message = message;
+    }
+}
+
+export class InvalidEnumArgumentException implements Error {
+    public name: string;
+    public message: string;
+    constructor(message: string) {
+        this.message = message;
+    }
+}

+ 16 - 0
src/MusicalScore/Graphical/AbstractGraphicalInstruction.ts

@@ -0,0 +1,16 @@
+import {GraphicalStaffEntry} from "./GraphicalStaffEntry";
+import {GraphicalObject} from "./GraphicalObject";
+
+export abstract class AbstractGraphicalInstruction extends GraphicalObject {
+    protected parent: GraphicalStaffEntry;
+    constructor(parent: GraphicalStaffEntry) {
+        super();
+        this.parent = parent;
+    }
+    public get Parent(): GraphicalStaffEntry {
+        return this.parent;
+    }
+    public set Parent(value: GraphicalStaffEntry) {
+        this.parent = value;
+    }
+}

+ 79 - 0
src/MusicalScore/Graphical/AccidentalCalculator.ts

@@ -0,0 +1,79 @@
+import {IGraphicalSymbolFactory} from "../Interfaces/IGraphicalSymbolFactory";
+import {AccidentalEnum} from "../../Common/DataObjects/pitch";
+import {KeyInstruction} from "../VoiceData/Instructions/KeyInstruction";
+import {GraphicalNote} from "./GraphicalNote";
+import {Pitch} from "../../Common/DataObjects/pitch";
+import {NoteEnum} from "../../Common/DataObjects/pitch";
+import Dictionary from "typescript-collections/dist/lib/Dictionary";
+
+export class AccidentalCalculator {
+    private symbolFactory: IGraphicalSymbolFactory;
+    private keySignatureNoteAlterationsDict: Dictionary<number, AccidentalEnum> = new Dictionary<number, AccidentalEnum>();
+    private currentAlterationsComparedToKeyInstructionDict: number[] = [];
+    private currentInMeasureNoteAlterationsDict: Dictionary<number, AccidentalEnum> = new Dictionary<number, AccidentalEnum>();
+    private activeKeyInstruction: KeyInstruction;
+
+    constructor(symbolFactory: IGraphicalSymbolFactory) {
+        this.symbolFactory = symbolFactory;
+    }
+
+    public get ActiveKeyInstruction(): KeyInstruction {
+        return this.activeKeyInstruction;
+    }
+
+    public set ActiveKeyInstruction(value: KeyInstruction) {
+        this.activeKeyInstruction = value;
+        this.reactOnKeyInstructionChange();
+    }
+
+    public doCalculationsAtEndOfMeasure(): void {
+        this.currentInMeasureNoteAlterationsDict.clear();
+        for (let key of this.keySignatureNoteAlterationsDict.keys()) {
+            this.currentInMeasureNoteAlterationsDict.setValue(key, this.keySignatureNoteAlterationsDict.getValue(key));
+        }
+    }
+
+    public checkAccidental(graphicalNote: GraphicalNote, pitch: Pitch, grace: boolean, graceScalingFactor: number): void {
+        if (pitch === undefined) {
+            return;
+        }
+        let pitchKey: number = <number>pitch.FundamentalNote + pitch.Octave * 12;
+        let pitchKeyGivenInMeasureDict: boolean = this.currentInMeasureNoteAlterationsDict.containsKey(pitchKey);
+        if (
+            (pitchKeyGivenInMeasureDict && this.currentInMeasureNoteAlterationsDict.getValue(pitchKey) !== pitch.Accidental)
+            || (!pitchKeyGivenInMeasureDict && pitch.Accidental !== AccidentalEnum.NONE)
+        ) {
+            if (this.currentAlterationsComparedToKeyInstructionDict.indexOf(pitchKey) === -1) {
+                this.currentAlterationsComparedToKeyInstructionDict.push(pitchKey);
+            }
+            this.currentInMeasureNoteAlterationsDict.setValue(pitchKey, pitch.Accidental);
+            this.symbolFactory.addGraphicalAccidental(graphicalNote, pitch, grace, graceScalingFactor);
+        } else if (
+            this.currentAlterationsComparedToKeyInstructionDict.indexOf(pitchKey) !== -1
+            && ((pitchKeyGivenInMeasureDict && this.currentInMeasureNoteAlterationsDict.getValue(pitchKey) !== pitch.Accidental)
+            || (!pitchKeyGivenInMeasureDict && pitch.Accidental === AccidentalEnum.NONE))
+        ) {
+            delete this.currentAlterationsComparedToKeyInstructionDict[pitchKey];
+            this.currentInMeasureNoteAlterationsDict.setValue(pitchKey, pitch.Accidental);
+            this.symbolFactory.addGraphicalAccidental(graphicalNote, pitch, grace, graceScalingFactor);
+        }
+    }
+
+    private reactOnKeyInstructionChange(): void {
+        let noteEnums: NoteEnum[] = KeyInstruction.getNoteEnumList(this.activeKeyInstruction);
+        let keyAccidentalType: AccidentalEnum;
+        if (this.activeKeyInstruction.Key > 0) {
+            keyAccidentalType = AccidentalEnum.SHARP;
+        } else {
+            keyAccidentalType = AccidentalEnum.FLAT;
+        }
+        this.keySignatureNoteAlterationsDict.clear();
+        this.currentAlterationsComparedToKeyInstructionDict.length = 0;
+        for (let octave: number = -9; octave < 9; octave++) {
+            for (let i: number = 0; i < noteEnums.length; i++) {
+                this.keySignatureNoteAlterationsDict.setValue(<number>noteEnums[i] + octave * 12, keyAccidentalType);
+            }
+        }
+        this.doCalculationsAtEndOfMeasure();
+    }
+}

+ 566 - 0
src/MusicalScore/Graphical/BoundingBox.ts

@@ -0,0 +1,566 @@
+import {ArgumentOutOfRangeException} from "../Exceptions";
+import {PointF2D} from "../../Common/DataObjects/PointF2D";
+import {SizeF2D} from "../../Common/DataObjects/SizeF2D";
+import {RectangleF2D} from "../../Common/DataObjects/RectangleF2D";
+
+export class BoundingBox {
+    protected isSymbol: boolean = false;
+    protected relativePositionHasBeenSet: boolean = false;
+    protected xBordersHaveBeenSet: boolean = false;
+    protected yBordersHaveBeenSet: boolean = false;
+    protected absolutePosition: PointF2D = new PointF2D();
+    protected relativePosition: PointF2D = new PointF2D();
+    protected size: SizeF2D = new SizeF2D();
+    protected marginSize: SizeF2D;
+    protected upperLeftCorner: PointF2D;
+    protected upperLeftMarginCorner: PointF2D;
+    protected borderLeft: number = 0;
+    protected borderRight: number = 0;
+    protected borderTop: number = 0;
+    protected borderBottom: number = 0;
+    protected borderMarginLeft: number = 0;
+    protected borderMarginRight: number = 0;
+    protected borderMarginTop: number = 0;
+    protected borderMarginBottom: number = 0;
+    protected boundingRectangle: RectangleF2D;
+    protected boundingMarginRectangle: RectangleF2D;
+    protected childElements: BoundingBox[] = [];
+    protected parent: BoundingBox;
+    protected dataObject: Object;
+
+    constructor(dataObject: Object = undefined, parent: BoundingBox = undefined) {
+        this.parent = parent;
+        this.dataObject = dataObject;
+        this.xBordersHaveBeenSet = false;
+        this.yBordersHaveBeenSet = false;
+    }
+
+    public get RelativePositionHasBeenSet(): boolean {
+        return this.relativePositionHasBeenSet;
+    }
+
+    public get XBordersHaveBeenSet(): boolean {
+        return this.xBordersHaveBeenSet;
+    }
+
+    public set XBordersHaveBeenSet(value: boolean) {
+        this.xBordersHaveBeenSet = value;
+    }
+
+    public get YBordersHaveBeenSet(): boolean {
+        return this.yBordersHaveBeenSet;
+    }
+
+    public set YBordersHaveBeenSet(value: boolean) {
+        this.yBordersHaveBeenSet = value;
+    }
+
+    public get AbsolutePosition(): PointF2D {
+        return this.absolutePosition;
+    }
+
+    public set AbsolutePosition(value: PointF2D) {
+        this.absolutePosition = value;
+    }
+
+    public get RelativePosition(): PointF2D {
+        return this.relativePosition;
+    }
+
+    public set RelativePosition(value: PointF2D) {
+        this.relativePosition = value;
+        this.relativePositionHasBeenSet = true;
+    }
+
+    public get Size(): SizeF2D {
+        return this.size;
+    }
+
+    public set Size(value: SizeF2D) {
+        this.size = value;
+    }
+
+    public get MarginSize(): SizeF2D {
+        return this.marginSize;
+    }
+
+    public get UpperLeftCorner(): PointF2D {
+        return this.upperLeftCorner;
+    }
+
+    public get UpperLeftMarginCorner(): PointF2D {
+        return this.upperLeftMarginCorner;
+    }
+
+    public get BorderLeft(): number {
+        return this.borderLeft;
+    }
+
+    public set BorderLeft(value: number) {
+        this.borderLeft = value;
+        this.calculateRectangle();
+    }
+
+    public get BorderRight(): number {
+        return this.borderRight;
+    }
+
+    public set BorderRight(value: number) {
+        this.borderRight = value;
+        this.calculateRectangle();
+    }
+
+    public get BorderTop(): number {
+        return this.borderTop;
+    }
+
+    public set BorderTop(value: number) {
+        this.borderTop = value;
+        this.calculateRectangle();
+    }
+
+    public get BorderBottom(): number {
+        return this.borderBottom;
+    }
+
+    public set BorderBottom(value: number) {
+        this.borderBottom = value;
+        this.calculateRectangle();
+    }
+
+    public get BorderMarginLeft(): number {
+        return this.borderMarginLeft;
+    }
+
+    public set BorderMarginLeft(value: number) {
+        this.borderMarginLeft = value;
+        this.calculateMarginRectangle();
+    }
+
+    public get BorderMarginRight(): number {
+        return this.borderMarginRight;
+    }
+
+    public set BorderMarginRight(value: number) {
+        this.borderMarginRight = value;
+        this.calculateMarginRectangle();
+    }
+
+    public get BorderMarginTop(): number {
+        return this.borderMarginTop;
+    }
+
+    public set BorderMarginTop(value: number) {
+        this.borderMarginTop = value;
+        this.calculateMarginRectangle();
+    }
+
+    public get BorderMarginBottom(): number {
+        return this.borderMarginBottom;
+    }
+
+    public set BorderMarginBottom(value: number) {
+        this.borderMarginBottom = value;
+        this.calculateMarginRectangle();
+    }
+
+    public get BoundingRectangle(): RectangleF2D {
+        return this.boundingRectangle;
+    }
+
+    public get BoundingMarginRectangle(): RectangleF2D {
+        return this.boundingMarginRectangle;
+    }
+
+    public get ChildElements(): BoundingBox[] {
+        return this.childElements;
+    }
+
+    public set ChildElements(value: BoundingBox[]) {
+        this.childElements = value;
+    }
+
+    public get Parent(): BoundingBox {
+        return this.parent;
+    }
+
+    public set Parent(value: BoundingBox) {
+        this.parent = value;
+    }
+
+    public get DataObject(): Object {
+        return this.dataObject;
+    }
+
+    public setAbsolutePositionFromParent(): void {
+        if (this.parent !== undefined) {
+            this.absolutePosition.x = this.parent.AbsolutePosition.x + this.relativePosition.x;
+            this.absolutePosition.y = this.parent.AbsolutePosition.y + this.relativePosition.y;
+        } else {
+            this.absolutePosition = this.relativePosition;
+        }
+    }
+
+    public calculateAbsolutePositionsRecursiveWithoutTopelement(): void {
+        this.absolutePosition.x = 0.0;
+        this.absolutePosition.y = 0.0;
+        for (let idx: number = 0, len: number = this.ChildElements.length; idx < len; ++idx) {
+            let child: BoundingBox = this.ChildElements[idx];
+            child.calculateAbsolutePositionsRecursive(this.absolutePosition.x, this.absolutePosition.y);
+        }
+    }
+
+    public calculateAbsolutePositionsRecursive(x: number, y: number): void {
+        this.absolutePosition.x = this.relativePosition.x + x;
+        this.absolutePosition.y = this.relativePosition.y + y;
+        for (let idx: number = 0, len: number = this.ChildElements.length; idx < len; ++idx) {
+            let child: BoundingBox = this.ChildElements[idx];
+            child.calculateAbsolutePositionsRecursive(this.absolutePosition.x, this.absolutePosition.y);
+        }
+    }
+
+    public calculateBoundingBox(): void {
+        if (this.childElements.length === 0) {
+            return;
+        }
+        for (let idx: number = 0, len: number = this.ChildElements.length; idx < len; ++idx) {
+            let childElement: BoundingBox = this.ChildElements[idx];
+            childElement.calculateBoundingBox();
+        }
+        let minLeft: number = Number.MAX_VALUE;
+        let maxRight: number = Number.MIN_VALUE;
+        let minTop: number = Number.MAX_VALUE;
+        let maxBottom: number = Number.MIN_VALUE;
+        let minMarginLeft: number = Number.MAX_VALUE;
+        let maxMarginRight: number = Number.MIN_VALUE;
+        let minMarginTop: number = Number.MAX_VALUE;
+        let maxMarginBottom: number = Number.MIN_VALUE;
+        if (this.isSymbol) {
+            minLeft = this.borderLeft;
+            maxRight = this.borderRight;
+            minTop = this.borderTop;
+            maxBottom = this.borderBottom;
+            minMarginLeft = this.borderMarginLeft;
+            maxMarginRight = this.borderMarginRight;
+            minMarginTop = this.borderMarginTop;
+            maxMarginBottom = this.borderMarginBottom;
+        }
+        for (let idx: number = 0, len: number = this.ChildElements.length; idx < len; ++idx) {
+            let childElement: BoundingBox = this.ChildElements[idx];
+            minLeft = Math.min(minLeft, childElement.relativePosition.x + childElement.borderLeft);
+            maxRight = Math.max(maxRight, childElement.relativePosition.x + childElement.borderRight);
+            minTop = Math.min(minTop, childElement.relativePosition.y + childElement.borderTop);
+            maxBottom = Math.max(maxBottom, childElement.relativePosition.y + childElement.borderBottom);
+            minMarginLeft = Math.min(minMarginLeft, childElement.relativePosition.x + childElement.borderMarginLeft);
+            maxMarginRight = Math.max(maxMarginRight, childElement.relativePosition.x + childElement.borderMarginRight);
+            minMarginTop = Math.min(minMarginTop, childElement.relativePosition.y + childElement.borderMarginTop);
+            maxMarginBottom = Math.max(maxMarginBottom, childElement.relativePosition.y + childElement.borderMarginBottom);
+        }
+        this.borderLeft = minLeft;
+        this.borderRight = maxRight;
+        this.borderTop = minTop;
+        this.borderBottom = maxBottom;
+        this.borderMarginLeft = minMarginLeft;
+        this.borderMarginRight = maxMarginRight;
+        this.borderMarginTop = minMarginTop;
+        this.borderMarginBottom = maxMarginBottom;
+        this.calculateRectangle();
+        this.calculateMarginRectangle();
+        this.xBordersHaveBeenSet = true;
+        this.yBordersHaveBeenSet = true;
+    }
+
+    public calculateTopBottomBorders(): void {
+        if (this.childElements.length === 0) {
+            return;
+        }
+        for (let idx: number = 0, len: number = this.ChildElements.length; idx < len; ++idx) {
+            let childElement: BoundingBox = this.ChildElements[idx];
+            childElement.calculateTopBottomBorders();
+        }
+        let minTop: number = Number.MAX_VALUE;
+        let maxBottom: number = Number.MIN_VALUE;
+        let minMarginTop: number = Number.MAX_VALUE;
+        let maxMarginBottom: number = Number.MIN_VALUE;
+        if (this.yBordersHaveBeenSet) {
+            minTop = this.borderTop;
+            maxBottom = this.borderBottom;
+            minMarginTop = this.borderMarginTop;
+            maxMarginBottom = this.borderMarginBottom;
+        }
+        for (let idx: number = 0, len: number = this.ChildElements.length; idx < len; ++idx) {
+            let childElement: BoundingBox = this.ChildElements[idx];
+            minTop = Math.min(minTop, childElement.relativePosition.y + childElement.borderTop);
+            maxBottom = Math.max(maxBottom, childElement.relativePosition.y + childElement.borderBottom);
+            minMarginTop = Math.min(minMarginTop, childElement.relativePosition.y + childElement.borderMarginTop);
+            maxMarginBottom = Math.max(maxMarginBottom, childElement.relativePosition.y + childElement.borderMarginBottom);
+        }
+        this.borderTop = minTop;
+        this.borderBottom = maxBottom;
+        this.borderMarginTop = minMarginTop;
+        this.borderMarginBottom = maxMarginBottom;
+        this.calculateRectangle();
+        this.calculateMarginRectangle();
+    }
+
+    public computeNonOverlappingPositionWithMargin(placementPsi: BoundingBox, direction: ColDirEnum, position: PointF2D): void {
+        this.RelativePosition = new PointF2D(position.x, position.y);
+        this.setAbsolutePositionFromParent();
+        let currentPosition: number = 0.0;
+        let hasBeenMoved: boolean = false;
+        do {
+            switch (direction) {
+                case ColDirEnum.Left:
+                case ColDirEnum.Right:
+                    currentPosition = this.relativePosition.x;
+                    placementPsi.calculateMarginPositionAlongDirection(this, direction);
+                    hasBeenMoved = Math.abs(currentPosition - this.relativePosition.x) > 0.001;
+                    break;
+                case ColDirEnum.Up:
+                case ColDirEnum.Down:
+                    currentPosition = this.relativePosition.y;
+                    placementPsi.calculateMarginPositionAlongDirection(this, direction);
+                    hasBeenMoved = Math.abs(currentPosition - this.relativePosition.y) > 0.001;
+                    break;
+                default:
+                    throw new ArgumentOutOfRangeException("direction");
+            }
+        }
+        while (hasBeenMoved);
+    }
+
+    public collisionDetection(psi: BoundingBox): boolean {
+        let overlapWidth: number = Math.min(this.AbsolutePosition.x + this.borderRight, psi.absolutePosition.x + psi.borderRight)
+            - Math.max(this.AbsolutePosition.x + this.borderLeft, psi.absolutePosition.x + psi.borderLeft);
+        let overlapHeight: number = Math.min(this.AbsolutePosition.y + this.borderBottom, psi.absolutePosition.y + psi.borderBottom)
+            - Math.max(this.AbsolutePosition.y + this.borderTop, psi.absolutePosition.y + psi.borderTop);
+        if (overlapWidth > 0 && overlapHeight > 0) {
+            return true;
+        }
+        return false;
+    }
+
+    public liesInsideBorders(psi: BoundingBox): boolean {
+        let leftBorderInside: boolean = (this.AbsolutePosition.x + this.borderLeft) <= (psi.absolutePosition.x + psi.borderLeft)
+            && (psi.absolutePosition.x + psi.borderLeft) <= (this.AbsolutePosition.x + this.borderRight);
+        let rightBorderInside: boolean = (this.AbsolutePosition.x + this.borderLeft) <= (psi.absolutePosition.x + psi.borderRight)
+            && (psi.absolutePosition.x + psi.borderRight) <= (this.AbsolutePosition.x + this.borderRight);
+        if (leftBorderInside && rightBorderInside) {
+            let topBorderInside: boolean = (this.AbsolutePosition.y + this.borderTop) <= (psi.absolutePosition.y + psi.borderTop)
+                && (psi.absolutePosition.y + psi.borderTop) <= (this.AbsolutePosition.y + this.borderBottom);
+            let bottomBorderInside: boolean = (this.AbsolutePosition.y + this.borderTop) <= (psi.absolutePosition.y + psi.borderBottom)
+                && (psi.absolutePosition.y + psi.borderBottom) <= (this.AbsolutePosition.y + this.borderBottom);
+            if (topBorderInside && bottomBorderInside) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public pointLiesInsideBorders(position: PointF2D): boolean {
+        let xInside: boolean = (this.AbsolutePosition.x + this.borderLeft) <= position.x && position.x <= (this.AbsolutePosition.x + this.borderRight);
+        if (xInside) {
+            let yInside: boolean = (this.AbsolutePosition.y + this.borderTop) <= position.y && position.y <= (this.AbsolutePosition.y + this.borderBottom);
+            if (yInside) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public marginCollisionDetection(psi: BoundingBox): boolean {
+        let overlapWidth: number = Math.min(this.AbsolutePosition.x + this.borderMarginRight, psi.absolutePosition.x + psi.borderMarginRight)
+            - Math.max(this.AbsolutePosition.x + this.borderMarginLeft, psi.absolutePosition.x + psi.borderMarginLeft);
+        let overlapHeight: number = Math.min(this.AbsolutePosition.y + this.borderMarginBottom, psi.absolutePosition.y + psi.borderMarginBottom)
+            - Math.max(this.AbsolutePosition.y + this.borderMarginTop, psi.absolutePosition.y + psi.borderMarginTop);
+        if (overlapWidth > 0 && overlapHeight > 0) {
+            return true;
+        }
+        return false;
+    }
+
+    public liesInsideMargins(psi: BoundingBox): boolean {
+        let leftMarginInside: boolean = (this.AbsolutePosition.x + this.borderMarginLeft) <= (psi.absolutePosition.x + psi.borderMarginLeft)
+            && (psi.absolutePosition.x + psi.borderMarginLeft) <= (this.AbsolutePosition.x + this.borderMarginRight);
+        let rightMarginInside: boolean = (this.AbsolutePosition.x + this.borderMarginLeft) <= (psi.absolutePosition.x + psi.borderMarginRight)
+            && (psi.absolutePosition.x + psi.borderMarginRight) <= (this.AbsolutePosition.x + this.borderMarginRight);
+        if (leftMarginInside && rightMarginInside) {
+            let topMarginInside: boolean = (this.AbsolutePosition.y + this.borderMarginTop) <= (psi.absolutePosition.y + psi.borderMarginTop)
+                && (psi.absolutePosition.y + psi.borderMarginTop) <= (this.AbsolutePosition.y + this.borderMarginBottom);
+            let bottomMarginInside: boolean = (this.AbsolutePosition.y + this.borderMarginTop) <= (psi.absolutePosition.y + psi.borderMarginBottom)
+                && (psi.absolutePosition.y + psi.borderMarginBottom) <= (this.AbsolutePosition.y + this.borderMarginBottom);
+            if (topMarginInside && bottomMarginInside) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public pointLiesInsideMargins(position: PointF2D): boolean {
+        let xInside: boolean = (this.AbsolutePosition.x + this.borderMarginLeft) <= position.x
+            && position.x <= (this.AbsolutePosition.x + this.borderMarginRight);
+        if (xInside) {
+            let yInside: boolean = (this.AbsolutePosition.y + this.borderMarginTop) <= position.y
+                && position.y <= (this.AbsolutePosition.y + this.borderMarginBottom);
+            if (yInside) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public computeNonOverlappingPosition(placementPsi: BoundingBox, direction: ColDirEnum, position: PointF2D): void {
+        this.RelativePosition = new PointF2D(position.x, position.y);
+        this.setAbsolutePositionFromParent();
+        let currentPosition: number = 0.0;
+        let hasBeenMoved: boolean = false;
+        do {
+            switch (direction) {
+                case ColDirEnum.Left:
+                case ColDirEnum.Right:
+                    currentPosition = this.relativePosition.x;
+                    placementPsi.calculatePositionAlongDirection(this, direction);
+                    hasBeenMoved = Math.abs(currentPosition - this.relativePosition.x) > 0.0001;
+                    break;
+                case ColDirEnum.Up:
+                case ColDirEnum.Down:
+                    currentPosition = this.relativePosition.y;
+                    placementPsi.calculatePositionAlongDirection(this, direction);
+                    hasBeenMoved = Math.abs(currentPosition - this.relativePosition.y) > 0.0001;
+                    break;
+                default:
+                    throw new ArgumentOutOfRangeException("direction");
+            }
+        }
+        while (hasBeenMoved);
+    }
+
+    public getClickedObjectOfType<T>(clickPosition: PointF2D): T {
+        let obj: Object = this.dataObject;
+        if (this.pointLiesInsideBorders(clickPosition) && (<T>obj !== undefined)) {
+            return (obj as T);
+        }
+        for (let idx: number = 0, len: number = this.childElements.length; idx < len; ++idx) {
+            let psi: BoundingBox = this.childElements[idx];
+            let innerObject: Object = psi.getClickedObjectOfType<T>(clickPosition);
+            if (innerObject !== undefined) {
+                return (innerObject as T);
+            }
+        }
+        return undefined;
+    }
+
+    public getObjectsInRegion<T>(region: BoundingBox, liesInside: boolean = true): T[] {
+        if (<T>this.dataObject !== undefined) {
+            if (liesInside) {
+                if (region.liesInsideBorders(this)) {
+                    return [this.dataObject as T];
+                }
+            } else {
+                if (region.collisionDetection(this)) {
+                    return [this.dataObject as T];
+                }
+            }
+            // FIXME Andrea: add here "return []"?
+        }
+        let result: T[] = [];
+        for (let child of this.childElements) {
+            result.concat(child.getObjectsInRegion<T>(region, liesInside));
+        }
+        return result;
+        //return this.childElements.SelectMany(psi => psi.getObjectsInRegion<T>(region, liesInside));
+    }
+
+    protected calculateRectangle(): void {
+        this.upperLeftCorner = new PointF2D(this.borderLeft, this.borderTop);
+        this.size = new SizeF2D(this.borderRight - this.borderLeft, this.borderBottom - this.borderTop);
+        this.boundingRectangle = RectangleF2D.createFromLocationAndSize(this.upperLeftCorner, this.size);
+    }
+
+    protected calculateMarginRectangle(): void {
+        this.upperLeftMarginCorner = new PointF2D(this.borderMarginLeft, this.borderMarginTop);
+        this.marginSize = new SizeF2D(this.borderMarginRight - this.borderMarginLeft, this.borderMarginBottom - this.borderMarginTop);
+        this.boundingMarginRectangle = RectangleF2D.createFromLocationAndSize(this.upperLeftMarginCorner, this.marginSize);
+    }
+
+    private calculateMarginPositionAlongDirection(toBePlaced: BoundingBox, direction: ColDirEnum): void {
+        if (this === toBePlaced) {
+            return;
+        }
+        if (this.isSymbol && this.marginCollisionDetection(toBePlaced)) {
+            let shiftDistance: number = 0;
+            switch (direction) {
+                case ColDirEnum.Left:
+                    shiftDistance = (this.absolutePosition.x + this.borderMarginLeft) - (toBePlaced.absolutePosition.x + toBePlaced.borderMarginRight);
+                    toBePlaced.relativePosition.x += shiftDistance;
+                    toBePlaced.absolutePosition.x += shiftDistance;
+                    return;
+                case ColDirEnum.Right:
+                    shiftDistance = (this.absolutePosition.x + this.borderMarginRight) - (toBePlaced.absolutePosition.x + toBePlaced.borderMarginLeft);
+                    toBePlaced.relativePosition.x += shiftDistance;
+                    toBePlaced.absolutePosition.x += shiftDistance;
+                    return;
+                case ColDirEnum.Up:
+                    shiftDistance = (this.absolutePosition.y + this.borderMarginTop) - (toBePlaced.absolutePosition.y + toBePlaced.borderMarginBottom);
+                    toBePlaced.relativePosition.y += shiftDistance;
+                    toBePlaced.absolutePosition.y += shiftDistance;
+                    return;
+                case ColDirEnum.Down:
+                    shiftDistance = (this.absolutePosition.y + this.borderMarginBottom) - (toBePlaced.absolutePosition.y + toBePlaced.borderMarginTop);
+                    toBePlaced.relativePosition.y += shiftDistance;
+                    toBePlaced.absolutePosition.y += shiftDistance;
+                    return;
+                default:
+                    throw new ArgumentOutOfRangeException("direction");
+            }
+        }
+        for (let idx: number = 0, len: number = this.ChildElements.length; idx < len; ++idx) {
+            let childElement: BoundingBox = this.ChildElements[idx];
+            childElement.calculateMarginPositionAlongDirection(toBePlaced, direction);
+        }
+    }
+
+    private calculatePositionAlongDirection(toBePlaced: BoundingBox, direction: ColDirEnum): void {
+        if (this === toBePlaced) {
+            return;
+        }
+        if (this.isSymbol && this.collisionDetection(toBePlaced)) {
+            let shiftDistance: number;
+            switch (direction) {
+                case ColDirEnum.Left:
+                    shiftDistance = (this.absolutePosition.x + this.borderLeft) - (toBePlaced.absolutePosition.x + toBePlaced.borderRight);
+                    toBePlaced.relativePosition.x += shiftDistance;
+                    toBePlaced.absolutePosition.x += shiftDistance;
+                    return;
+                case ColDirEnum.Right:
+                    shiftDistance = (this.absolutePosition.x + this.borderRight) - (toBePlaced.absolutePosition.x + toBePlaced.borderLeft);
+                    toBePlaced.relativePosition.x += shiftDistance;
+                    toBePlaced.absolutePosition.x += shiftDistance;
+                    return;
+                case ColDirEnum.Up:
+                    shiftDistance = (this.absolutePosition.y + this.borderTop) - (toBePlaced.absolutePosition.y + toBePlaced.borderBottom);
+                    toBePlaced.relativePosition.y += shiftDistance;
+                    toBePlaced.absolutePosition.y += shiftDistance;
+                    return;
+                case ColDirEnum.Down:
+                    shiftDistance = (this.absolutePosition.y + this.borderBottom) - (toBePlaced.absolutePosition.y + toBePlaced.borderTop);
+                    toBePlaced.relativePosition.y += shiftDistance;
+                    toBePlaced.absolutePosition.y += shiftDistance;
+                    return;
+                default:
+                    throw new ArgumentOutOfRangeException("direction");
+            }
+        }
+        for (let idx: number = 0, len: number = this.ChildElements.length; idx < len; ++idx) {
+            let childElement: BoundingBox = this.ChildElements[idx];
+            childElement.calculatePositionAlongDirection(toBePlaced, direction);
+        }
+    }
+}
+
+export enum ColDirEnum {
+    Left = 0,
+    Right = 1,
+    Up = 2,
+    Down = 3
+}

+ 5 - 0
src/MusicalScore/Graphical/Clickable.ts

@@ -0,0 +1,5 @@
+import {GraphicalObject} from "./GraphicalObject";
+
+export class Clickable extends GraphicalObject {
+    public dataObject: Object;
+}

+ 61 - 0
src/MusicalScore/Graphical/DrawingEnums.ts

@@ -0,0 +1,61 @@
+export enum OutlineAndFillStyleEnum {
+    BaseWritingColor,
+    FollowingCursor,
+    AlternativeFollowingCursor,
+    PlaybackCursor,
+    Highlighted,
+    ErrorUnderlay,
+    Selected,
+    SelectionSymbol,
+    DebugColor1,
+    DebugColor2,
+    DebugColor3,
+    SplitScreenDivision,
+    GreyTransparentOverlay,
+    MarkedArea1,
+    MarkedArea2,
+    MarkedArea3,
+    MarkedArea4,
+    MarkedArea5,
+    MarkedArea6,
+    MarkedArea7,
+    MarkedArea8,
+    MarkedArea9,
+    MarkedArea10,
+    Comment1,
+    Comment2,
+    Comment3,
+    Comment4,
+    Comment5,
+    Comment6,
+    Comment7,
+    Comment8,
+    Comment9,
+    Comment10
+}
+export enum StyleSets {
+    MarkedArea,
+    Comment
+}
+export enum GraphicalLayers {
+    Background,
+    Highlight,
+    MeasureError,
+    SelectionSymbol,
+    Cursor,
+    PSI_Debug,
+    Notes,
+    Comment,
+    Debug_above
+}
+export enum NoteState {
+    Normal,
+    Selected,
+    Follow_Confirmed,
+    QFeedback_NotFound,
+    QFeedback_OK,
+    QFeedback_Perfect,
+    Debug1,
+    Debug2,
+    Debug3
+}

+ 26 - 0
src/MusicalScore/Graphical/DrawingMode.ts

@@ -0,0 +1,26 @@
+export enum DrawingMode {
+    All,
+    NoOverlays,
+    Leadsheet
+}
+
+export enum MusicSymbolDrawingStyle {
+    Normal,
+    Disabled,
+    Selected,
+    Clickable,
+    PlaybackSymbols,
+    FollowSymbols,
+    QFeedbackNotFound,
+    QFeedbackOk,
+    QFeedbackPerfect,
+    Debug1,
+    Debug2,
+    Debug3
+}
+
+export enum PhonicScoreModes {
+    Following,
+    Midi,
+    Manual
+}

+ 47 - 0
src/MusicalScore/Graphical/DrawingParameters.ts

@@ -0,0 +1,47 @@
+export class DrawingParameters {
+    public drawHighlights: boolean;
+    public drawErrors: boolean;
+    public drawSelectionStartSymbol: boolean;
+    public drawSelectionEndSymbol: boolean;
+    public drawCursors: boolean;
+    public drawActivitySymbols: boolean;
+    public drawScrollIndicator: boolean;
+    public drawComments: boolean;
+    public drawMarkedAreas: boolean;
+
+    public setForAllOn(): void {
+        this.drawHighlights = true;
+        this.drawErrors = true;
+        this.drawSelectionStartSymbol = true;
+        this.drawSelectionEndSymbol = true;
+        this.drawCursors = true;
+        this.drawActivitySymbols = true;
+        this.drawScrollIndicator = true;
+        this.drawComments = true;
+        this.drawMarkedAreas = true;
+    }
+
+    public setForThumbmail(): void {
+        this.drawHighlights = false;
+        this.drawErrors = false;
+        this.drawSelectionStartSymbol = false;
+        this.drawSelectionStartSymbol = false;
+        this.drawCursors = false;
+        this.drawActivitySymbols = false;
+        this.drawScrollIndicator = false;
+        this.drawComments = true;
+        this.drawMarkedAreas = true;
+    }
+
+    public setForLeadsheet(): void {
+        this.drawHighlights = false;
+        this.drawErrors = false;
+        this.drawSelectionStartSymbol = true;
+        this.drawSelectionEndSymbol = true;
+        this.drawCursors = true;
+        this.drawActivitySymbols = false;
+        this.drawScrollIndicator = true;
+        this.drawComments = true;
+        this.drawMarkedAreas = true;
+    }
+}

+ 1123 - 0
src/MusicalScore/Graphical/EngravingRules.ts

@@ -0,0 +1,1123 @@
+import {PagePlacementEnum} from "./GraphicalMusicPage";
+//import {MusicSymbol} from "./MusicSymbol";
+import {Logging} from "../../Common/logging";
+
+export class EngravingRules {
+    private static rules: EngravingRules;
+    private static unit: number = 1.0;
+    private samplingUnit: number;
+    private staccatoShorteningFactor: number;
+    private sheetTitleHeight: number;
+    private sheetSubtitleHeight: number;
+    private sheetMinimumDistanceBetweenTitleAndSubtitle: number;
+    private sheetComposerHeight: number;
+    private sheetAuthorHeight: number;
+    private pagePlacementEnum: PagePlacementEnum;
+    private pageHeight: number;
+    private pageTopMargin: number;
+    private pageBottomMargin: number;
+    private pageLeftMargin: number;
+    private pageRightMargin: number;
+    private titleTopDistance: number;
+    private titleBottomDistance: number;
+    private systemDistance: number;
+    private systemLeftMargin: number;
+    private systemRightMargin: number;
+    private firstSystemMargin: number;
+    private systemLabelsRightMargin: number;
+    private systemComposerDistance: number;
+    private instrumentLabelTextHeight: number;
+    private minimumAllowedDistanceBetweenSystems: number;
+    private lastSystemMaxScalingFactor: number;
+    private staffDistance: number;
+    private betweenStaffDistance: number;
+    private staffHeight: number;
+    private betweenStaffLinesDistance: number;
+    private beamWidth: number;
+    private beamSpaceWidth: number;
+    private beamForwardLength: number;
+    private clefLeftMargin: number;
+    private clefRightMargin: number;
+    private betweenKeySymbolsDistance: number;
+    private keyRightMargin: number;
+    private rhythmRightMargin: number;
+    private inStaffClefScalingFactor: number;
+    private distanceBetweenNaturalAndSymbolWhenCancelling: number;
+    private noteHelperLinesOffset: number;
+    private measureLeftMargin: number;
+    private measureRightMargin: number;
+    private distanceBetweenLastInstructionAndRepetitionBarline: number;
+    private arpeggioDistance: number;
+    private idealStemLength: number;
+    private stemNoteHeadBorderYOffset: number;
+    private stemWidth: number;
+    private stemMargin: number;
+    private stemMinLength: number;
+    private stemMaxLength: number;
+    private beamSlopeMaxAngle: number;
+    private stemMinAllowedDistanceBetweenNoteHeadAndBeamLine: number;
+    private graceNoteScalingFactor: number;
+    private graceNoteXOffset: number;
+    private wedgeOpeningLength: number;
+    private wedgeMeasureEndOpeningLength: number;
+    private wedgeMeasureBeginOpeningLength: number;
+    private wedgePlacementAboveY: number;
+    private wedgePlacementBelowY: number;
+    private wedgeHorizontalMargin: number;
+    private wedgeVerticalMargin: number;
+    private distanceOffsetBetweenTwoHorizontallyCrossedWedges: number;
+    private wedgeMinLength: number;
+    private distanceBetweenAdjacentDynamics: number;
+    private tempoChangeMeasureValitidy: number;
+    private tempoContinousFactor: number;
+    private staccatoScalingFactor: number;
+    private betweenDotsDistance: number;
+    private ornamentAccidentalScalingFactor: number;
+    private chordSymbolTextHeight: number;
+    //private chordSymbolYOffset: number;
+    private fingeringLabelFontHeight: number;
+    private measureNumberLabelHeight: number;
+    private measureNumberLabelOffset: number;
+    private tupletNumberLabelHeight: number;
+    private tupletNumberYOffset: number;
+    private labelMarginBorderFactor: number;
+    private tupletVerticalLineLength: number;
+    private repetitionEndingLabelHeight: number;
+    private repetitionEndingLabelXOffset: number;
+    private repetitionEndingLabelYOffset: number;
+    private repetitionEndingLineYLowerOffset: number;
+    private repetitionEndingLineYUpperOffset: number;
+    private lyricsHeight: number;
+    private verticalBetweenLyricsDistance: number;
+    private betweenSyllabelMaximumDistance: number;
+    private minimumDistanceBetweenDashes: number;
+    private bezierCurveStepSize: number;
+    private tPower3: number[];
+    private oneMinusTPower3: number[];
+    private factorOne: number[];
+    private factorTwo: number[];
+    private tieGhostObjectWidth: number;
+    private tieYPositionOffsetFactor: number;
+    private minimumNeededXspaceForTieGhostObject: number;
+    private tieHeightMinimum: number;
+    private tieHeightMaximum: number;
+    private tieHeightInterpolationK: number;
+    private tieHeightInterpolationD: number;
+    private slurNoteHeadYOffset: number;
+    private slurStemXOffset: number;
+    private slurSlopeMaxAngle: number;
+    private slurTangentMinAngle: number;
+    private slurTangentMaxAngle: number;
+    private slursStartingAtSameStaffEntryYOffset: number;
+    private instantaniousTempoTextHeight: number;
+    private continuousDynamicTextHeight: number;
+    private moodTextHeight: number;
+    private unknownTextHeight: number;
+    private continuousTempoTextHeight: number;
+    private staffLineWidth: number;
+    private ledgerLineWidth: number;
+    private wedgeLineWidth: number;
+    private tupletLineWidth: number;
+    private lyricUnderscoreLineWidth: number;
+    private systemThinLineWidth: number;
+    private systemBoldLineWidth: number;
+    private systemRepetitionEndingLineWidth: number;
+    private systemDotWidth: number;
+    private distanceBetweenVerticalSystemLines: number;
+    private distanceBetweenDotAndLine: number;
+    private octaveShiftLineWidth: number;
+    private octaveShiftVerticalLineLength: number;
+    private graceLineWidth: number;
+    private minimumStaffLineDistance: number;
+    private minimumCrossedBeamDifferenceMargin: number;
+    private displacedNoteMargin: number;
+    private minNoteDistance: number;
+    private subMeasureXSpacingThreshold: number;
+    private measureDynamicsMaxScalingFactor: number;
+    private maxInstructionsConstValue: number;
+    private noteDistances: number[] = [1.0, 1.0, 1.3, 1.6, 2.0, 2.5, 3.0, 4.0];
+    private noteDistancesScalingFactors: number[] = [1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0];
+    private durationDistanceDict: {[_: number]: number; } = {};
+    private durationScalingDistanceDict: {[_: number]: number; } = {};
+    constructor() {
+        this.samplingUnit = EngravingRules.unit * 3;
+        this.sheetTitleHeight = 4.0;
+        this.sheetSubtitleHeight = 2.0;
+        this.sheetMinimumDistanceBetweenTitleAndSubtitle = 1.0;
+        this.sheetComposerHeight = 2.0;
+        this.sheetAuthorHeight = 2.0;
+        this.pagePlacementEnum = PagePlacementEnum.Down;
+        this.pageHeight = 100001.0;
+        this.pageTopMargin = 5.0;
+        this.pageBottomMargin = 5.0;
+        this.pageLeftMargin = 5.0;
+        this.pageRightMargin = 5.0;
+        this.titleTopDistance = 9.0;
+        this.titleBottomDistance = 1.0;
+        this.staffDistance = 7.0;
+        this.betweenStaffDistance = 5.0;
+        this.staffHeight = 4.0;
+        this.betweenStaffLinesDistance = EngravingRules.unit;
+        this.systemDistance = 10.0;
+        this.systemLeftMargin = 0.0;
+        this.systemRightMargin = 0.0;
+        this.firstSystemMargin = 15.0;
+        this.systemLabelsRightMargin = 2.0;
+        this.systemComposerDistance = 2.0;
+        this.instrumentLabelTextHeight = 2;
+        this.minimumAllowedDistanceBetweenSystems = 3.0;
+        this.lastSystemMaxScalingFactor = 1.4;
+        this.beamWidth = EngravingRules.unit / 2.0;
+        this.beamSpaceWidth = EngravingRules.unit / 3.0;
+        this.beamForwardLength = 1.25 * EngravingRules.unit;
+        this.clefLeftMargin = 0.5;
+        this.clefRightMargin = 0.75;
+        this.betweenKeySymbolsDistance = 0.2;
+        this.keyRightMargin = 0.75;
+        this.rhythmRightMargin = 1.25;
+        this.inStaffClefScalingFactor = 0.8;
+        this.distanceBetweenNaturalAndSymbolWhenCancelling = 0.4;
+        this.noteHelperLinesOffset = 0.25;
+        this.measureLeftMargin = 0.7;
+        this.measureRightMargin = 0.0;
+        this.distanceBetweenLastInstructionAndRepetitionBarline = 1.0;
+        this.arpeggioDistance = 0.6;
+        this.staccatoShorteningFactor = 2;
+        this.idealStemLength = 3.0;
+        this.stemNoteHeadBorderYOffset = 0.2;
+        this.stemWidth = 0.13;
+        this.stemMargin = 0.2;
+        this.stemMinLength = 2.5;
+        this.stemMaxLength = 4.5;
+        this.beamSlopeMaxAngle = 10.0;
+        this.stemMinAllowedDistanceBetweenNoteHeadAndBeamLine = 1.0;
+        this.graceNoteScalingFactor = 0.6;
+        this.graceNoteXOffset = 0.2;
+        this.wedgeOpeningLength = 1.2;
+        this.wedgeMeasureEndOpeningLength = 0.75;
+        this.wedgeMeasureBeginOpeningLength = 0.75;
+        this.wedgePlacementAboveY = -1.5;
+        this.wedgePlacementBelowY = 1.5;
+        this.wedgeHorizontalMargin = 0.6;
+        this.wedgeVerticalMargin = 0.5;
+        this.distanceOffsetBetweenTwoHorizontallyCrossedWedges = 0.3;
+        this.wedgeMinLength = 2.0;
+        this.distanceBetweenAdjacentDynamics = 0.75;
+        this.tempoChangeMeasureValitidy = 4;
+        this.tempoContinousFactor = 0.7;
+        this.staccatoScalingFactor = 0.8;
+        this.betweenDotsDistance = 0.8;
+        this.ornamentAccidentalScalingFactor = 0.65;
+        this.chordSymbolTextHeight = 2.0;
+        this.fingeringLabelFontHeight = 1.7;
+        this.measureNumberLabelHeight = 1.5 * EngravingRules.unit;
+        this.measureNumberLabelOffset = 2;
+        this.tupletNumberLabelHeight = 1.5 * EngravingRules.unit;
+        this.tupletNumberYOffset = 0.5;
+        this.labelMarginBorderFactor = 0.1;
+        this.tupletVerticalLineLength = 0.5;
+        this.bezierCurveStepSize = 1000;
+        this.calculateCurveParametersArrays();
+        this.tieGhostObjectWidth = 0.75;
+        this.tieYPositionOffsetFactor = 0.3;
+        this.minimumNeededXspaceForTieGhostObject = 1.0;
+        this.tieHeightMinimum = 0.28;
+        this.tieHeightMaximum = 1.2;
+        this.tieHeightInterpolationK = 0.0288;
+        this.tieHeightInterpolationD = 0.136;
+        this.slurNoteHeadYOffset = 0.5;
+        this.slurStemXOffset = 0.3;
+        this.slurSlopeMaxAngle = 15.0;
+        this.slurTangentMinAngle = 30.0;
+        this.slurTangentMaxAngle = 80.0;
+        this.slursStartingAtSameStaffEntryYOffset = 0.8;
+        this.repetitionEndingLabelHeight = 2.0;
+        this.repetitionEndingLabelXOffset = 0.5;
+        this.repetitionEndingLabelYOffset = 0.3;
+        this.repetitionEndingLineYLowerOffset = 0.5;
+        this.repetitionEndingLineYUpperOffset = 0.3;
+        this.lyricsHeight = 2.0;
+        this.verticalBetweenLyricsDistance = 0.5;
+        this.betweenSyllabelMaximumDistance = 10.0;
+        this.minimumDistanceBetweenDashes = 5.0;
+        this.instantaniousTempoTextHeight = 2.3;
+        this.continuousDynamicTextHeight = 2.3;
+        this.moodTextHeight = 2.3;
+        this.unknownTextHeight = 2.0;
+        this.continuousTempoTextHeight = 2.3;
+        this.staffLineWidth = 0.12;
+        this.ledgerLineWidth = 0.12;
+        this.wedgeLineWidth = 0.12;
+        this.tupletLineWidth = 0.12;
+        this.lyricUnderscoreLineWidth = 0.12;
+        this.systemThinLineWidth = 0.12;
+        this.systemBoldLineWidth = EngravingRules.unit / 2.0;
+        this.systemRepetitionEndingLineWidth = 0.12;
+        this.systemDotWidth = EngravingRules.unit / 5.0;
+        this.distanceBetweenVerticalSystemLines = 0.35;
+        this.distanceBetweenDotAndLine = 0.7;
+        this.octaveShiftLineWidth = 0.12;
+        this.octaveShiftVerticalLineLength = EngravingRules.unit;
+        this.graceLineWidth = this.staffLineWidth * this.GraceNoteScalingFactor;
+        this.minimumStaffLineDistance = 1.0;
+        this.minimumCrossedBeamDifferenceMargin = 0.0001;
+        this.displacedNoteMargin = 0.1;
+        this.minNoteDistance = 2.0;
+        this.subMeasureXSpacingThreshold = 35;
+        this.measureDynamicsMaxScalingFactor = 2.5;
+        this.populateDictionaries();
+        try {
+            this.maxInstructionsConstValue = this.ClefLeftMargin + this.ClefRightMargin + this.KeyRightMargin + this.RhythmRightMargin + 11;
+            //if (FontInfo.Info !== undefined) {
+            //    this.maxInstructionsConstValue += FontInfo.Info.getBoundingBox(MusicSymbol.G_CLEF).width
+            //        + FontInfo.Info.getBoundingBox(MusicSymbol.FOUR).width
+            //        + 7 * FontInfo.Info.getBoundingBox(MusicSymbol.SHARP).width;
+            //}
+        } catch (ex) {
+            Logging.log("EngravingRules()", ex);
+        }
+
+    }
+    public static get Rules(): EngravingRules {
+        return EngravingRules.rules !== undefined ? EngravingRules.rules : (EngravingRules.rules = new EngravingRules());
+    }
+    public get SamplingUnit(): number {
+        return this.samplingUnit;
+    }
+    public get SheetTitleHeight(): number {
+        return this.sheetTitleHeight;
+    }
+    public set SheetTitleHeight(value: number) {
+        this.sheetTitleHeight = value;
+    }
+    public get SheetSubtitleHeight(): number {
+        return this.sheetSubtitleHeight;
+    }
+    public set SheetSubtitleHeight(value: number) {
+        this.sheetSubtitleHeight = value;
+    }
+    public get SheetMinimumDistanceBetweenTitleAndSubtitle(): number {
+        return this.sheetMinimumDistanceBetweenTitleAndSubtitle;
+    }
+    public set SheetMinimumDistanceBetweenTitleAndSubtitle(value: number) {
+        this.sheetMinimumDistanceBetweenTitleAndSubtitle = value;
+    }
+    public get SheetComposerHeight(): number {
+        return this.sheetComposerHeight;
+    }
+    public set SheetComposerHeight(value: number) {
+        this.sheetComposerHeight = value;
+    }
+    public get SheetAuthorHeight(): number {
+        return this.sheetAuthorHeight;
+    }
+    public set SheetAuthorHeight(value: number) {
+        this.sheetAuthorHeight = value;
+    }
+    public get PagePlacement(): PagePlacementEnum {
+        return this.pagePlacementEnum;
+    }
+    public set PagePlacement(value: PagePlacementEnum) {
+        this.pagePlacementEnum = value;
+    }
+    public get PageHeight(): number {
+        return this.pageHeight;
+    }
+    public set PageHeight(value: number) {
+        this.pageHeight = value;
+    }
+    public get PageTopMargin(): number {
+        return this.pageTopMargin;
+    }
+    public set PageTopMargin(value: number) {
+        this.pageTopMargin = value;
+    }
+    public get PageBottomMargin(): number {
+        return this.pageBottomMargin;
+    }
+    public set PageBottomMargin(value: number) {
+        this.pageBottomMargin = value;
+    }
+    public get PageLeftMargin(): number {
+        return this.pageLeftMargin;
+    }
+    public set PageLeftMargin(value: number) {
+        this.pageLeftMargin = value;
+    }
+    public get PageRightMargin(): number {
+        return this.pageRightMargin;
+    }
+    public set PageRightMargin(value: number) {
+        this.pageRightMargin = value;
+    }
+    public get TitleTopDistance(): number {
+        return this.titleTopDistance;
+    }
+    public set TitleTopDistance(value: number) {
+        this.titleTopDistance = value;
+    }
+    public get TitleBottomDistance(): number {
+        return this.titleBottomDistance;
+    }
+    public set TitleBottomDistance(value: number) {
+        this.titleBottomDistance = value;
+    }
+    public get SystemComposerDistance(): number {
+        return this.systemComposerDistance;
+    }
+    public set SystemComposerDistance(value: number) {
+        this.systemComposerDistance = value;
+    }
+    public get InstrumentLabelTextHeight(): number {
+        return this.instrumentLabelTextHeight;
+    }
+    public set InstrumentLabelTextHeight(value: number) {
+        this.instrumentLabelTextHeight = value;
+    }
+    public get SystemDistance(): number {
+        return this.systemDistance;
+    }
+    public set SystemDistance(value: number) {
+        this.systemDistance = value;
+    }
+    public get SystemLeftMargin(): number {
+        return this.systemLeftMargin;
+    }
+    public set SystemLeftMargin(value: number) {
+        this.systemLeftMargin = value;
+    }
+    public get SystemRightMargin(): number {
+        return this.systemRightMargin;
+    }
+    public set SystemRightMargin(value: number) {
+        this.systemRightMargin = value;
+    }
+    public get FirstSystemMargin(): number {
+        return this.firstSystemMargin;
+    }
+    public set FirstSystemMargin(value: number) {
+        this.firstSystemMargin = value;
+    }
+    public get SystemLabelsRightMargin(): number {
+        return this.systemLabelsRightMargin;
+    }
+    public get MinimumAllowedDistanceBetweenSystems(): number {
+        return this.minimumAllowedDistanceBetweenSystems;
+    }
+    public set MinimumAllowedDistanceBetweenSystems(value: number) {
+        this.minimumAllowedDistanceBetweenSystems = value;
+    }
+    public get LastSystemMaxScalingFactor(): number {
+        return this.lastSystemMaxScalingFactor;
+    }
+    public set LastSystemMaxScalingFactor(value: number) {
+        this.lastSystemMaxScalingFactor = value;
+    }
+    public get StaffDistance(): number {
+        return this.staffDistance;
+    }
+    public set StaffDistance(value: number) {
+        this.staffDistance = value;
+    }
+    public get BetweenStaffDistance(): number {
+        return this.betweenStaffDistance;
+    }
+    public set BetweenStaffDistance(value: number) {
+        this.betweenStaffDistance = value;
+    }
+    public get StaffHeight(): number {
+        return this.staffHeight;
+    }
+    public set StaffHeight(value: number) {
+        this.staffHeight = value;
+    }
+    public get BetweenStaffLinesDistance(): number {
+        return this.betweenStaffLinesDistance;
+    }
+    public set BetweenStaffLinesDistance(value: number) {
+        this.betweenStaffLinesDistance = value;
+    }
+    public get BeamWidth(): number {
+        return this.beamWidth;
+    }
+    public set BeamWidth(value: number) {
+        this.beamWidth = value;
+    }
+    public get BeamSpaceWidth(): number {
+        return this.beamSpaceWidth;
+    }
+    public set BeamSpaceWidth(value: number) {
+        this.beamSpaceWidth = value;
+    }
+    public get BeamForwardLength(): number {
+        return this.beamForwardLength;
+    }
+    public set BeamForwardLength(value: number) {
+        this.beamForwardLength = value;
+    }
+    public get BetweenKeySymbolsDistance(): number {
+        return this.betweenKeySymbolsDistance;
+    }
+    public set BetweenKeySymbolsDistance(value: number) {
+        this.betweenKeySymbolsDistance = value;
+    }
+    public get ClefLeftMargin(): number {
+        return this.clefLeftMargin;
+    }
+    public set ClefLeftMargin(value: number) {
+        this.clefLeftMargin = value;
+    }
+    public get ClefRightMargin(): number {
+        return this.clefRightMargin;
+    }
+    public set ClefRightMargin(value: number) {
+        this.clefRightMargin = value;
+    }
+    public get KeyRightMargin(): number {
+        return this.keyRightMargin;
+    }
+    public set KeyRightMargin(value: number) {
+        this.keyRightMargin = value;
+    }
+    public get RhythmRightMargin(): number {
+        return this.rhythmRightMargin;
+    }
+    public set RhythmRightMargin(value: number) {
+        this.rhythmRightMargin = value;
+    }
+    public get InStaffClefScalingFactor(): number {
+        return this.inStaffClefScalingFactor;
+    }
+    public set InStaffClefScalingFactor(value: number) {
+        this.inStaffClefScalingFactor = value;
+    }
+    public get DistanceBetweenNaturalAndSymbolWhenCancelling(): number {
+        return this.distanceBetweenNaturalAndSymbolWhenCancelling;
+    }
+    public set DistanceBetweenNaturalAndSymbolWhenCancelling(value: number) {
+        this.distanceBetweenNaturalAndSymbolWhenCancelling = value;
+    }
+    public get NoteHelperLinesOffset(): number {
+        return this.noteHelperLinesOffset;
+    }
+    public set NoteHelperLinesOffset(value: number) {
+        this.noteHelperLinesOffset = value;
+    }
+    public get MeasureLeftMargin(): number {
+        return this.measureLeftMargin;
+    }
+    public set MeasureLeftMargin(value: number) {
+        this.measureLeftMargin = value;
+    }
+    public get MeasureRightMargin(): number {
+        return this.measureRightMargin;
+    }
+    public set MeasureRightMargin(value: number) {
+        this.measureRightMargin = value;
+    }
+    public get DistanceBetweenLastInstructionAndRepetitionBarline(): number {
+        return this.distanceBetweenLastInstructionAndRepetitionBarline;
+    }
+    public set DistanceBetweenLastInstructionAndRepetitionBarline(value: number) {
+        this.distanceBetweenLastInstructionAndRepetitionBarline = value;
+    }
+    public get ArpeggioDistance(): number {
+        return this.arpeggioDistance;
+    }
+    public set ArpeggioDistance(value: number) {
+        this.arpeggioDistance = value;
+    }
+    public get StaccatoShorteningFactor(): number {
+        return this.staccatoShorteningFactor;
+    }
+    public set StaccatoShorteningFactor(value: number) {
+        this.staccatoShorteningFactor = value;
+    }
+    public get IdealStemLength(): number {
+        return this.idealStemLength;
+    }
+    public set IdealStemLength(value: number) {
+        this.idealStemLength = value;
+    }
+    public get StemNoteHeadBorderYOffset(): number {
+        return this.stemNoteHeadBorderYOffset;
+    }
+    public set StemNoteHeadBorderYOffset(value: number) {
+        this.stemNoteHeadBorderYOffset = value;
+    }
+    public get StemWidth(): number {
+        return this.stemWidth;
+    }
+    public set StemWidth(value: number) {
+        this.stemWidth = value;
+    }
+    public get StemMargin(): number {
+        return this.stemMargin;
+    }
+    public set StemMargin(value: number) {
+        this.stemMargin = value;
+    }
+    public get StemMinLength(): number {
+        return this.stemMinLength;
+    }
+    public set StemMinLength(value: number) {
+        this.stemMinLength = value;
+    }
+    public get StemMaxLength(): number {
+        return this.stemMaxLength;
+    }
+    public set StemMaxLength(value: number) {
+        this.stemMaxLength = value;
+    }
+    public get BeamSlopeMaxAngle(): number {
+        return this.beamSlopeMaxAngle;
+    }
+    public set BeamSlopeMaxAngle(value: number) {
+        this.beamSlopeMaxAngle = value;
+    }
+    public get StemMinAllowedDistanceBetweenNoteHeadAndBeamLine(): number {
+        return this.stemMinAllowedDistanceBetweenNoteHeadAndBeamLine;
+    }
+    public set StemMinAllowedDistanceBetweenNoteHeadAndBeamLine(value: number) {
+        this.stemMinAllowedDistanceBetweenNoteHeadAndBeamLine = value;
+    }
+    public get GraceNoteScalingFactor(): number {
+        return this.graceNoteScalingFactor;
+    }
+    public set GraceNoteScalingFactor(value: number) {
+        this.graceNoteScalingFactor = value;
+    }
+    public get GraceNoteXOffset(): number {
+        return this.graceNoteXOffset;
+    }
+    public set GraceNoteXOffset(value: number) {
+        this.graceNoteXOffset = value;
+    }
+    public get WedgeOpeningLength(): number {
+        return this.wedgeOpeningLength;
+    }
+    public set WedgeOpeningLength(value: number) {
+        this.wedgeOpeningLength = value;
+    }
+    public get WedgeMeasureEndOpeningLength(): number {
+        return this.wedgeMeasureEndOpeningLength;
+    }
+    public set WedgeMeasureEndOpeningLength(value: number) {
+        this.wedgeMeasureEndOpeningLength = value;
+    }
+    public get WedgeMeasureBeginOpeningLength(): number {
+        return this.wedgeMeasureBeginOpeningLength;
+    }
+    public set WedgeMeasureBeginOpeningLength(value: number) {
+        this.wedgeMeasureBeginOpeningLength = value;
+    }
+    public get WedgePlacementAboveY(): number {
+        return this.wedgePlacementAboveY;
+    }
+    public set WedgePlacementAboveY(value: number) {
+        this.wedgePlacementAboveY = value;
+    }
+    public get WedgePlacementBelowY(): number {
+        return this.wedgePlacementBelowY;
+    }
+    public set WedgePlacementBelowY(value: number) {
+        this.wedgePlacementBelowY = value;
+    }
+    public get WedgeHorizontalMargin(): number {
+        return this.wedgeHorizontalMargin;
+    }
+    public set WedgeHorizontalMargin(value: number) {
+        this.wedgeHorizontalMargin = value;
+    }
+    public get WedgeVerticalMargin(): number {
+        return this.wedgeVerticalMargin;
+    }
+    public set WedgeVerticalMargin(value: number) {
+        this.wedgeVerticalMargin = value;
+    }
+    public get DistanceOffsetBetweenTwoHorizontallyCrossedWedges(): number {
+        return this.distanceOffsetBetweenTwoHorizontallyCrossedWedges;
+    }
+    public set DistanceOffsetBetweenTwoHorizontallyCrossedWedges(value: number) {
+        this.distanceOffsetBetweenTwoHorizontallyCrossedWedges = value;
+    }
+    public get WedgeMinLength(): number {
+        return this.wedgeMinLength;
+    }
+    public set WedgeMinLength(value: number) {
+        this.wedgeMinLength = value;
+    }
+    public get DistanceBetweenAdjacentDynamics(): number {
+        return this.distanceBetweenAdjacentDynamics;
+    }
+    public set DistanceBetweenAdjacentDynamics(value: number) {
+        this.distanceBetweenAdjacentDynamics = value;
+    }
+    public get TempoChangeMeasureValitidy(): number {
+        return this.tempoChangeMeasureValitidy;
+    }
+    public set TempoChangeMeasureValitidy(value: number) {
+        this.tempoChangeMeasureValitidy = value;
+    }
+    public get TempoContinousFactor(): number {
+        return this.tempoContinousFactor;
+    }
+    public set TempoContinousFactor(value: number) {
+        this.tempoContinousFactor = value;
+    }
+    public get StaccatoScalingFactor(): number {
+        return this.staccatoScalingFactor;
+    }
+    public set StaccatoScalingFactor(value: number) {
+        this.staccatoScalingFactor = value;
+    }
+    public get BetweenDotsDistance(): number {
+        return this.betweenDotsDistance;
+    }
+    public set BetweenDotsDistance(value: number) {
+        this.betweenDotsDistance = value;
+    }
+    public get OrnamentAccidentalScalingFactor(): number {
+        return this.ornamentAccidentalScalingFactor;
+    }
+    public set OrnamentAccidentalScalingFactor(value: number) {
+        this.ornamentAccidentalScalingFactor = value;
+    }
+    public get ChordSymbolTextHeight(): number {
+        return this.chordSymbolTextHeight;
+    }
+    public set ChordSymbolTextHeight(value: number) {
+        this.chordSymbolTextHeight = value;
+    }
+    public get FingeringLabelFontHeight(): number {
+        return this.fingeringLabelFontHeight;
+    }
+    public set FingeringLabelFontHeight(value: number) {
+        this.fingeringLabelFontHeight = value;
+    }
+    public get MeasureNumberLabelHeight(): number {
+        return this.measureNumberLabelHeight;
+    }
+    public set MeasureNumberLabelHeight(value: number) {
+        this.measureNumberLabelHeight = value;
+    }
+    public get MeasureNumberLabelOffset(): number {
+        return this.measureNumberLabelOffset;
+    }
+    public set MeasureNumberLabelOffset(value: number) {
+        this.measureNumberLabelOffset = value;
+    }
+    public get TupletNumberLabelHeight(): number {
+        return this.tupletNumberLabelHeight;
+    }
+    public set TupletNumberLabelHeight(value: number) {
+        this.tupletNumberLabelHeight = value;
+    }
+    public get TupletNumberYOffset(): number {
+        return this.tupletNumberYOffset;
+    }
+    public set TupletNumberYOffset(value: number) {
+        this.tupletNumberYOffset = value;
+    }
+    public get LabelMarginBorderFactor(): number {
+        return this.labelMarginBorderFactor;
+    }
+    public set LabelMarginBorderFactor(value: number) {
+        this.labelMarginBorderFactor = value;
+    }
+    public get TupletVerticalLineLength(): number {
+        return this.tupletVerticalLineLength;
+    }
+    public set TupletVerticalLineLength(value: number) {
+        this.tupletVerticalLineLength = value;
+    }
+    public get RepetitionEndingLabelHeight(): number {
+        return this.repetitionEndingLabelHeight;
+    }
+    public set RepetitionEndingLabelHeight(value: number) {
+        this.repetitionEndingLabelHeight = value;
+    }
+    public get RepetitionEndingLabelXOffset(): number {
+        return this.repetitionEndingLabelXOffset;
+    }
+    public set RepetitionEndingLabelXOffset(value: number) {
+        this.repetitionEndingLabelXOffset = value;
+    }
+    public get RepetitionEndingLabelYOffset(): number {
+        return this.repetitionEndingLabelYOffset;
+    }
+    public set RepetitionEndingLabelYOffset(value: number) {
+        this.repetitionEndingLabelYOffset = value;
+    }
+    public get RepetitionEndingLineYLowerOffset(): number {
+        return this.repetitionEndingLineYLowerOffset;
+    }
+    public set RepetitionEndingLineYLowerOffset(value: number) {
+        this.repetitionEndingLineYLowerOffset = value;
+    }
+    public get RepetitionEndingLineYUpperOffset(): number {
+        return this.repetitionEndingLineYUpperOffset;
+    }
+    public set RepetitionEndingLineYUpperOffset(value: number) {
+        this.repetitionEndingLineYUpperOffset = value;
+    }
+    public get LyricsHeight(): number {
+        return this.lyricsHeight;
+    }
+    public set LyricsHeight(value: number) {
+        this.lyricsHeight = value;
+    }
+    public get VerticalBetweenLyricsDistance(): number {
+        return this.verticalBetweenLyricsDistance;
+    }
+    public set VerticalBetweenLyricsDistance(value: number) {
+        this.verticalBetweenLyricsDistance = value;
+    }
+    public get BetweenSyllabelMaximumDistance(): number {
+        return this.betweenSyllabelMaximumDistance;
+    }
+    public set BetweenSyllabelMaximumDistance(value: number) {
+        this.betweenSyllabelMaximumDistance = value;
+    }
+    public get MinimumDistanceBetweenDashes(): number {
+        return this.minimumDistanceBetweenDashes;
+    }
+    public set MinimumDistanceBetweenDashes(value: number) {
+        this.minimumDistanceBetweenDashes = value;
+    }
+    public get BezierCurveStepSize(): number {
+        return this.bezierCurveStepSize;
+    }
+    public set BezierCurveStepSize(value: number) {
+        this.bezierCurveStepSize = value;
+    }
+    public get TPow3(): number[] {
+        return this.tPower3;
+    }
+    public set TPow3(value: number[]) {
+        this.tPower3 = value;
+    }
+    public get OneMinusTPow3(): number[] {
+        return this.oneMinusTPower3;
+    }
+    public set OneMinusTPow3(value: number[]) {
+        this.oneMinusTPower3 = value;
+    }
+    public get BezierFactorOne(): number[] {
+        return this.factorOne;
+    }
+    public set BezierFactorOne(value: number[]) {
+        this.factorOne = value;
+    }
+    public get BezierFactorTwo(): number[] {
+        return this.factorTwo;
+    }
+    public set BezierFactorTwo(value: number[]) {
+        this.factorTwo = value;
+    }
+    public get TieGhostObjectWidth(): number {
+        return this.tieGhostObjectWidth;
+    }
+    public set TieGhostObjectWidth(value: number) {
+        this.tieGhostObjectWidth = value;
+    }
+    public get TieYPositionOffsetFactor(): number {
+        return this.tieYPositionOffsetFactor;
+    }
+    public set TieYPositionOffsetFactor(value: number) {
+        this.tieYPositionOffsetFactor = value;
+    }
+    public get MinimumNeededXspaceForTieGhostObject(): number {
+        return this.minimumNeededXspaceForTieGhostObject;
+    }
+    public set MinimumNeededXspaceForTieGhostObject(value: number) {
+        this.minimumNeededXspaceForTieGhostObject = value;
+    }
+    public get TieHeightMinimum(): number {
+        return this.tieHeightMinimum;
+    }
+    public set TieHeightMinimum(value: number) {
+        this.tieHeightMinimum = value;
+    }
+    public get TieHeightMaximum(): number {
+        return this.tieHeightMaximum;
+    }
+    public set TieHeightMaximum(value: number) {
+        this.tieHeightMaximum = value;
+    }
+    public get TieHeightInterpolationK(): number {
+        return this.tieHeightInterpolationK;
+    }
+    public set TieHeightInterpolationK(value: number) {
+        this.tieHeightInterpolationK = value;
+    }
+    public get TieHeightInterpolationD(): number {
+        return this.tieHeightInterpolationD;
+    }
+    public set TieHeightInterpolationD(value: number) {
+        this.tieHeightInterpolationD = value;
+    }
+    public get SlurNoteHeadYOffset(): number {
+        return this.slurNoteHeadYOffset;
+    }
+    public set SlurNoteHeadYOffset(value: number) {
+        this.slurNoteHeadYOffset = value;
+    }
+    public get SlurStemXOffset(): number {
+        return this.slurStemXOffset;
+    }
+    public set SlurStemXOffset(value: number) {
+        this.slurStemXOffset = value;
+    }
+    public get SlurSlopeMaxAngle(): number {
+        return this.slurSlopeMaxAngle;
+    }
+    public set SlurSlopeMaxAngle(value: number) {
+        this.slurSlopeMaxAngle = value;
+    }
+    public get SlurTangentMinAngle(): number {
+        return this.slurTangentMinAngle;
+    }
+    public set SlurTangentMinAngle(value: number) {
+        this.slurTangentMinAngle = value;
+    }
+    public get SlurTangentMaxAngle(): number {
+        return this.slurTangentMaxAngle;
+    }
+    public set SlurTangentMaxAngle(value: number) {
+        this.slurTangentMaxAngle = value;
+    }
+    public get SlursStartingAtSameStaffEntryYOffset(): number {
+        return this.slursStartingAtSameStaffEntryYOffset;
+    }
+    public set SlursStartingAtSameStaffEntryYOffset(value: number) {
+        this.slursStartingAtSameStaffEntryYOffset = value;
+    }
+    public get InstantaniousTempoTextHeight(): number {
+        return this.instantaniousTempoTextHeight;
+    }
+    public set InstantaniousTempoTextHeight(value: number) {
+        this.instantaniousTempoTextHeight = value;
+    }
+    public get ContinuousDynamicTextHeight(): number {
+        return this.continuousDynamicTextHeight;
+    }
+    public set ContinuousDynamicTextHeight(value: number) {
+        this.continuousDynamicTextHeight = value;
+    }
+    public get MoodTextHeight(): number {
+        return this.moodTextHeight;
+    }
+    public set MoodTextHeight(value: number) {
+        this.moodTextHeight = value;
+    }
+    public get ContinuousTempoTextHeight(): number {
+        return this.continuousTempoTextHeight;
+    }
+    public set ContinuousTempoTextHeight(value: number) {
+        this.continuousTempoTextHeight = value;
+    }
+    public get UnknownTextHeight(): number {
+        return this.unknownTextHeight;
+    }
+    public set UnknownTextHeight(value: number) {
+        this.unknownTextHeight = value;
+    }
+    public get StaffLineWidth(): number {
+        return this.staffLineWidth;
+    }
+    public set StaffLineWidth(value: number) {
+        this.staffLineWidth = value;
+    }
+    public get LedgerLineWidth(): number {
+        return this.ledgerLineWidth;
+    }
+    public set LedgerLineWidth(value: number) {
+        this.ledgerLineWidth = value;
+    }
+    public get WedgeLineWidth(): number {
+        return this.wedgeLineWidth;
+    }
+    public set WedgeLineWidth(value: number) {
+        this.wedgeLineWidth = value;
+    }
+    public get TupletLineWidth(): number {
+        return this.tupletLineWidth;
+    }
+    public set TupletLineWidth(value: number) {
+        this.tupletLineWidth = value;
+    }
+    public get LyricUnderscoreLineWidth(): number {
+        return this.lyricUnderscoreLineWidth;
+    }
+    public set LyricUnderscoreLineWidth(value: number) {
+        this.lyricUnderscoreLineWidth = value;
+    }
+    public get SystemThinLineWidth(): number {
+        return this.systemThinLineWidth;
+    }
+    public set SystemThinLineWidth(value: number) {
+        this.systemThinLineWidth = value;
+    }
+    public get SystemBoldLineWidth(): number {
+        return this.systemBoldLineWidth;
+    }
+    public set SystemBoldLineWidth(value: number) {
+        this.systemBoldLineWidth = value;
+    }
+    public get SystemRepetitionEndingLineWidth(): number {
+        return this.systemRepetitionEndingLineWidth;
+    }
+    public set SystemRepetitionEndingLineWidth(value: number) {
+        this.systemRepetitionEndingLineWidth = value;
+    }
+    public get SystemDotWidth(): number {
+        return this.systemDotWidth;
+    }
+    public set SystemDotWidth(value: number) {
+        this.systemDotWidth = value;
+    }
+    public get DistanceBetweenVerticalSystemLines(): number {
+        return this.distanceBetweenVerticalSystemLines;
+    }
+    public set DistanceBetweenVerticalSystemLines(value: number) {
+        this.distanceBetweenVerticalSystemLines = value;
+    }
+    public get DistanceBetweenDotAndLine(): number {
+        return this.distanceBetweenDotAndLine;
+    }
+    public set DistanceBetweenDotAndLine(value: number) {
+        this.distanceBetweenDotAndLine = value;
+    }
+    public get OctaveShiftLineWidth(): number {
+        return this.octaveShiftLineWidth;
+    }
+    public set OctaveShiftLineWidth(value: number) {
+        this.octaveShiftLineWidth = value;
+    }
+    public get OctaveShiftVerticalLineLength(): number {
+        return this.octaveShiftVerticalLineLength;
+    }
+    public set OctaveShiftVerticalLineLength(value: number) {
+        this.octaveShiftVerticalLineLength = value;
+    }
+    public get GraceLineWidth(): number {
+        return this.graceLineWidth;
+    }
+    public set GraceLineWidth(value: number) {
+        this.graceLineWidth = value;
+    }
+    public get MinimumStaffLineDistance(): number {
+        return this.minimumStaffLineDistance;
+    }
+    public set MinimumStaffLineDistance(value: number) {
+        this.minimumStaffLineDistance = value;
+    }
+    public get MinimumCrossedBeamDifferenceMargin(): number {
+        return this.minimumCrossedBeamDifferenceMargin;
+    }
+    public set MinimumCrossedBeamDifferenceMargin(value: number) {
+        this.minimumCrossedBeamDifferenceMargin = value;
+    }
+    public get DisplacedNoteMargin(): number {
+        return this.displacedNoteMargin;
+    }
+    public set DisplacedNoteMargin(value: number) {
+        this.displacedNoteMargin = value;
+    }
+    public get MinNoteDistance(): number {
+        return this.minNoteDistance;
+    }
+    public set MinNoteDistance(value: number) {
+        this.minNoteDistance = value;
+    }
+    public get SubMeasureXSpacingThreshold(): number {
+        return this.subMeasureXSpacingThreshold;
+    }
+    public set SubMeasureXSpacingThreshold(value: number) {
+        this.subMeasureXSpacingThreshold = value;
+    }
+    public get MeasureDynamicsMaxScalingFactor(): number {
+        return this.measureDynamicsMaxScalingFactor;
+    }
+    public set MeasureDynamicsMaxScalingFactor(value: number) {
+        this.measureDynamicsMaxScalingFactor = value;
+    }
+    public get MaxInstructionsConstValue(): number {
+        return this.maxInstructionsConstValue;
+    }
+    public set MaxInstructionsConstValue(value: number) {
+        this.maxInstructionsConstValue = value;
+    }
+    public get NoteDistances(): number[] {
+        return this.noteDistances;
+    }
+    public set NoteDistances(value: number[]) {
+        this.noteDistances = value;
+    }
+    public get NoteDistancesScalingFactors(): number[] {
+        return this.noteDistancesScalingFactors;
+    }
+    public set NoteDistancesScalingFactors(value: number[]) {
+        this.noteDistancesScalingFactors = value;
+    }
+    public get DurationDistanceDict(): {[_: number]: number; } {
+        return this.durationDistanceDict;
+    }
+    public get DurationScalingDistanceDict(): {[_: number]: number; } {
+        return this.durationScalingDistanceDict;
+    }
+    private populateDictionaries(): void {
+        for (let i: number = 0; i < this.noteDistances.length; i++) {
+            switch (i) {
+                case 0:
+                    this.durationDistanceDict[0.015625] = this.noteDistances[i];
+                    this.durationScalingDistanceDict[0.015625] = this.noteDistancesScalingFactors[i];
+                    break;
+                case 1:
+                    this.durationDistanceDict[0.03125] = this.noteDistances[i];
+                    this.durationScalingDistanceDict[0.03125] = this.noteDistancesScalingFactors[i];
+                    break;
+                case 2:
+                    this.durationDistanceDict[0.0625] = this.noteDistances[i];
+                    this.durationScalingDistanceDict[0.0625] = this.noteDistancesScalingFactors[i];
+                    break;
+                case 3:
+                    this.durationDistanceDict[0.125] = this.noteDistances[i];
+                    this.durationScalingDistanceDict[0.125] = this.noteDistancesScalingFactors[i];
+                    break;
+                case 4:
+                    this.durationDistanceDict[0.25] = this.noteDistances[i];
+                    this.durationScalingDistanceDict[0.25] = this.noteDistancesScalingFactors[i];
+                    break;
+                case 5:
+                    this.durationDistanceDict[0.5] = this.noteDistances[i];
+                    this.durationScalingDistanceDict[0.5] = this.noteDistancesScalingFactors[i];
+                    break;
+                case 6:
+                    this.durationDistanceDict[1.0] = this.noteDistances[i];
+                    this.durationScalingDistanceDict[1.0] = this.noteDistancesScalingFactors[i];
+                    break;
+                case 7:
+                    this.durationDistanceDict[2.0] = this.noteDistances[i];
+                    this.durationScalingDistanceDict[2.0] = this.noteDistancesScalingFactors[i];
+                    break;
+                default:
+                    // FIXME
+            }
+        }
+    }
+    private calculateCurveParametersArrays(): void {
+        this.tPower3 = new Array(this.bezierCurveStepSize);
+        this.oneMinusTPower3 = new Array(this.bezierCurveStepSize);
+        this.factorOne = new Array(this.bezierCurveStepSize);
+        this.factorTwo = new Array(this.bezierCurveStepSize);
+        for (let i: number = 0; i < this.bezierCurveStepSize; i++) {
+            let t: number = i / this.bezierCurveStepSize;
+            this.tPower3[i] = Math.pow(t, 3);
+            this.oneMinusTPower3[i] = Math.pow((1 - t), 3);
+            this.factorOne[i] = 3 * Math.pow((1 - t), 2) * t;
+            this.factorTwo[i] = 3 * (1 - t) * Math.pow(t, 2);
+        }
+    }
+}

+ 30 - 0
src/MusicalScore/Graphical/GraphicalChordSymbolContainer.ts

@@ -0,0 +1,30 @@
+import {TextAlignment} from "../../Common/Enums/TextAlignment";
+import {Label} from "../Label";
+import {GraphicalLabel} from "./GraphicalLabel";
+import {ChordSymbolContainer} from "../VoiceData/ChordSymbolContainer";
+import {BoundingBox} from "./BoundingBox";
+import {GraphicalObject} from "./GraphicalObject";
+import {PointF2D} from "../../Common/DataObjects/PointF2D";
+
+export class GraphicalChordSymbolContainer extends GraphicalObject {
+    private chordSymbolContainer: ChordSymbolContainer;
+    private graphicalLabel: GraphicalLabel;
+    constructor(chordSymbolContainer: ChordSymbolContainer, parent: BoundingBox, textHeight: number, transposeHalftones: number) {
+        super();
+        this.chordSymbolContainer = chordSymbolContainer;
+        this.boundingBox = new BoundingBox(this, parent);
+        this.calculateLabel(textHeight, transposeHalftones);
+    }
+    public get GetChordSymbolContainer(): ChordSymbolContainer {
+        return this.chordSymbolContainer;
+    }
+    public get GetGraphicalLabel(): GraphicalLabel {
+        return this.graphicalLabel;
+    }
+    private calculateLabel(textHeight: number, transposeHalftones: number): void {
+        let text: string = ChordSymbolContainer.calculateChordText(this.chordSymbolContainer, transposeHalftones);
+        this.graphicalLabel = new GraphicalLabel(new Label(text), textHeight, TextAlignment.CenterBottom, this.boundingBox);
+        this.graphicalLabel.PositionAndShape.RelativePosition = new PointF2D(0.0, 0.0);
+        this.boundingBox.ChildElements.push(this.graphicalLabel.PositionAndShape);
+    }
+}

+ 9 - 0
src/MusicalScore/Graphical/GraphicalComment.ts

@@ -0,0 +1,9 @@
+import {GraphicalLabel} from "./GraphicalLabel";
+export class GraphicalComment {
+    constructor(label: GraphicalLabel, settingsLabel: GraphicalLabel) {
+        this.label = label;
+        this.settings = settingsLabel;
+    }
+    public label: GraphicalLabel;
+    public settings: GraphicalLabel;
+}

+ 96 - 0
src/MusicalScore/Graphical/GraphicalLabel.ts

@@ -0,0 +1,96 @@
+import {Label} from "../Label";
+import {TextAlignment} from "../../Common/Enums/TextAlignment";
+import {Clickable} from "./Clickable";
+import {BoundingBox} from "./BoundingBox";
+import {EngravingRules} from "./EngravingRules";
+import {MusicSheetCalculator} from "./MusicSheetCalculator";
+
+export class GraphicalLabel extends Clickable {
+    private label: Label;
+
+    constructor(label: Label, textHeight: number, alignment: TextAlignment, parent: BoundingBox = undefined) {
+        super();
+        this.label = label;
+        this.boundingBox = new BoundingBox(this, parent);
+        this.label.fontHeight = textHeight;
+        this.label.textAlignment = alignment;
+    }
+
+    public get Label(): Label {
+        return this.label;
+    }
+
+    public setLabelPositionAndShapeBorders(): void {
+        if (this.Label.text.trim() === "") {
+            return;
+        }
+        let labelMarginBorderFactor: number = EngravingRules.Rules.LabelMarginBorderFactor;
+
+        let widthToHeightRatio: number =
+            MusicSheetCalculator.TextMeasurer.computeTextWidthToHeightRatio(this.Label.text, this.Label.font, this.Label.fontStyle);
+        let height: number = this.Label.fontHeight;
+        let width: number = height * widthToHeightRatio;
+        let psi: BoundingBox = this.PositionAndShape;
+        switch (this.Label.textAlignment) {
+            case TextAlignment.CenterBottom:
+                psi.BorderTop = -height;
+                psi.BorderLeft = -width / 2;
+                psi.BorderBottom = 0;
+                psi.BorderRight = width / 2;
+                break;
+            case TextAlignment.CenterCenter:
+                psi.BorderTop = -height / 2;
+                psi.BorderLeft = -width / 2;
+                psi.BorderBottom = height / 2;
+                psi.BorderRight = width / 2;
+                break;
+            case TextAlignment.CenterTop:
+                psi.BorderTop = 0;
+                psi.BorderLeft = -width / 2;
+                psi.BorderBottom = height;
+                psi.BorderRight = width / 2;
+                break;
+            case TextAlignment.LeftBottom:
+                psi.BorderTop = -height;
+                psi.BorderLeft = 0;
+                psi.BorderBottom = 0;
+                psi.BorderRight = width;
+                break;
+            case TextAlignment.LeftCenter:
+                psi.BorderTop = -height / 2;
+                psi.BorderLeft = 0;
+                psi.BorderBottom = height / 2;
+                psi.BorderRight = width;
+                break;
+            case TextAlignment.LeftTop:
+                psi.BorderTop = 0;
+                psi.BorderLeft = 0;
+                psi.BorderBottom = height;
+                psi.BorderRight = width;
+                break;
+            case TextAlignment.RightBottom:
+                psi.BorderTop = -height;
+                psi.BorderLeft = -width;
+                psi.BorderBottom = 0;
+                psi.BorderRight = 0;
+                break;
+            case TextAlignment.RightCenter:
+                psi.BorderTop = -height / 2;
+                psi.BorderLeft = -width;
+                psi.BorderBottom = height / 2;
+                psi.BorderRight = 0;
+                break;
+            case TextAlignment.RightTop:
+                psi.BorderTop = 0;
+                psi.BorderLeft = -width;
+                psi.BorderBottom = height;
+                psi.BorderRight = 0;
+                break;
+            default:
+        }
+        psi.BorderMarginTop = psi.BorderTop - height * labelMarginBorderFactor;
+        psi.BorderMarginLeft = psi.BorderLeft - height * labelMarginBorderFactor;
+        psi.BorderMarginBottom = psi.BorderBottom + height * labelMarginBorderFactor;
+        psi.BorderMarginRight = psi.BorderRight + height * labelMarginBorderFactor;
+    }
+}

+ 35 - 0
src/MusicalScore/Graphical/GraphicalLine.ts

@@ -0,0 +1,35 @@
+import {OutlineAndFillStyleEnum} from "./DrawingEnums";
+import {PointF2D} from "../../Common/DataObjects/PointF2D";
+
+export class GraphicalLine {
+    constructor(start: PointF2D, end: PointF2D, width: number = 0, styleEnum: OutlineAndFillStyleEnum = OutlineAndFillStyleEnum.BaseWritingColor) {
+        this.start = start;
+        this.end = end;
+        this.width = width;
+        this.styleId = <number>styleEnum;
+    }
+    public styleId: number;
+
+    private start: PointF2D;
+    private end: PointF2D;
+    private width: number;
+
+    public get Start(): PointF2D {
+        return this.start;
+    }
+    public set Start(value: PointF2D) {
+        this.start = value;
+    }
+    public get End(): PointF2D {
+        return this.end;
+    }
+    public set End(value: PointF2D) {
+        this.end = value;
+    }
+    public get Width(): number {
+        return this.width;
+    }
+    public set Width(value: number) {
+        this.width = value;
+    }
+}

+ 48 - 0
src/MusicalScore/Graphical/GraphicalLyricEntry.ts

@@ -0,0 +1,48 @@
+import {LyricsEntry} from "../VoiceData/Lyrics/LyricsEntry";
+import {GraphicalLyricWord} from "./GraphicalLyricWord";
+import {GraphicalLabel} from "./GraphicalLabel";
+import {GraphicalStaffEntry} from "./GraphicalStaffEntry";
+import {Label} from "../Label";
+import {TextAlignment} from "../../Common/Enums/TextAlignment";
+import {PointF2D} from "../../Common/DataObjects/PointF2D";
+
+export class GraphicalLyricEntry {
+    private lyricsEntry: LyricsEntry;
+    private graphicalLyricWord: GraphicalLyricWord;
+    private graphicalLabel: GraphicalLabel;
+    private graphicalStaffEntry: GraphicalStaffEntry;
+
+    constructor(lyricsEntry: LyricsEntry, graphicalStaffEntry: GraphicalStaffEntry, lyricsHeight: number, staffHeight: number) {
+        this.lyricsEntry = lyricsEntry;
+        this.graphicalStaffEntry = graphicalStaffEntry;
+        this.graphicalLabel = new GraphicalLabel(
+            new Label(lyricsEntry.Text),
+            lyricsHeight,
+            TextAlignment.CenterBottom,
+            graphicalStaffEntry.PositionAndShape
+        );
+        this.graphicalLabel.PositionAndShape.RelativePosition = new PointF2D(0.0, staffHeight);
+    }
+
+    public get GetLyricsEntry(): LyricsEntry {
+        return this.lyricsEntry;
+    }
+    public get ParentLyricWord(): GraphicalLyricWord {
+        return this.graphicalLyricWord;
+    }
+    public set ParentLyricWord(value: GraphicalLyricWord) {
+        this.graphicalLyricWord = value;
+    }
+    public get GraphicalLabel(): GraphicalLabel {
+        return this.graphicalLabel;
+    }
+    public set GraphicalLabel(value: GraphicalLabel) {
+        this.graphicalLabel = value;
+    }
+    public get StaffEntryParent(): GraphicalStaffEntry {
+        return this.graphicalStaffEntry;
+    }
+    public set StaffEntryParent(value: GraphicalStaffEntry) {
+        this.graphicalStaffEntry = value;
+    }
+}

+ 39 - 0
src/MusicalScore/Graphical/GraphicalLyricWord.ts

@@ -0,0 +1,39 @@
+import {LyricWord} from "../VoiceData/Lyrics/LyricsWord";
+import {GraphicalLyricEntry} from "./GraphicalLyricEntry";
+
+export class GraphicalLyricWord {
+    private lyricWord: LyricWord;
+    private graphicalLyricsEntries: GraphicalLyricEntry[] = [];
+
+    constructor(lyricWord: LyricWord) {
+        this.lyricWord = lyricWord;
+        this.initialize();
+    }
+
+    public get GetLyricWord(): LyricWord {
+        return this.lyricWord;
+    }
+
+    public get GraphicalLyricsEntries(): GraphicalLyricEntry[] {
+        return this.graphicalLyricsEntries;
+    }
+
+    public set GraphicalLyricsEntries(value: GraphicalLyricEntry[]) {
+        this.graphicalLyricsEntries = value;
+    }
+
+    public isFilled(): boolean {
+        for (let i: number = 0; i < this.graphicalLyricsEntries.length; i++) {
+            if (this.graphicalLyricsEntries[i] === undefined) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private initialize(): void {
+        for (let i: number = 0; i < this.lyricWord.Syllables.length; i++) {
+            this.graphicalLyricsEntries.push(undefined);
+        }
+    }
+}

+ 15 - 0
src/MusicalScore/Graphical/GraphicalMarkedArea.ts

@@ -0,0 +1,15 @@
+import {GraphicalLabel} from "./GraphicalLabel";
+import {GraphicalRectangle} from "./GraphicalRectangle";
+export class GraphicalMarkedArea {
+    constructor(systemRectangle: GraphicalRectangle, labelRectangle: GraphicalRectangle = undefined, label: GraphicalLabel = undefined,
+                settingsLabel: GraphicalLabel = undefined) {
+        this.systemRectangle = systemRectangle;
+        this.labelRectangle = labelRectangle;
+        this.label = label;
+        this.settings = settingsLabel;
+    }
+    public systemRectangle: GraphicalRectangle;
+    public labelRectangle: GraphicalRectangle;
+    public label: GraphicalLabel;
+    public settings: GraphicalLabel;
+}

+ 70 - 0
src/MusicalScore/Graphical/GraphicalMusicPage.ts

@@ -0,0 +1,70 @@
+import {BoundingBox} from "./BoundingBox";
+import {GraphicalObject} from "./GraphicalObject";
+import {GraphicalLabel} from "./GraphicalLabel";
+import {MusicSystem} from "./MusicSystem";
+import {EngravingRules} from "./EngravingRules";
+import {PointF2D} from "../../Common/DataObjects/PointF2D";
+import {GraphicalMusicSheet} from "./GraphicalMusicSheet";
+
+export class GraphicalMusicPage extends GraphicalObject {
+    private musicSystems: MusicSystem[] = [];
+    private labels: GraphicalLabel[] = [];
+    private parent: GraphicalMusicSheet;
+
+    constructor(parent: GraphicalMusicSheet) {
+        super();
+        this.parent = parent;
+        this.boundingBox = new BoundingBox(this, undefined);
+    }
+
+    public get MusicSystems(): MusicSystem[] {
+        return this.musicSystems;
+    }
+
+    public set MusicSystems(value: MusicSystem[]) {
+        this.musicSystems = value;
+    }
+
+    public get Labels(): GraphicalLabel[] {
+        return this.labels;
+    }
+
+    public set Labels(value: GraphicalLabel[]) {
+        this.labels = value;
+    }
+
+    public get Parent(): GraphicalMusicSheet {
+        return this.parent;
+    }
+
+    public set Parent(value: GraphicalMusicSheet) {
+        this.parent = value;
+    }
+
+    public setMusicPageAbsolutePosition(pageIndex: number, rules: EngravingRules): PointF2D {
+        if (rules.PagePlacement === PagePlacementEnum.Down) {
+            return new PointF2D(0.0, pageIndex * rules.PageHeight);
+        } else if (rules.PagePlacement === PagePlacementEnum.Right) {
+            return new PointF2D(pageIndex * this.parent.ParentMusicSheet.pageWidth, 0.0);
+        } else {
+            if (pageIndex % 2 === 0) {
+                if (pageIndex === 0) {
+                    return new PointF2D(0.0, pageIndex * rules.PageHeight);
+                } else {
+                    return new PointF2D(0.0, (pageIndex - 1) * rules.PageHeight);
+                }
+            } else {
+                if (pageIndex === 1) {
+                    return new PointF2D(this.parent.ParentMusicSheet.pageWidth, (pageIndex - 1) * rules.PageHeight);
+                } else {
+                    return new PointF2D(this.parent.ParentMusicSheet.pageWidth, (pageIndex - 2) * rules.PageHeight);
+                }
+            }
+        }
+    }
+}
+export enum PagePlacementEnum {
+    Down,
+    Right,
+    RightDown
+}

+ 835 - 0
src/MusicalScore/Graphical/GraphicalMusicSheet.ts

@@ -0,0 +1,835 @@
+import {MusicSheet} from "../MusicSheet";
+import {SourceMeasure} from "../VoiceData/SourceMeasure";
+import {StaffMeasure} from "./StaffMeasure";
+import {GraphicalMusicPage} from "./GraphicalMusicPage";
+import {VerticalGraphicalStaffEntryContainer} from "./VerticalGraphicalStaffEntryContainer";
+import {GraphicalLabel} from "./GraphicalLabel";
+import {GraphicalLine} from "./GraphicalLine";
+import {MusicSystem} from "./MusicSystem";
+import {GraphicalStaffEntry} from "./GraphicalStaffEntry";
+import {SourceStaffEntry} from "../VoiceData/SourceStaffEntry";
+import {PointF2D} from "../../Common/DataObjects/PointF2D";
+import {ClefInstruction} from "../VoiceData/Instructions/ClefInstruction";
+import {AbstractNotationInstruction} from "../VoiceData/Instructions/AbstractNotationInstruction";
+import {KeyInstruction} from "../VoiceData/Instructions/KeyInstruction";
+import {Fraction} from "../../Common/DataObjects/fraction";
+import {GraphicalNote} from "./GraphicalNote";
+import {Instrument} from "../Instrument";
+import {BoundingBox} from "./BoundingBox";
+import {Note} from "../VoiceData/Note";
+import {MusicSheetCalculator} from "./MusicSheetCalculator";
+import {Logging} from "../../Common/logging";
+import Dictionary from "typescript-collections/dist/lib/Dictionary";
+import {CollectionUtil} from "../../Util/collectionUtil";
+import {SelectionStartSymbol} from "./SelectionStartSymbol";
+import {SelectionEndSymbol} from "./SelectionEndSymbol";
+import {OutlineAndFillStyleEnum} from "./DrawingEnums";
+
+export class GraphicalMusicSheet {
+    constructor(musicSheet: MusicSheet, calculator: MusicSheetCalculator) {
+        this.musicSheet = musicSheet;
+        this.numberOfStaves = this.musicSheet.Staves.length;
+        this.calculator = calculator;
+        this.sourceToGraphicalMeasureLinks = new Dictionary<SourceMeasure, StaffMeasure[]>();
+        this.calculator.initialize(this);
+    }
+
+    public sourceToGraphicalMeasureLinks: Dictionary<SourceMeasure, StaffMeasure[]>;
+
+    private musicSheet: MusicSheet;
+    //private fontInfo: FontInfo = FontInfo.Info;
+    private calculator: MusicSheetCalculator;
+    private musicPages: GraphicalMusicPage[] = [];
+    private measureList: StaffMeasure[][] = [];
+    private verticalGraphicalStaffEntryContainers: VerticalGraphicalStaffEntryContainer[] = [];
+    private title: GraphicalLabel;
+    private subtitle: GraphicalLabel;
+    private composer: GraphicalLabel;
+    private lyricist: GraphicalLabel;
+    private cursors: GraphicalLine[] = [];
+    private selectionStartSymbol: SelectionStartSymbol;
+    private selectionEndSymbol: SelectionEndSymbol;
+    private minAllowedSystemWidth: number;
+    //private systemImages: Dictionary<MusicSystem, SystemImageProperties> = new Dictionary<MusicSystem, SystemImageProperties>();
+    private numberOfStaves: number;
+    private leadSheet: boolean = false;
+
+    public get ParentMusicSheet(): MusicSheet {
+        return this.musicSheet;
+    }
+
+    public get GetCalculator(): MusicSheetCalculator {
+        return this.calculator;
+    }
+
+    public get MusicPages(): GraphicalMusicPage[] {
+        return this.musicPages;
+    }
+
+    public set MusicPages(value: GraphicalMusicPage[]) {
+        this.musicPages = value;
+    }
+
+    //public get FontInfo(): FontInfo {
+    //    return this.fontInfo;
+    //}
+
+    public get MeasureList(): StaffMeasure[][] {
+        return this.measureList;
+    }
+
+    public set MeasureList(value: StaffMeasure[][]) {
+        this.measureList = value;
+    }
+
+    public get VerticalGraphicalStaffEntryContainers(): VerticalGraphicalStaffEntryContainer[] {
+        return this.verticalGraphicalStaffEntryContainers;
+    }
+
+    public set VerticalGraphicalStaffEntryContainers(value: VerticalGraphicalStaffEntryContainer[]) {
+        this.verticalGraphicalStaffEntryContainers = value;
+    }
+
+    public get Title(): GraphicalLabel {
+        return this.title;
+    }
+
+    public set Title(value: GraphicalLabel) {
+        this.title = value;
+    }
+
+    public get Subtitle(): GraphicalLabel {
+        return this.subtitle;
+    }
+
+    public set Subtitle(value: GraphicalLabel) {
+        this.subtitle = value;
+    }
+
+    public get Composer(): GraphicalLabel {
+        return this.composer;
+    }
+
+    public set Composer(value: GraphicalLabel) {
+        this.composer = value;
+    }
+
+    public get Lyricist(): GraphicalLabel {
+        return this.lyricist;
+    }
+
+    public set Lyricist(value: GraphicalLabel) {
+        this.lyricist = value;
+    }
+
+    public get Cursors(): GraphicalLine[] {
+        return this.cursors;
+    }
+
+    public get SelectionStartSymbol(): SelectionStartSymbol {
+        return this.selectionStartSymbol;
+    }
+
+    public get SelectionEndSymbol(): SelectionEndSymbol {
+        return this.selectionEndSymbol;
+    }
+
+    public get MinAllowedSystemWidth(): number {
+        return this.minAllowedSystemWidth;
+    }
+
+    public set MinAllowedSystemWidth(value: number) {
+        this.minAllowedSystemWidth = value;
+    }
+
+    // public get SystemImages(): Dictionary<MusicSystem, SystemImageProperties> {
+    //     return this.systemImages;
+    // }
+
+    public get NumberOfStaves(): number {
+        return this.numberOfStaves;
+    }
+
+    public get LeadSheet(): boolean {
+        return this.leadSheet;
+    }
+
+    public set LeadSheet(value: boolean) {
+        this.leadSheet = value;
+    }
+
+    public static transformRelativeToAbsolutePosition(graphicalMusicSheet: GraphicalMusicSheet): void {
+        for (let i: number = 0; i < graphicalMusicSheet.MusicPages.length; i++) {
+            let pageAbsolute: PointF2D = graphicalMusicSheet.MusicPages[i].setMusicPageAbsolutePosition(i, graphicalMusicSheet.ParentMusicSheet.rules);
+            let page: GraphicalMusicPage = graphicalMusicSheet.MusicPages[i];
+            page.PositionAndShape.calculateAbsolutePositionsRecursive(pageAbsolute.x, pageAbsolute.y);
+        }
+    }
+
+    public Initialize(): void {
+        this.verticalGraphicalStaffEntryContainers = [];
+        this.musicPages = [];
+        this.measureList = [];
+    }
+
+    public reCalculate(): void {
+        this.calculator.calculate();
+    }
+
+    public prepare(): void {
+        this.calculator.prepareGraphicalMusicSheet();
+    }
+
+    public EnforceRedrawOfMusicSystems(): void {
+        for (let idx: number = 0, len: number = this.musicPages.length; idx < len; ++idx) {
+            let graphicalMusicPage: GraphicalMusicPage = this.musicPages[idx];
+            for (let idx2: number = 0, len2: number = graphicalMusicPage.MusicSystems.length; idx2 < len2; ++idx2) {
+                let musicSystem: MusicSystem = graphicalMusicPage.MusicSystems[idx2];
+                musicSystem.needsToBeRedrawn = true;
+            }
+        }
+    }
+
+    public getClickedObject<T>(positionOnMusicSheet: PointF2D): T {
+        for (let idx: number = 0, len: number = this.MusicPages.length; idx < len; ++idx) {
+            let graphicalMusicPage: GraphicalMusicPage = this.MusicPages[idx];
+            return graphicalMusicPage.PositionAndShape.getClickedObjectOfType<T>(positionOnMusicSheet);
+        }
+        return undefined;
+    }
+
+    public findGraphicalStaffEntryFromMeasureList(staffIndex: number, measureIndex: number, sourceStaffEntry: SourceStaffEntry): GraphicalStaffEntry {
+        for (let i: number = measureIndex; i < this.measureList.length; i++) {
+            let graphicalMeasure: StaffMeasure = this.measureList[i][staffIndex];
+            for (let idx: number = 0, len: number = graphicalMeasure.staffEntries.length; idx < len; ++idx) {
+                let graphicalStaffEntry: GraphicalStaffEntry = graphicalMeasure.staffEntries[idx];
+                if (graphicalStaffEntry.sourceStaffEntry === sourceStaffEntry) {
+                    return graphicalStaffEntry;
+                }
+            }
+        }
+        return undefined;
+    }
+
+    public findNextGraphicalStaffEntry(staffIndex: number, measureIndex: number, graphicalStaffEntry: GraphicalStaffEntry): GraphicalStaffEntry {
+        let graphicalMeasure: StaffMeasure = graphicalStaffEntry.parentMeasure;
+        let graphicalStaffEntryIndex: number = graphicalMeasure.staffEntries.indexOf(graphicalStaffEntry);
+        if (graphicalStaffEntryIndex < graphicalMeasure.staffEntries.length - 1) {
+            return graphicalMeasure.staffEntries[graphicalStaffEntryIndex + 1];
+        } else if (measureIndex < this.measureList.length - 1) {
+            let nextMeasure: StaffMeasure = this.measureList[measureIndex + 1][staffIndex];
+            if (nextMeasure.staffEntries.length > 0) {
+                return nextMeasure.staffEntries[0];
+            }
+        }
+        return undefined;
+    }
+
+    public getFirstVisibleMeasuresListFromIndeces(start: number, end: number): StaffMeasure[] {
+        let graphicalMeasures: StaffMeasure[] = [];
+        let numberOfStaves: number = this.measureList[0].length;
+        for (let i: number = start; i <= end; i++) {
+            for (let j: number = 0; j < numberOfStaves; j++) {
+                if (this.measureList[i][j].isVisible()) {
+                    graphicalMeasures.push(this.measureList[i][j]);
+                    break;
+                }
+            }
+        }
+        return graphicalMeasures;
+    }
+
+    public orderMeasuresByStaffLine(measures: StaffMeasure[]): StaffMeasure[][] {
+        let orderedMeasures: StaffMeasure[][] = [];
+        let mList: StaffMeasure[] = [];
+        orderedMeasures.push(mList);
+        for (let i: number = 0; i < measures.length; i++) {
+            if (i === 0) {
+                mList.push(measures[0]);
+            } else {
+                if (measures[i].ParentStaffLine === measures[i - 1].ParentStaffLine) {
+                    mList.push(measures[i]);
+                } else {
+                    if (orderedMeasures.indexOf(mList) === -1) {
+                        orderedMeasures.push(mList);
+                    }
+                    mList = [];
+                    orderedMeasures.push(mList);
+                    mList.push(measures[i]);
+                }
+            }
+        }
+        return orderedMeasures;
+    }
+
+    public initializeActiveClefs(): ClefInstruction[] {
+        let activeClefs: ClefInstruction[] = [];
+        let firstSourceMeasure: SourceMeasure = this.musicSheet.getFirstSourceMeasure();
+        if (firstSourceMeasure !== undefined) {
+            for (let i: number = 0; i < firstSourceMeasure.CompleteNumberOfStaves; i++) {
+                let clef: ClefInstruction = new ClefInstruction();
+                if (firstSourceMeasure.FirstInstructionsStaffEntries[i] !== undefined) {
+                    for (let idx: number = 0, len: number = firstSourceMeasure.FirstInstructionsStaffEntries[i].Instructions.length; idx < len; ++idx) {
+                        let abstractNotationInstruction: AbstractNotationInstruction = firstSourceMeasure.FirstInstructionsStaffEntries[i].Instructions[idx];
+                        if (abstractNotationInstruction instanceof ClefInstruction) {
+                            clef = <ClefInstruction>abstractNotationInstruction;
+
+                        }
+                    }
+                }
+                activeClefs.push(clef);
+            }
+        }
+        return activeClefs;
+    }
+
+    public GetMainKey(): KeyInstruction {
+        let firstSourceMeasure: SourceMeasure = this.musicSheet.getFirstSourceMeasure();
+        if (firstSourceMeasure !== undefined) {
+            for (let i: number = 0; i < firstSourceMeasure.CompleteNumberOfStaves; i++) {
+                for (let idx: number = 0, len: number = firstSourceMeasure.FirstInstructionsStaffEntries[i].Instructions.length; idx < len; ++idx) {
+                    let abstractNotationInstruction: AbstractNotationInstruction = firstSourceMeasure.FirstInstructionsStaffEntries[i].Instructions[idx];
+                    if (abstractNotationInstruction instanceof KeyInstruction) {
+                        return <KeyInstruction>abstractNotationInstruction;
+                    }
+                }
+            }
+        }
+        return undefined;
+    }
+
+    public getOrCreateVerticalContainer(timestamp: Fraction): VerticalGraphicalStaffEntryContainer {
+        if (this.verticalGraphicalStaffEntryContainers.length === 0 ||
+            timestamp > CollectionUtil.getLastElement(this.verticalGraphicalStaffEntryContainers).AbsoluteTimestamp) {
+            let verticalGraphicalStaffEntryContainer: VerticalGraphicalStaffEntryContainer =
+                new VerticalGraphicalStaffEntryContainer(this.numberOfStaves, timestamp);
+            this.verticalGraphicalStaffEntryContainers.push(verticalGraphicalStaffEntryContainer);
+            return verticalGraphicalStaffEntryContainer;
+        }
+        let i: number;
+        for (; i >= 0; i--) {
+            if (this.verticalGraphicalStaffEntryContainers[i].AbsoluteTimestamp < timestamp) {
+                let verticalGraphicalStaffEntryContainer: VerticalGraphicalStaffEntryContainer =
+                    new VerticalGraphicalStaffEntryContainer(this.numberOfStaves, timestamp);
+                this.verticalGraphicalStaffEntryContainers.splice(i + 1, 0, verticalGraphicalStaffEntryContainer);
+                return verticalGraphicalStaffEntryContainer;
+            }
+            if (this.verticalGraphicalStaffEntryContainers[i].AbsoluteTimestamp === timestamp) {
+                return this.verticalGraphicalStaffEntryContainers[i];
+            }
+        }
+        return undefined;
+    }
+
+    public GetVerticalContainerFromTimestamp(timestamp: Fraction, startIndex: number = 0): VerticalGraphicalStaffEntryContainer {
+        let index: number = CollectionUtil.binarySearch(this.verticalGraphicalStaffEntryContainers,
+                                                        new VerticalGraphicalStaffEntryContainer(0, timestamp),
+                                                        VerticalGraphicalStaffEntryContainer.compareByTimestamp,
+                                                        startIndex,
+                                                        this.verticalGraphicalStaffEntryContainers.length - startIndex);
+        if (index >= 0) {
+            return this.verticalGraphicalStaffEntryContainers[index];
+        }
+        return undefined;
+    }
+
+    public GetInterpolatedIndexInVerticalContainers(musicTimestamp: Fraction): number {
+        let containers: VerticalGraphicalStaffEntryContainer[] = this.verticalGraphicalStaffEntryContainers;
+        let leftIndex: number = 0;
+        let rightIndex: number = containers.length - 1;
+        let foundIndex: number;
+        let leftTS: Fraction = undefined;
+        let rightTS: Fraction = undefined;
+        if (musicTimestamp <= containers[containers.length - 1].AbsoluteTimestamp) {
+            while (rightIndex - leftIndex > 1) {
+                let middleIndex: number = (rightIndex + leftIndex) / 2;
+                if (containers[leftIndex].AbsoluteTimestamp === musicTimestamp) {
+                    rightIndex = leftIndex;
+                    break;
+                } else if (containers[rightIndex].AbsoluteTimestamp === musicTimestamp) {
+                    leftIndex = rightIndex;
+                    break;
+                } else if (containers[middleIndex].AbsoluteTimestamp === musicTimestamp) {
+                    return this.verticalGraphicalStaffEntryContainers.indexOf(containers[middleIndex]);
+                } else if (containers[middleIndex].AbsoluteTimestamp > musicTimestamp) {
+                    rightIndex = middleIndex;
+                } else {
+                    leftIndex = middleIndex;
+                }
+            }
+            if (leftIndex === rightIndex) {
+                return this.verticalGraphicalStaffEntryContainers.indexOf(containers[leftIndex]);
+            }
+            leftTS = containers[leftIndex].AbsoluteTimestamp;
+            rightTS = containers[rightIndex].AbsoluteTimestamp;
+        } else {
+            leftTS = containers[containers.length - 1].AbsoluteTimestamp;
+            rightTS = Fraction.plus(this.getLongestStaffEntryDuration(containers.length - 1), leftTS);
+            rightIndex = containers.length;
+        }
+        let diff: number = rightTS.RealValue - leftTS.RealValue;
+        let diffTS: number = rightTS.RealValue - musicTimestamp.RealValue;
+        foundIndex = rightIndex - (diffTS / diff);
+        return Math.min(foundIndex, this.verticalGraphicalStaffEntryContainers.length);
+    }
+
+    public getVisibleStavesIndecesFromSourceMeasure(visibleMeasures: StaffMeasure[]): number[] {
+        let visibleInstruments: Instrument[] = [];
+        let visibleStavesIndeces: number[] = [];
+        for (let idx: number = 0, len: number = visibleMeasures.length; idx < len; ++idx) {
+            let graphicalMeasure: StaffMeasure = visibleMeasures[idx];
+            let instrument: Instrument = graphicalMeasure.ParentStaff.ParentInstrument;
+            if (visibleInstruments.indexOf(instrument) === -1) {
+                visibleInstruments.push(instrument);
+            }
+        }
+        for (let idx: number = 0, len: number = visibleInstruments.length; idx < len; ++idx) {
+            let instrument: Instrument = visibleInstruments[idx];
+            let index: number = this.musicSheet.getGlobalStaffIndexOfFirstStaff(instrument);
+            for (let j: number = 0; j < instrument.Staves.length; j++) {
+                visibleStavesIndeces.push(index + j);
+            }
+        }
+        return visibleStavesIndeces;
+    }
+
+    public getGraphicalMeasureFromSourceMeasureAndIndex(sourceMeasure: SourceMeasure, index: number): StaffMeasure {
+        for (let i: number = 0; i < this.measureList.length; i++) {
+            if (this.measureList[i][0].parentSourceMeasure === sourceMeasure) {
+                return this.measureList[i][index];
+            }
+        }
+        return undefined;
+    }
+
+    public getMeasureIndex(graphicalMeasure: StaffMeasure, measureIndex: number, inListIndex: number): boolean {
+        measureIndex = 0;
+        inListIndex = 0;
+        for (; measureIndex < this.measureList.length; measureIndex++) {
+            for (let idx: number = 0, len: number = this.measureList[measureIndex].length; idx < len; ++idx) {
+                let measure: StaffMeasure = this.measureList[measureIndex][idx];
+                if (measure === graphicalMeasure) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    public GetNearesNote(clickPosition: PointF2D, maxClickDist: PointF2D): GraphicalNote {
+        let initialSearchArea: number = 10;
+        let foundNotes: GraphicalNote[] = [];
+        let region: BoundingBox = new BoundingBox();
+        region.BorderLeft = clickPosition.x - initialSearchArea;
+        region.BorderTop = clickPosition.y - initialSearchArea;
+        region.BorderRight = clickPosition.x + initialSearchArea;
+        region.BorderBottom = clickPosition.y + initialSearchArea;
+        region.AbsolutePosition = new PointF2D(0, 0);
+        for (let idx: number = 0, len: number = this.MusicPages.length; idx < len; ++idx) {
+            let graphicalMusicPage: GraphicalMusicPage = this.MusicPages[idx];
+            let entries: GraphicalNote[] = graphicalMusicPage.PositionAndShape.getObjectsInRegion<GraphicalNote>(region);
+            //let entriesArr: GraphicalNote[] = __as__<GraphicalNote[]>(entries, GraphicalNote[]) ? ? entries;
+            if (entries === undefined) {
+                continue;
+            } else {
+                for (let idx2: number = 0, len2: number = entries.length; idx2 < len2; ++idx2) {
+                    let note: GraphicalNote = entries[idx2];
+                    if (Math.abs(note.PositionAndShape.AbsolutePosition.x - clickPosition.x) < maxClickDist.x
+                        && Math.abs(note.PositionAndShape.AbsolutePosition.y - clickPosition.y) < maxClickDist.y) {
+                        foundNotes.push(note);
+                    }
+                }
+            }
+        }
+        let closest: GraphicalNote = undefined;
+        for (let idx: number = 0, len: number = foundNotes.length; idx < len; ++idx) {
+            let note: GraphicalNote = foundNotes[idx];
+            if (closest === undefined) {
+                closest = note;
+            } else {
+                if (note.parentStaffEntry.relInMeasureTimestamp === undefined) {
+                    continue;
+                }
+                let deltaNew: number = this.CalculateDistance(note.PositionAndShape.AbsolutePosition, clickPosition);
+                let deltaOld: number = this.CalculateDistance(closest.PositionAndShape.AbsolutePosition, clickPosition);
+                if (deltaNew < deltaOld) {
+                    closest = note;
+                }
+            }
+        }
+        if (closest !== undefined) {
+            return closest;
+        }
+        return undefined;
+    }
+
+    public GetClickableLabel(clickPosition: PointF2D): GraphicalLabel {
+        let initialSearchAreaX: number = 4;
+        let initialSearchAreaY: number = 4;
+        let region: BoundingBox = new BoundingBox();
+        region.BorderLeft = clickPosition.x - initialSearchAreaX;
+        region.BorderTop = clickPosition.y - initialSearchAreaY;
+        region.BorderRight = clickPosition.x + initialSearchAreaX;
+        region.BorderBottom = clickPosition.y + initialSearchAreaY;
+        region.AbsolutePosition = new PointF2D(0, 0);
+        for (let idx: number = 0, len: number = this.MusicPages.length; idx < len; ++idx) {
+            let graphicalMusicPage: GraphicalMusicPage = this.MusicPages[idx];
+            let entries: GraphicalLabel[] = graphicalMusicPage.PositionAndShape.getObjectsInRegion<GraphicalLabel>(region);
+            if (entries.length !== 1) {
+                continue;
+            } else {
+                for (let idx2: number = 0, len2: number = entries.length; idx2 < len2; ++idx2) {
+                    let clickedLabel: GraphicalLabel = entries[idx2];
+                    return clickedLabel;
+                }
+            }
+        }
+        return undefined;
+    }
+
+    public GetNearestStaffEntry(clickPosition: PointF2D): GraphicalStaffEntry {
+        let initialSearchArea: number = 10;
+        let foundEntries: GraphicalStaffEntry[] = [];
+        let region: BoundingBox = new BoundingBox(undefined);
+        region.BorderLeft = clickPosition.x - initialSearchArea;
+        region.BorderTop = clickPosition.y - initialSearchArea;
+        region.BorderRight = clickPosition.x + initialSearchArea;
+        region.BorderBottom = clickPosition.y + initialSearchArea;
+        region.AbsolutePosition = new PointF2D(0, 0);
+        for (let idx: number = 0, len: number = this.MusicPages.length; idx < len; ++idx) {
+            let graphicalMusicPage: GraphicalMusicPage = this.MusicPages[idx];
+            let entries: GraphicalStaffEntry[] = graphicalMusicPage.PositionAndShape.getObjectsInRegion<GraphicalStaffEntry>(region, false);
+            if (entries === undefined || entries.length === 0) {
+                continue;
+            } else {
+                for (let idx2: number = 0, len2: number = entries.length; idx2 < len2; ++idx2) {
+                    let gse: GraphicalStaffEntry = entries[idx2];
+                    foundEntries.push(gse);
+                }
+            }
+        }
+        let closest: GraphicalStaffEntry = undefined;
+        for (let idx: number = 0, len: number = foundEntries.length; idx < len; ++idx) {
+            let gse: GraphicalStaffEntry = foundEntries[idx];
+            if (closest === undefined) {
+                closest = gse;
+            } else {
+                if (gse.relInMeasureTimestamp === undefined) {
+                    continue;
+                }
+                let deltaNew: number = this.CalculateDistance(gse.PositionAndShape.AbsolutePosition, clickPosition);
+                let deltaOld: number = this.CalculateDistance(closest.PositionAndShape.AbsolutePosition, clickPosition);
+                if (deltaNew < deltaOld) {
+                    closest = gse;
+                }
+            }
+        }
+        if (closest !== undefined) {
+            return closest;
+        }
+        return undefined;
+    }
+
+    public GetPossibleCommentAnchor(clickPosition: PointF2D): SourceStaffEntry {
+        let entry: GraphicalStaffEntry = this.GetNearestStaffEntry(clickPosition);
+        if (entry === undefined) {
+            return undefined;
+        }
+        return entry.sourceStaffEntry;
+    }
+
+    public getClickedObjectOfType<T>(positionOnMusicSheet: PointF2D): T {
+        for (let idx: number = 0, len: number = this.musicPages.length; idx < len; ++idx) {
+            let page: GraphicalMusicPage = this.musicPages[idx];
+            let o: Object = page.PositionAndShape.getClickedObjectOfType<T>(positionOnMusicSheet);
+            if (o !== undefined) {
+                return (o as T);
+            }
+        }
+        return undefined;
+    }
+
+    public tryGetTimestampFromPosition(positionOnMusicSheet: PointF2D): Fraction {
+        let entry: GraphicalStaffEntry = this.getClickedObjectOfType<GraphicalStaffEntry>(positionOnMusicSheet);
+        if (entry === undefined) {
+            return undefined;
+        }
+        return entry.getAbsoluteTimestamp();
+    }
+
+    public tryGetClickableLabel(positionOnMusicSheet: PointF2D): GraphicalLabel {
+        try {
+            return this.GetClickableLabel(positionOnMusicSheet);
+        } catch (ex) {
+            Logging.log("GraphicalMusicSheet.tryGetClickableObject", "positionOnMusicSheet: " + positionOnMusicSheet, ex);
+        }
+
+        return undefined;
+    }
+
+    public tryGetTimeStampFromPosition(positionOnMusicSheet: PointF2D): Fraction {
+        try {
+            let entry: GraphicalStaffEntry = this.GetNearestStaffEntry(positionOnMusicSheet);
+            if (entry === undefined) {
+                return undefined;
+            }
+            return entry.getAbsoluteTimestamp();
+        } catch (ex) {
+            Logging.log(
+                "GraphicalMusicSheet.tryGetTimeStampFromPosition",
+                "positionOnMusicSheet: " + positionOnMusicSheet, ex
+            );
+        }
+
+        return undefined;
+    }
+
+    public getStaffEntry(index: number): GraphicalStaffEntry {
+        let container: VerticalGraphicalStaffEntryContainer = this.VerticalGraphicalStaffEntryContainers[index];
+        let staffEntry: GraphicalStaffEntry = undefined;
+        try {
+            for (let idx: number = 0, len: number = container.StaffEntries.length; idx < len; ++idx) {
+                let entry: GraphicalStaffEntry = container.StaffEntries[idx];
+                if (entry === undefined || !entry.sourceStaffEntry.ParentStaff.ParentInstrument.Visible) {
+                    continue;
+                }
+                if (staffEntry === undefined) {
+                    staffEntry = entry;
+                } else if (entry.PositionAndShape !== undefined && staffEntry.PositionAndShape !== undefined) {
+                    if (staffEntry.PositionAndShape.RelativePosition.x > entry.PositionAndShape.RelativePosition.x) {
+                        staffEntry = entry;
+                    }
+                }
+            }
+        } catch (ex) {
+            Logging.log("GraphicalMusicSheet.getStaffEntry", ex);
+        }
+
+        return staffEntry;
+    }
+
+    public GetPreviousVisibleContainerIndex(index: number): number {
+        for (let i: number = index - 1; i >= 0; i--) {
+            let entries: GraphicalStaffEntry[] = this.verticalGraphicalStaffEntryContainers[i].StaffEntries;
+            for (let idx: number = 0, len: number = entries.length; idx < len; ++idx) {
+                let entry: GraphicalStaffEntry = entries[idx];
+                if (entry !== undefined && entry.sourceStaffEntry.ParentStaff.ParentInstrument.Visible) {
+                    return i;
+                }
+            }
+        }
+        return -1;
+    }
+
+    public GetNextVisibleContainerIndex(index: number): number {
+        for (let i: number = index + 1; i < this.verticalGraphicalStaffEntryContainers.length; ++i) {
+            let entries: GraphicalStaffEntry[] = this.verticalGraphicalStaffEntryContainers[i].StaffEntries;
+            for (let idx: number = 0, len: number = entries.length; idx < len; ++idx) {
+                let entry: GraphicalStaffEntry = entries[idx];
+                if (entry !== undefined && entry.sourceStaffEntry.ParentStaff.ParentInstrument.Visible) {
+                    return i;
+                }
+            }
+        }
+        return -1;
+    }
+
+    public findClosestLeftStaffEntry(fractionalIndex: number, searchOnlyVisibleEntries: boolean): GraphicalStaffEntry {
+        let foundEntry: GraphicalStaffEntry = undefined;
+        let leftIndex: number = <number>Math.floor(fractionalIndex);
+        leftIndex = Math.min(this.VerticalGraphicalStaffEntryContainers.length - 1, leftIndex);
+        for (let i: number = leftIndex; i >= 0; i--) {
+            foundEntry = this.getStaffEntry(i);
+            if (foundEntry !== undefined) {
+                if (searchOnlyVisibleEntries) {
+                    if (foundEntry.sourceStaffEntry.ParentStaff.ParentInstrument.Visible) {
+                        return foundEntry;
+                    }
+                } else {
+                    return foundEntry;
+                }
+            }
+        }
+        return undefined;
+    }
+
+    public findClosestRightStaffEntry(fractionalIndex: number, returnOnlyVisibleEntries: boolean): GraphicalStaffEntry {
+        let foundEntry: GraphicalStaffEntry = undefined;
+        let rightIndex: number = <number>Math.max(0, Math.ceil(fractionalIndex));
+        for (let i: number = rightIndex; i < this.VerticalGraphicalStaffEntryContainers.length; i++) {
+            foundEntry = this.getStaffEntry(i);
+            if (foundEntry !== undefined) {
+                if (returnOnlyVisibleEntries) {
+                    if (foundEntry.sourceStaffEntry.ParentStaff.ParentInstrument.Visible) {
+                        return foundEntry;
+                    }
+                } else {
+                    return foundEntry;
+                }
+            }
+        }
+        return undefined;
+    }
+
+    public calculateCursorLineAtTimestamp(musicTimestamp: Fraction, styleEnum: OutlineAndFillStyleEnum): GraphicalLine {
+        let result: [number, MusicSystem] = this.calculateXPositionFromTimestamp(musicTimestamp);
+        let xPos: number = result[0];
+        let correspondingMusicSystem: MusicSystem = result[1];
+        if (correspondingMusicSystem === undefined || correspondingMusicSystem.StaffLines.length === 0) {
+            return undefined;
+        }
+        let yCoordinate: number = correspondingMusicSystem.PositionAndShape.AbsolutePosition.y;
+        let height: number = CollectionUtil.last(correspondingMusicSystem.StaffLines).PositionAndShape.RelativePosition.y + 4;
+        return new GraphicalLine(new PointF2D(xPos, yCoordinate), new PointF2D(xPos, yCoordinate + height), 3, styleEnum);
+    }
+
+    public calculateXPositionFromTimestamp(timeStamp: Fraction): [number, MusicSystem] {
+        let currentMusicSystem: MusicSystem = undefined;
+        let fractionalIndex: number = this.GetInterpolatedIndexInVerticalContainers(timeStamp);
+        let previousStaffEntry: GraphicalStaffEntry = this.findClosestLeftStaffEntry(fractionalIndex, true);
+        let nextStaffEntry: GraphicalStaffEntry = this.findClosestRightStaffEntry(fractionalIndex, true);
+        let currentTimeStamp: number = timeStamp.RealValue;
+        if (previousStaffEntry === undefined && nextStaffEntry === undefined) {
+            return [0, undefined];
+        }
+        let previousStaffEntryMusicSystem: MusicSystem = undefined;
+        if (previousStaffEntry !== undefined) {
+            previousStaffEntryMusicSystem = previousStaffEntry.parentMeasure.ParentStaffLine.ParentMusicSystem;
+        } else {
+            previousStaffEntryMusicSystem = nextStaffEntry.parentMeasure.ParentStaffLine.ParentMusicSystem;
+        }
+        let nextStaffEntryMusicSystem: MusicSystem = undefined;
+        if (nextStaffEntry !== undefined) {
+            nextStaffEntryMusicSystem = nextStaffEntry.parentMeasure.ParentStaffLine.ParentMusicSystem;
+        } else {
+            nextStaffEntryMusicSystem = previousStaffEntry.parentMeasure.ParentStaffLine.ParentMusicSystem;
+        }
+        if (previousStaffEntryMusicSystem === nextStaffEntryMusicSystem) {
+            currentMusicSystem = previousStaffEntryMusicSystem;
+            let fraction: number;
+            let previousStaffEntryPositionX: number;
+            let nextStaffEntryPositionX: number;
+            if (previousStaffEntry === undefined) {
+                previousStaffEntryPositionX = nextStaffEntryPositionX = nextStaffEntry.PositionAndShape.AbsolutePosition.x;
+                fraction = 0;
+            } else if (nextStaffEntry === undefined) {
+                previousStaffEntryPositionX = previousStaffEntry.PositionAndShape.AbsolutePosition.x;
+                nextStaffEntryPositionX = currentMusicSystem.GetRightBorderAbsoluteXPosition();
+                let sm: SourceMeasure = previousStaffEntry.parentMeasure.parentSourceMeasure;
+                fraction = (currentTimeStamp - previousStaffEntry.getAbsoluteTimestamp().RealValue) / (
+                    Fraction.plus(sm.AbsoluteTimestamp, sm.Duration).RealValue - previousStaffEntry.getAbsoluteTimestamp().RealValue);
+            } else {
+                previousStaffEntryPositionX = previousStaffEntry.PositionAndShape.AbsolutePosition.x;
+                nextStaffEntryPositionX = nextStaffEntry.PositionAndShape.AbsolutePosition.x;
+                if (previousStaffEntry === nextStaffEntry) {
+                    fraction = 0;
+                } else {
+                    fraction = (currentTimeStamp - previousStaffEntry.getAbsoluteTimestamp().RealValue) /
+                        (nextStaffEntry.getAbsoluteTimestamp().RealValue - previousStaffEntry.getAbsoluteTimestamp().RealValue);
+                }
+            }
+            fraction = Math.min(1, Math.max(0, fraction));
+            let interpolatedXPosition: number = previousStaffEntryPositionX + fraction * (nextStaffEntryPositionX - previousStaffEntryPositionX);
+            return [interpolatedXPosition, currentMusicSystem];
+        } else {
+            let nextSystemLeftBorderTimeStamp: number = nextStaffEntry.parentMeasure.parentSourceMeasure.AbsoluteTimestamp.RealValue;
+            let fraction: number;
+            let interpolatedXPosition: number;
+            if (currentTimeStamp < nextSystemLeftBorderTimeStamp) {
+                currentMusicSystem = previousStaffEntryMusicSystem;
+                let previousStaffEntryPositionX: number = previousStaffEntry.PositionAndShape.AbsolutePosition.x;
+                let previousSystemRightBorderX: number = currentMusicSystem.GetRightBorderAbsoluteXPosition();
+                fraction = (currentTimeStamp - previousStaffEntry.getAbsoluteTimestamp().RealValue) /
+                    (nextSystemLeftBorderTimeStamp - previousStaffEntry.getAbsoluteTimestamp().RealValue);
+                fraction = Math.min(1, Math.max(0, fraction));
+                interpolatedXPosition = previousStaffEntryPositionX + fraction * (previousSystemRightBorderX - previousStaffEntryPositionX);
+            } else {
+                currentMusicSystem = nextStaffEntryMusicSystem;
+                let nextStaffEntryPositionX: number = nextStaffEntry.PositionAndShape.AbsolutePosition.x;
+                let nextSystemLeftBorderX: number = currentMusicSystem.GetLeftBorderAbsoluteXPosition();
+                fraction = (currentTimeStamp - nextSystemLeftBorderTimeStamp) /
+                    (nextStaffEntry.getAbsoluteTimestamp().RealValue - nextSystemLeftBorderTimeStamp);
+                fraction = Math.min(1, Math.max(0, fraction));
+                interpolatedXPosition = nextSystemLeftBorderX + fraction * (nextStaffEntryPositionX - nextSystemLeftBorderX);
+            }
+            return [interpolatedXPosition, currentMusicSystem];
+        }
+    }
+
+    public GetNumberOfVisibleInstruments(): number {
+        let visibleInstrumentCount: number = 0;
+        for (let idx: number = 0, len: number = this.musicSheet.Instruments.length; idx < len; ++idx) {
+            let instrument: Instrument = this.musicSheet.Instruments[idx];
+            if (instrument.Visible === true) {
+                visibleInstrumentCount++;
+            }
+        }
+        return visibleInstrumentCount;
+    }
+
+    public GetNumberOfFollowedInstruments(): number {
+        let followedInstrumentCount: number = 0;
+        for (let idx: number = 0, len: number = this.musicSheet.Instruments.length; idx < len; ++idx) {
+            let instrument: Instrument = this.musicSheet.Instruments[idx];
+            if (instrument.Following === true) {
+                followedInstrumentCount++;
+            }
+        }
+        return followedInstrumentCount;
+    }
+
+    public GetGraphicalFromSourceMeasure(sourceMeasure: SourceMeasure): StaffMeasure[] {
+        return this.sourceToGraphicalMeasureLinks.getValue(sourceMeasure);
+    }
+
+    public GetGraphicalFromSourceStaffEntry(sourceStaffEntry: SourceStaffEntry): GraphicalStaffEntry {
+        let graphicalMeasure: StaffMeasure = this.GetGraphicalFromSourceMeasure(sourceStaffEntry.VerticalContainerParent.ParentMeasure)
+            [sourceStaffEntry.ParentStaff.idInMusicSheet];
+        return graphicalMeasure.findGraphicalStaffEntryFromTimestamp(sourceStaffEntry.Timestamp);
+    }
+
+    public GetGraphicalNoteFromSourceNote(note: Note, containingGse: GraphicalStaffEntry): GraphicalNote {
+        for (let idx: number = 0, len: number = containingGse.notes.length; idx < len; ++idx) {
+            let graphicalNotes: GraphicalNote[] = containingGse.notes[idx];
+            for (let idx2: number = 0, len2: number = graphicalNotes.length; idx2 < len2; ++idx2) {
+                let graphicalNote: GraphicalNote = graphicalNotes[idx2];
+                if (graphicalNote.sourceNote === note) {
+                    return graphicalNote;
+                }
+            }
+        }
+        return undefined;
+    }
+
+    private CalculateDistance(pt1: PointF2D, pt2: PointF2D): number {
+        let deltaX: number = pt1.x - pt2.x;
+        let deltaY: number = pt1.y - pt2.y;
+        return (deltaX * deltaX) + (deltaY * deltaY);
+    }
+
+    private getLongestStaffEntryDuration(index: number): Fraction {
+        let maxLength: Fraction = new Fraction(0, 1);
+        for (let idx: number = 0, len: number = this.verticalGraphicalStaffEntryContainers[index].StaffEntries.length; idx < len; ++idx) {
+            let graphicalStaffEntry: GraphicalStaffEntry = this.verticalGraphicalStaffEntryContainers[index].StaffEntries[idx];
+            if (graphicalStaffEntry === undefined) {
+                continue;
+            }
+            for (let idx2: number = 0, len2: number = graphicalStaffEntry.notes.length; idx2 < len2; ++idx2) {
+                let graphicalNotes: GraphicalNote[] = graphicalStaffEntry.notes[idx2];
+                for (let idx3: number = 0, len3: number = graphicalNotes.length; idx3 < len3; ++idx3) {
+                    let note: GraphicalNote = graphicalNotes[idx3];
+                    if (note.graphicalNoteLength > maxLength) {
+                        maxLength = note.graphicalNoteLength;
+                    }
+                }
+            }
+        }
+        return maxLength;
+    }
+}
+
+export class SystemImageProperties {
+    public positionInPixels: PointF2D;
+    public systemImageId: number;
+    public system: MusicSystem;
+}

+ 41 - 0
src/MusicalScore/Graphical/GraphicalNote.ts

@@ -0,0 +1,41 @@
+import {Note} from "../VoiceData/Note";
+import {Fraction} from "../../Common/DataObjects/fraction";
+import {KeyInstruction} from "../VoiceData/Instructions/KeyInstruction";
+import {ClefInstruction} from "../VoiceData/Instructions/ClefInstruction";
+import {OctaveEnum} from "../VoiceData/Expressions/ContinuousExpressions/octaveShift";
+import {Pitch} from "../../Common/DataObjects/pitch";
+import {GraphicalStaffEntry} from "./GraphicalStaffEntry";
+import {GraphicalObject} from "./GraphicalObject";
+import {MusicSheetCalculator} from "./MusicSheetCalculator";
+import {BoundingBox} from "./BoundingBox";
+
+export class GraphicalNote extends GraphicalObject {
+    constructor(note: Note, parent: GraphicalStaffEntry) {
+        super();
+        this.sourceNote = note;
+        this.parentStaffEntry = parent;
+        this.PositionAndShape = new BoundingBox(this, parent.PositionAndShape);
+    }
+
+    public sourceNote: Note;
+    public graphicalNoteLength: Fraction;
+    public parentStaffEntry: GraphicalStaffEntry;
+
+    public get ParentList(): GraphicalNote[] {
+        for (let idx: number = 0, len: number = this.parentStaffEntry.notes.length; idx < len; ++idx) {
+            let graphicalNotes: GraphicalNote[] = this.parentStaffEntry.notes[idx];
+            if (graphicalNotes.indexOf(this) !== -1) {
+                return graphicalNotes;
+            }
+        }
+        return undefined;
+    }
+
+    public Transpose(keyInstruction: KeyInstruction, activeClef: ClefInstruction, halfTones: number, octaveEnum: OctaveEnum): Pitch {
+        let transposedPitch: Pitch = this.sourceNote.Pitch;
+        if (MusicSheetCalculator.transposeCalculator !== undefined) {
+            transposedPitch = MusicSheetCalculator.transposeCalculator.transposePitch(this.sourceNote.Pitch, keyInstruction, halfTones);
+        }
+        return transposedPitch;
+    }
+}

+ 11 - 0
src/MusicalScore/Graphical/GraphicalObject.ts

@@ -0,0 +1,11 @@
+import {BoundingBox} from "./BoundingBox";
+
+export class GraphicalObject {
+    protected boundingBox: BoundingBox;
+    public get PositionAndShape(): BoundingBox {
+        return this.boundingBox;
+    }
+    public set PositionAndShape(value: BoundingBox) {
+        this.boundingBox = value;
+    }
+}

+ 43 - 0
src/MusicalScore/Graphical/GraphicalOctaveShift.ts

@@ -0,0 +1,43 @@
+import {GraphicalObject} from "./GraphicalObject";
+import {OctaveShift, OctaveEnum} from "../VoiceData/Expressions/ContinuousExpressions/octaveShift";
+import {BoundingBox} from "./BoundingBox";
+import {MusicSymbol} from "./MusicSymbol";
+import {ArgumentOutOfRangeException} from "../Exceptions";
+import {PointF2D} from "../../Common/DataObjects/PointF2D";
+export class GraphicalOctaveShift extends GraphicalObject {
+    constructor(octaveShift: OctaveShift, parent: BoundingBox) {
+        super();
+        this.getOctaveShift = octaveShift;
+        this.setSymbol();
+        // ToDo: set the size again due to the given symbol...
+        //this.PositionAndShape = new BoundingBox(parent, this.octaveSymbol, this);
+        this.PositionAndShape = new BoundingBox(this, parent);
+    }
+
+    public getOctaveShift: OctaveShift;
+    public octaveSymbol: MusicSymbol;
+    public dashesStart: PointF2D;
+    public dashesEnd: PointF2D;
+    public endsOnDifferentStaffLine: boolean;
+    public isFirstPart: boolean;
+    public isSecondPart: boolean;
+
+    private setSymbol(): void {
+        switch (this.getOctaveShift.Type) {
+            case OctaveEnum.VA8:
+                this.octaveSymbol = MusicSymbol.VA8;
+                break;
+            case OctaveEnum.VB8:
+                this.octaveSymbol = MusicSymbol.VB8;
+                break;
+            case OctaveEnum.MA15:
+                this.octaveSymbol = MusicSymbol.MA15;
+                break;
+            case OctaveEnum.MB15:
+                this.octaveSymbol = MusicSymbol.MB15;
+                break;
+            default:
+                throw new ArgumentOutOfRangeException("");
+        }
+    }
+}

+ 15 - 0
src/MusicalScore/Graphical/GraphicalRectangle.ts

@@ -0,0 +1,15 @@
+import {OutlineAndFillStyleEnum} from "./DrawingEnums";
+import {BoundingBox} from "./BoundingBox";
+import {PointF2D} from "../../Common/DataObjects/PointF2D";
+import {GraphicalObject} from "./GraphicalObject";
+export class GraphicalRectangle extends GraphicalObject {
+    constructor(upperLeftPoint: PointF2D, lowerRightPoint: PointF2D, parent: BoundingBox, style: OutlineAndFillStyleEnum) {
+        super();
+        this.boundingBox = new BoundingBox(parent);
+        this.boundingBox.RelativePosition = upperLeftPoint;
+        this.boundingBox.BorderRight = lowerRightPoint.x - upperLeftPoint.x;
+        this.boundingBox.BorderBottom = lowerRightPoint.y - upperLeftPoint.y;
+        this.style = style;
+    }
+    public style: OutlineAndFillStyleEnum;
+}

+ 294 - 0
src/MusicalScore/Graphical/GraphicalStaffEntry.ts

@@ -0,0 +1,294 @@
+import {SourceStaffEntry} from "../VoiceData/SourceStaffEntry";
+import {BoundingBox} from "./BoundingBox";
+import {Fraction} from "../../Common/DataObjects/fraction";
+import {VerticalGraphicalStaffEntryContainer} from "./VerticalGraphicalStaffEntryContainer";
+import {Note} from "../VoiceData/Note";
+import {Slur} from "../VoiceData/Expressions/ContinuousExpressions/Slur";
+import {Voice} from "../VoiceData/Voice";
+import {VoiceEntry} from "../VoiceData/VoiceEntry";
+import {LinkedVoice} from "../VoiceData/LinkedVoice";
+import {GraphicalTie} from "./GraphicalTie";
+import {GraphicalObject} from "./GraphicalObject";
+import {StaffMeasure} from "./StaffMeasure";
+import {GraphicalNote} from "./GraphicalNote";
+import {GraphicalChordSymbolContainer} from "./GraphicalChordSymbolContainer";
+import {GraphicalLyricEntry} from "./GraphicalLyricEntry";
+import {AbstractGraphicalInstruction} from "./AbstractGraphicalInstruction";
+import {GraphicalStaffEntryLink} from "./GraphicalStaffEntryLink";
+import {CollectionUtil} from "../../Util/collectionUtil";
+
+export abstract class GraphicalStaffEntry extends GraphicalObject {
+    constructor(parentMeasure: StaffMeasure, sourceStaffEntry: SourceStaffEntry = undefined, staffEntryParent: GraphicalStaffEntry = undefined) {
+        super();
+        this.parentMeasure = parentMeasure;
+        this.notes = [];
+        this.graceStaffEntriesBefore = [];
+        this.graceStaffEntriesAfter = [];
+        this.sourceStaffEntry = sourceStaffEntry;
+        if (staffEntryParent !== undefined) {
+            this.staffEntryParent = staffEntryParent;
+            this.parentVerticalContainer = staffEntryParent.parentVerticalContainer;
+            this.PositionAndShape = new BoundingBox(this, staffEntryParent.PositionAndShape);
+        } else {
+            this.PositionAndShape = new BoundingBox(this, parentMeasure.PositionAndShape);
+        }
+        if (sourceStaffEntry !== undefined) {
+            this.relInMeasureTimestamp = sourceStaffEntry.Timestamp;
+        }
+    }
+
+    public graphicalChordContainer: GraphicalChordSymbolContainer;
+    public graphicalLink: GraphicalStaffEntryLink;
+    public relInMeasureTimestamp: Fraction;
+    public sourceStaffEntry: SourceStaffEntry;
+    public parentMeasure: StaffMeasure;
+    public notes: GraphicalNote[][];
+    public graceStaffEntriesBefore: GraphicalStaffEntry[];
+    public graceStaffEntriesAfter: GraphicalStaffEntry[];
+    public staffEntryParent: GraphicalStaffEntry;
+    public parentVerticalContainer: VerticalGraphicalStaffEntryContainer;
+
+    private graphicalInstructions: AbstractGraphicalInstruction[] = [];
+    private graphicalTies: GraphicalTie[] = [];
+    private lyricsEntries: GraphicalLyricEntry[] = [];
+
+    public get GraphicalInstructions(): AbstractGraphicalInstruction[] {
+        return this.graphicalInstructions;
+    }
+
+    public get GraphicalTies(): GraphicalTie[] {
+        return this.graphicalTies;
+    }
+
+    public get LyricsEntries(): GraphicalLyricEntry[] {
+        return this.lyricsEntries;
+    }
+
+    public getAbsoluteTimestamp(): Fraction {
+        let result: Fraction = Fraction.createFromFraction(this.parentMeasure.parentSourceMeasure.AbsoluteTimestamp);
+        if (this.relInMeasureTimestamp !== undefined) {
+            result.Add(this.relInMeasureTimestamp);
+        }
+        return result;
+    }
+
+    public findEndTieGraphicalNoteFromNote(tieNote: Note): GraphicalNote {
+        for (let idx: number = 0, len: number = this.notes.length; idx < len; ++idx) {
+            let graphicalNotes: GraphicalNote[] = this.notes[idx];
+            for (let idx2: number = 0, len2: number = graphicalNotes.length; idx2 < len2; ++idx2) {
+                let graphicalNote: GraphicalNote = graphicalNotes[idx2];
+                let note: Note = graphicalNote.sourceNote;
+                if (
+                    note.Pitch !== undefined && note.Pitch.FundamentalNote === tieNote.Pitch.FundamentalNote
+                    && note.Pitch.Octave === tieNote.Pitch.Octave && note.getAbsoluteTimestamp() === tieNote.getAbsoluteTimestamp()
+                ) {
+                    return graphicalNote;
+                }
+            }
+        }
+        return undefined;
+    }
+
+    public findEndTieGraphicalNoteFromNoteWithStartingSlur(tieNote: Note, slur: Slur): GraphicalNote {
+        for (let idx: number = 0, len: number = this.notes.length; idx < len; ++idx) {
+            let graphicalNotes: GraphicalNote[] = this.notes[idx];
+            for (let idx2: number = 0, len2: number = graphicalNotes.length; idx2 < len2; ++idx2) {
+                let graphicalNote: GraphicalNote = graphicalNotes[idx2];
+                let note: Note = graphicalNote.sourceNote;
+                if (note.NoteTie !== undefined && note.NoteSlurs.indexOf(slur) !== -1) {
+                    return graphicalNote;
+                }
+            }
+        }
+        return undefined;
+    }
+
+    public findEndTieGraphicalNoteFromNoteWithEndingSlur(tieNote: Note): GraphicalNote {
+        for (let idx: number = 0, len: number = this.notes.length; idx < len; ++idx) {
+            let graphicalNotes: GraphicalNote[] = this.notes[idx];
+            for (let idx2: number = 0, len2: number = graphicalNotes.length; idx2 < len2; ++idx2) {
+                let graphicalNote: GraphicalNote = graphicalNotes[idx2];
+                let note: Note = graphicalNote.sourceNote;
+                if (
+                    note.Pitch !== undefined && note.Pitch.FundamentalNote === tieNote.Pitch.FundamentalNote
+                    && note.Pitch.Octave === tieNote.Pitch.Octave && this.getAbsoluteTimestamp() === tieNote.getAbsoluteTimestamp()
+                ) {
+                    return graphicalNote;
+                }
+            }
+        }
+        return undefined;
+    }
+
+    public findGraphicalNoteFromGraceNote(graceNote: Note): GraphicalNote {
+        for (let idx: number = 0, len: number = this.notes.length; idx < len; ++idx) {
+            let graphicalNotes: GraphicalNote[] = this.notes[idx];
+            for (let idx2: number = 0, len2: number = graphicalNotes.length; idx2 < len2; ++idx2) {
+                let graphicalNote: GraphicalNote = graphicalNotes[idx2];
+                if (graphicalNote.sourceNote === graceNote) {
+                    return graphicalNote;
+                }
+            }
+        }
+        return undefined;
+    }
+
+    public findGraphicalNoteFromNote(baseNote: Note): GraphicalNote {
+        for (let idx: number = 0, len: number = this.notes.length; idx < len; ++idx) {
+            let graphicalNotes: GraphicalNote[] = this.notes[idx];
+            for (let idx2: number = 0, len2: number = graphicalNotes.length; idx2 < len2; ++idx2) {
+                let graphicalNote: GraphicalNote = graphicalNotes[idx2];
+                if (graphicalNote.sourceNote === baseNote && this.getAbsoluteTimestamp() === baseNote.getAbsoluteTimestamp()) {
+                    return graphicalNote;
+                }
+            }
+        }
+        return undefined;
+    }
+
+    public getGraphicalNoteDurationFromVoice(voice: Voice): Fraction {
+        for (let idx: number = 0, len: number = this.notes.length; idx < len; ++idx) {
+            let graphicalNotes: GraphicalNote[] = this.notes[idx];
+            if (graphicalNotes[0].sourceNote.ParentVoiceEntry.ParentVoice === voice) {
+                return graphicalNotes[0].graphicalNoteLength;
+            }
+        }
+        return new Fraction(0, 1);
+    }
+
+    public findLinkedNotes(notLinkedNotes: GraphicalNote[]): void {
+        if (this.sourceStaffEntry !== undefined && this.sourceStaffEntry.Link !== undefined) {
+            for (let idx: number = 0, len: number = this.notes.length; idx < len; ++idx) {
+                let graphicalNotes: GraphicalNote[] = this.notes[idx];
+                for (let idx2: number = 0, len2: number = graphicalNotes.length; idx2 < len2; ++idx2) {
+                    let graphicalNote: GraphicalNote = graphicalNotes[idx2];
+                    if (graphicalNote.parentStaffEntry === this) {
+                        notLinkedNotes.push(graphicalNote);
+                    }
+                }
+            }
+        }
+    }
+
+    public findVoiceEntryGraphicalNotes(voiceEntry: VoiceEntry): GraphicalNote[] {
+        for (let idx: number = 0, len: number = this.notes.length; idx < len; ++idx) {
+            let graphicalNotes: GraphicalNote[] = this.notes[idx];
+            for (let idx2: number = 0, len2: number = graphicalNotes.length; idx2 < len2; ++idx2) {
+                let graphicalNote: GraphicalNote = graphicalNotes[idx2];
+                if (graphicalNote.sourceNote.ParentVoiceEntry === voiceEntry) {
+                    return graphicalNotes;
+                }
+            }
+        }
+        return undefined;
+    }
+
+    public isVoiceEntryPartOfLinkedVoiceEntry(voiceEntry: VoiceEntry): boolean {
+        if (this.sourceStaffEntry.Link !== undefined) {
+            for (let idx: number = 0, len: number = this.sourceStaffEntry.Link.LinkStaffEntries.length; idx < len; ++idx) {
+                let sEntry: SourceStaffEntry = this.sourceStaffEntry.Link.LinkStaffEntries[idx];
+                if (sEntry.VoiceEntries.indexOf(voiceEntry) !== -1 && sEntry !== this.sourceStaffEntry) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    public getMainVoice(): Voice {
+        for (let idx: number = 0, len: number = this.sourceStaffEntry.VoiceEntries.length; idx < len; ++idx) {
+            let voiceEntry: VoiceEntry = this.sourceStaffEntry.VoiceEntries[idx];
+            if (!(voiceEntry.ParentVoice instanceof LinkedVoice)) {
+                return voiceEntry.ParentVoice;
+            }
+        }
+        return this.notes[0][0].sourceNote.ParentVoiceEntry.ParentVoice;
+    }
+
+    public findStaffEntryMinNoteLength(): Fraction {
+        let minLength: Fraction = new Fraction(Number.MAX_VALUE, 1);
+        for (let idx: number = 0, len: number = this.notes.length; idx < len; ++idx) {
+            let graphicalNotes: GraphicalNote[] = this.notes[idx];
+            for (let idx2: number = 0, len2: number = graphicalNotes.length; idx2 < len2; ++idx2) {
+                let graphicalNote: GraphicalNote = graphicalNotes[idx2];
+                let calNoteLen: Fraction = graphicalNote.graphicalNoteLength;
+                if (calNoteLen < minLength && calNoteLen.Numerator > 0) {
+                    minLength = calNoteLen;
+                }
+            }
+        }
+        return minLength;
+    }
+
+    public findStaffEntryMaxNoteLength(): Fraction {
+        let maxLength: Fraction = new Fraction(0, 1);
+        for (let idx: number = 0, len: number = this.notes.length; idx < len; ++idx) {
+            let graphicalNotes: GraphicalNote[] = this.notes[idx];
+            for (let idx2: number = 0, len2: number = graphicalNotes.length; idx2 < len2; ++idx2) {
+                let graphicalNote: GraphicalNote = graphicalNotes[idx2];
+                let calNoteLen: Fraction = graphicalNote.graphicalNoteLength;
+                if (calNoteLen > maxLength && calNoteLen.Numerator > 0) {
+                    maxLength = calNoteLen;
+                }
+            }
+        }
+        return maxLength;
+    }
+
+    public findOrCreateGraphicalNotesListFromVoiceEntry(voiceEntry: VoiceEntry): GraphicalNote[] {
+        let graphicalNotes: GraphicalNote[];
+        if (this.notes.length === 0) {
+            graphicalNotes = [];
+            this.notes.push(graphicalNotes);
+        } else {
+            for (let i: number = 0; i < this.notes.length; i++) {
+                if (this.notes[i][0].sourceNote.ParentVoiceEntry.ParentVoice === voiceEntry.ParentVoice) {
+                    return this.notes[i];
+                }
+            }
+            graphicalNotes = [];
+            this.notes.push(graphicalNotes);
+        }
+        return graphicalNotes;
+    }
+
+    public findOrCreateGraphicalNotesListFromGraphicalNote(graphicalNote: GraphicalNote): GraphicalNote[] {
+        let graphicalNotes: GraphicalNote[];
+        let tieStartSourceStaffEntry: SourceStaffEntry = graphicalNote.sourceNote.ParentStaffEntry;
+        if (this.sourceStaffEntry !== tieStartSourceStaffEntry) {
+            graphicalNotes = this.findOrCreateGraphicalNotesListFromVoiceEntry(graphicalNote.sourceNote.ParentVoiceEntry);
+        } else {
+            if (this.notes.length === 0) {
+                graphicalNotes = [];
+                this.notes.push(graphicalNotes);
+            } else {
+                for (let i: number = 0; i < this.notes.length; i++) {
+                    if (this.notes[i][0].sourceNote.ParentVoiceEntry.ParentVoice === graphicalNote.sourceNote.ParentVoiceEntry.ParentVoice) {
+                        return this.notes[i];
+                    }
+                }
+                graphicalNotes = [];
+                this.notes.push(graphicalNotes);
+            }
+        }
+        return graphicalNotes;
+    }
+
+    public addGraphicalNoteToListAtCorrectYPosition(graphicalNotes: GraphicalNote[], graphicalNote: GraphicalNote): void {
+        if (graphicalNotes.length === 0 ||
+            graphicalNote.PositionAndShape.RelativePosition.y < CollectionUtil.last(graphicalNotes).PositionAndShape.RelativePosition.Y) {
+            graphicalNotes.push(graphicalNote);
+        } else {
+            for (let i: number = graphicalNotes.length - 1; i >= 0; i--) {
+                if (graphicalNotes[i].PositionAndShape.RelativePosition.y > graphicalNote.PositionAndShape.RelativePosition.y) {
+                    graphicalNotes.splice(i + 1, 0, graphicalNote);
+                    break;
+                }
+                if (i === 0) {
+                    graphicalNotes.splice(0, 0, graphicalNote);
+                    break;
+                }
+            }
+        }
+    }
+}

+ 54 - 0
src/MusicalScore/Graphical/GraphicalStaffEntryLink.ts

@@ -0,0 +1,54 @@
+import {StaffEntryLink} from "../VoiceData/StaffEntryLink";
+import {GraphicalStaffEntry} from "./GraphicalStaffEntry";
+import {GraphicalNote} from "./GraphicalNote";
+
+export class GraphicalStaffEntryLink {
+    private staffEntryLink: StaffEntryLink;
+    private graphicalLinkedStaffEntries: GraphicalStaffEntry[] = [];
+    constructor(staffEntryLink: StaffEntryLink) {
+        this.staffEntryLink = staffEntryLink;
+        this.initialize();
+    }
+    public get GetStaffEntryLink(): StaffEntryLink {
+        return this.staffEntryLink;
+    }
+    public get GraphicalLinkedStaffEntries(): GraphicalStaffEntry[] {
+        return this.graphicalLinkedStaffEntries;
+    }
+    public set GraphicalLinkedStaffEntries(value: GraphicalStaffEntry[]) {
+        this.graphicalLinkedStaffEntries = value;
+    }
+    public isFilled(): boolean {
+        for (let i: number = 0; i < this.graphicalLinkedStaffEntries.length; i++) {
+            if (this.graphicalLinkedStaffEntries[i] === undefined) {
+                return false;
+            }
+        }
+        return true;
+    }
+    public getLinkedStaffEntriesGraphicalNotes(graphicalStaffEntry: GraphicalStaffEntry): GraphicalNote[] {
+        if (this.graphicalLinkedStaffEntries.indexOf(graphicalStaffEntry) !== -1) {
+            let notes: GraphicalNote[] = [];
+            for (let idx: number = 0, len: number = this.graphicalLinkedStaffEntries.length; idx < len; ++idx) {
+                let graphicalLinkedStaffEntry: GraphicalStaffEntry = this.graphicalLinkedStaffEntries[idx];
+                for (let idx2: number = 0, len2: number = graphicalLinkedStaffEntry.notes.length; idx2 < len2; ++idx2) {
+                    let graphicalNotes: GraphicalNote[] = graphicalLinkedStaffEntry.notes[idx2];
+                    for (let idx3: number = 0, len3: number = graphicalNotes.length; idx3 < len3; ++idx3) {
+                        let graphicalNote: GraphicalNote = graphicalNotes[idx3];
+                        if (graphicalNote.sourceNote.ParentStaffEntry.Link !== undefined
+                            && graphicalNote.sourceNote.ParentVoiceEntry === this.staffEntryLink.GetVoiceEntry) {
+                            notes.push(graphicalNote);
+                        }
+                    }
+                }
+            }
+            return notes;
+        }
+        return undefined;
+    }
+    private initialize(): void {
+        for (let idx: number = 0, len: number = this.staffEntryLink.LinkStaffEntries.length; idx < len; ++idx) {
+            this.graphicalLinkedStaffEntries.push(undefined);
+        }
+    }
+}

+ 28 - 0
src/MusicalScore/Graphical/GraphicalTie.ts

@@ -0,0 +1,28 @@
+import {Tie} from "../VoiceData/Tie";
+import {GraphicalNote} from "./GraphicalNote";
+
+export class GraphicalTie {
+    private tie: Tie;
+    private startNote: GraphicalNote;
+    private endNote: GraphicalNote;
+    constructor(tie: Tie, start: GraphicalNote = undefined, end: GraphicalNote = undefined) {
+        this.tie = tie;
+        this.startNote = start;
+        this.endNote = end;
+    }
+    public get GetTie(): Tie {
+        return this.tie;
+    }
+    public get StartNote(): GraphicalNote {
+        return this.startNote;
+    }
+    public set StartNote(value: GraphicalNote) {
+        this.startNote = value;
+    }
+    public get EndNote(): GraphicalNote {
+        return this.endNote;
+    }
+    public set EndNote(value: GraphicalNote) {
+        this.endNote = value;
+    }
+}

+ 1518 - 0
src/MusicalScore/Graphical/MusicSheetCalculator.ts

@@ -0,0 +1,1518 @@
+import {GraphicalStaffEntry} from "./GraphicalStaffEntry";
+import {StaffLine} from "./StaffLine";
+import {GraphicalMusicSheet} from "./GraphicalMusicSheet";
+import {EngravingRules} from "./EngravingRules";
+import {Tie} from "../VoiceData/Tie";
+import {Fraction} from "../../Common/DataObjects/fraction";
+import {Note} from "../VoiceData/Note";
+import {MusicSheet} from "../MusicSheet";
+import {StaffMeasure} from "./StaffMeasure";
+import {ClefInstruction} from "../VoiceData/Instructions/ClefInstruction";
+import {LyricWord} from "../VoiceData/Lyrics/LyricsWord";
+import {SourceMeasure} from "../VoiceData/SourceMeasure";
+import {GraphicalMusicPage} from "./GraphicalMusicPage";
+import {GraphicalNote} from "./GraphicalNote";
+import {Beam} from "../VoiceData/Beam";
+import {OctaveEnum} from "../VoiceData/Expressions/ContinuousExpressions/octaveShift";
+import {LyricsEntry} from "../VoiceData/Lyrics/LyricsEntry";
+import {VoiceEntry} from "../VoiceData/VoiceEntry";
+import {OrnamentContainer} from "../VoiceData/OrnamentContainer";
+import {ArticulationEnum} from "../VoiceData/VoiceEntry";
+import {Tuplet} from "../VoiceData/Tuplet";
+import {MusicSystem} from "./MusicSystem";
+import {GraphicalTie} from "./GraphicalTie";
+import {RepetitionInstruction} from "../VoiceData/Instructions/RepetitionInstruction";
+import {MultiExpression} from "../VoiceData/Expressions/multiExpression";
+import {StaffEntryLink} from "../VoiceData/StaffEntryLink";
+import {MusicSystemBuilder} from "./MusicSystemBuilder";
+import {MultiTempoExpression} from "../VoiceData/Expressions/multiTempoExpression";
+import {Repetition} from "../MusicSource/Repetition";
+import {PointF2D} from "../../Common/DataObjects/PointF2D";
+import {SourceStaffEntry} from "../VoiceData/SourceStaffEntry";
+import {BoundingBox} from "./BoundingBox";
+import {Instrument} from "../Instrument";
+import {GraphicalLabel} from "./GraphicalLabel";
+import {TextAlignment} from "../../Common/Enums/TextAlignment";
+import {VerticalGraphicalStaffEntryContainer} from "./VerticalGraphicalStaffEntryContainer";
+import {KeyInstruction} from "../VoiceData/Instructions/KeyInstruction";
+import {AbstractNotationInstruction} from "../VoiceData/Instructions/AbstractNotationInstruction";
+import {ClefEnum} from "../VoiceData/Instructions/ClefInstruction";
+import {TechnicalInstruction} from "../VoiceData/Instructions/TechnicalInstruction";
+import {Pitch} from "../../Common/DataObjects/pitch";
+import {LinkedVoice} from "../VoiceData/LinkedVoice";
+import {ColDirEnum} from "./BoundingBox";
+import {IGraphicalSymbolFactory} from "../Interfaces/IGraphicalSymbolFactory";
+import {ITextMeasurer} from "../Interfaces/ITextMeasurer";
+import {ITransposeCalculator} from "../Interfaces/ITransposeCalculator";
+import {OctaveShiftParams} from "./OctaveShiftParams";
+import {AccidentalCalculator} from "./AccidentalCalculator";
+import {MidiInstrument} from "../VoiceData/Instructions/ClefInstruction";
+import {Staff} from "../VoiceData/Staff";
+import {OctaveShift} from "../VoiceData/Expressions/ContinuousExpressions/octaveShift";
+import {Logging} from "../../Common/logging";
+import Dictionary from "typescript-collections/dist/lib/Dictionary";
+import {CollectionUtil} from "../../Util/collectionUtil";
+
+export abstract class MusicSheetCalculator {
+    public static transposeCalculator: ITransposeCalculator;
+    protected static textMeasurer: ITextMeasurer;
+    protected staffEntriesWithGraphicalTies: GraphicalStaffEntry[] = [];
+    protected staffEntriesWithOrnaments: GraphicalStaffEntry[] = [];
+    protected staffEntriesWithChordSymbols: GraphicalStaffEntry[] = [];
+    protected staffLinesWithLyricWords: StaffLine[] = [];
+    protected staffLinesWithGraphicalExpressions: StaffLine[] = [];
+    protected graphicalMusicSheet: GraphicalMusicSheet;
+    protected rules: EngravingRules;
+    protected symbolFactory: IGraphicalSymbolFactory;
+
+    constructor(symbolFactory: IGraphicalSymbolFactory) {
+        this.symbolFactory = symbolFactory;
+    }
+
+    public static get TextMeasurer(): ITextMeasurer {
+        return MusicSheetCalculator.textMeasurer;
+    }
+
+    public static set TextMeasurer(value: ITextMeasurer) {
+        MusicSheetCalculator.textMeasurer = value;
+    }
+
+    protected get leadSheet(): boolean {
+        return this.graphicalMusicSheet.LeadSheet;
+    }
+
+    private static addTieToTieTimestampsDict(tieTimestampListDict: Dictionary<Tie, Fraction[]>, note: Note): void {
+        note.NoteTie.initializeBoolList();
+        let tieTimestampList: Fraction[] = [];
+        for (let m: number = 0; m < note.NoteTie.Fractions.length; m++) {
+            let musicTimestamp: Fraction;
+            if (m === 0) {
+                musicTimestamp = Fraction.plus(note.calculateNoteLengthWithoutTie(), note.getAbsoluteTimestamp());
+            } else {
+                musicTimestamp = Fraction.plus(tieTimestampList[m - 1], note.NoteTie.Fractions[m - 1]);
+            }
+            tieTimestampList.push(musicTimestamp);
+        }
+        tieTimestampListDict.setValue(note.NoteTie, tieTimestampList);
+    }
+
+    private static setMeasuresMinStaffEntriesWidth(measures: StaffMeasure[], minimumStaffEntriesWidth: number): void {
+        for (let idx: number = 0, len: number = measures.length; idx < len; ++idx) {
+            let measure: StaffMeasure = measures[idx];
+            measure.minimumStaffEntriesWidth = minimumStaffEntriesWidth;
+        }
+    }
+
+    public initialize(graphicalMusicSheet: GraphicalMusicSheet): void {
+        this.graphicalMusicSheet = graphicalMusicSheet;
+        this.rules = graphicalMusicSheet.ParentMusicSheet.rules;
+        this.prepareGraphicalMusicSheet();
+        this.calculate();
+    }
+
+    public prepareGraphicalMusicSheet(): void {
+        //this.graphicalMusicSheet.SystemImages.length = 0;
+        let musicSheet: MusicSheet = this.graphicalMusicSheet.ParentMusicSheet;
+        this.staffEntriesWithGraphicalTies = [];
+        this.staffEntriesWithOrnaments = [];
+        this.staffEntriesWithChordSymbols = [];
+        this.staffLinesWithLyricWords = [];
+        this.staffLinesWithGraphicalExpressions = [];
+        this.graphicalMusicSheet.Initialize();
+        let measureList: StaffMeasure[][] = this.graphicalMusicSheet.MeasureList;
+        let accidentalCalculators: AccidentalCalculator[] = this.createAccidentalCalculators();
+        let activeClefs: ClefInstruction[] = this.graphicalMusicSheet.initializeActiveClefs();
+        let lyricWords: LyricWord[] = [];
+        let completeNumberOfStaves: number = musicSheet.getCompleteNumberOfStaves();
+        let openOctaveShifts: OctaveShiftParams[] = [];
+        let tieTimestampListDictList: Dictionary<Tie, Fraction[]>[] = [];
+        for (let i: number = 0; i < completeNumberOfStaves; i++) {
+            let tieTimestampListDict: Dictionary<Tie, Fraction[]> = new Dictionary<Tie, Fraction[]>();
+            tieTimestampListDictList.push(tieTimestampListDict);
+            openOctaveShifts.push(undefined);
+        }
+        for (let idx: number = 0, len: number = musicSheet.SourceMeasures.length; idx < len; ++idx) {
+            let sourceMeasure: SourceMeasure = musicSheet.SourceMeasures[idx];
+            let graphicalMeasures: StaffMeasure[] = this.createGraphicalMeasuresForSourceMeasure(
+                sourceMeasure,
+                accidentalCalculators,
+                lyricWords,
+                tieTimestampListDictList,
+                openOctaveShifts,
+                activeClefs
+            );
+            measureList.push(graphicalMeasures);
+        }
+        this.handleStaffEntries();
+        this.calculateVerticalContainersList();
+        this.setIndecesToVerticalGraphicalContainers();
+    }
+
+    public calculate(): void {
+        this.clearSystemsAndMeasures();
+        this.clearRecreatedObjects();
+        this.createGraphicalTies();
+        this.calculateSheetLabelBoundingBoxes();
+        this.calculateXLayout(this.graphicalMusicSheet, this.maxInstrNameLabelLength());
+        this.graphicalMusicSheet.MusicPages.length = 0;
+        this.calculateMusicSystems();
+        this.graphicalMusicSheet.MusicPages[0].PositionAndShape.BorderMarginBottom += 9;
+        GraphicalMusicSheet.transformRelativeToAbsolutePosition(this.graphicalMusicSheet);
+    }
+
+    public calculateXLayout(graphicalMusicSheet: GraphicalMusicSheet, maxInstrNameLabelLength: number): void {
+        let minLength: number = 0;
+        let maxInstructionsLength: number = this.rules.MaxInstructionsConstValue;
+        if (this.graphicalMusicSheet.MeasureList.length > 0) {
+            let measures: StaffMeasure[] = this.graphicalMusicSheet.MeasureList[0];
+            let minimumStaffEntriesWidth: number = this.calculateMeasureXLayout(measures);
+            MusicSheetCalculator.setMeasuresMinStaffEntriesWidth(measures, minimumStaffEntriesWidth);
+            minLength = minimumStaffEntriesWidth * 1.2 + maxInstrNameLabelLength + maxInstructionsLength;
+            for (let i: number = 1; i < this.graphicalMusicSheet.MeasureList.length; i++) {
+                measures = this.graphicalMusicSheet.MeasureList[i];
+                minimumStaffEntriesWidth = this.calculateMeasureXLayout(measures);
+                MusicSheetCalculator.setMeasuresMinStaffEntriesWidth(measures, minimumStaffEntriesWidth);
+                minLength = Math.max(minLength, minimumStaffEntriesWidth * 1.2 + maxInstructionsLength);
+            }
+        }
+        this.graphicalMusicSheet.MinAllowedSystemWidth = minLength;
+    }
+
+    protected calculateMeasureXLayout(measures: StaffMeasure[]): number {
+        throw new Error("abstract, not implemented");
+    }
+
+    protected calculateSystemYLayout(): void {
+        throw new Error("abstract, not implemented");
+    }
+
+    protected initStaffMeasuresCreation(): void {
+        throw new Error("abstract, not implemented");
+    }
+
+    protected handleBeam(graphicalNote: GraphicalNote, beam: Beam, openBeams: Beam[]): void {
+        throw new Error("abstract, not implemented");
+    }
+
+    protected createGraphicalTieNote(beams: Beam[], activeClef: ClefInstruction,
+                                     octaveShiftValue: OctaveEnum,
+                                     graphicalStaffEntry: GraphicalStaffEntry, duration: Fraction, numberOfDots: number,
+                                     openTie: Tie, isLastTieNote: boolean): void {
+        throw new Error("abstract, not implemented");
+    }
+
+    protected handleVoiceEntryLyrics(lyricsEntries: Dictionary<number, LyricsEntry>, voiceEntry: VoiceEntry, graphicalStaffEntry: GraphicalStaffEntry,
+                                     openLyricWords: LyricWord[]): void {
+        throw new Error("abstract, not implemented");
+    }
+
+    protected handleVoiceEntryOrnaments(ornamentContainer: OrnamentContainer, voiceEntry: VoiceEntry,
+                                        graphicalStaffEntry: GraphicalStaffEntry): void {
+        throw new Error("abstract, not implemented");
+    }
+
+    protected handleVoiceEntryArticulations(articulations: ArticulationEnum[],
+                                            voiceEntry: VoiceEntry,
+                                            graphicalStaffEntry: GraphicalStaffEntry): void {
+        throw new Error("abstract, not implemented");
+    }
+
+    protected handleTuplet(graphicalNote: GraphicalNote, tuplet: Tuplet, openTuplets: Tuplet[]): void {
+        throw new Error("abstract, not implemented");
+    }
+
+    protected layoutVoiceEntry(voiceEntry: VoiceEntry, graphicalNotes: GraphicalNote[],
+                               graphicalStaffEntry: GraphicalStaffEntry, hasPitchedNote: boolean, isGraceStaffEntry: boolean): void {
+        throw new Error("abstract, not implemented");
+    }
+
+    protected layoutStaffEntry(graphicalStaffEntry: GraphicalStaffEntry): void {
+        throw new Error("abstract, not implemented");
+    }
+
+    protected handleTie(tie: Tie, startGraphicalStaffEntry: GraphicalStaffEntry, staffIndex: number,
+                        measureIndex: number): void {
+        throw new Error("abstract, not implemented");
+    }
+
+    protected updateStaffLineBorders(staffLine: StaffLine): void {
+        throw new Error("abstract, not implemented");
+    }
+
+    protected calculateMeasureNumberPlacement(musicSystem: MusicSystem): void {
+        throw new Error("abstract, not implemented");
+    }
+
+    protected layoutGraphicalTie(tie: GraphicalTie, tieIsAtSystemBreak: boolean): void {
+        throw new Error("abstract, not implemented");
+    }
+
+    protected calculateSingleStaffLineLyricsPosition(staffLine: StaffLine, lyricVersesNumber: number[]): void {
+        throw new Error("abstract, not implemented");
+    }
+
+    protected calculateSingleOctaveShift(sourceMeasure: SourceMeasure, multiExpression: MultiExpression,
+                                         measureIndex: number, staffIndex: number): void {
+        throw new Error("abstract, not implemented");
+    }
+
+    protected calculateWordRepetitionInstruction(repetitionInstruction: RepetitionInstruction,
+                                                 measureIndex: number): void {
+        throw new Error("abstract, not implemented");
+    }
+
+    protected calculateMoodAndUnknownExpression(multiExpression: MultiExpression, measureIndex: number, staffIndex: number): void {
+        throw new Error("abstract, not implemented");
+    }
+
+    protected clearRecreatedObjects(): void {
+        Logging.log("clearRecreatedObjects not implemented");
+    }
+
+    protected handleStaffEntryLink(graphicalStaffEntry: GraphicalStaffEntry,
+                                   staffEntryLinks: StaffEntryLink[]): void {
+        Logging.log("handleStaffEntryLink not implemented");
+    }
+
+    protected calculateMusicSystems(): void {
+        if (this.graphicalMusicSheet.MeasureList === undefined) {
+            return;
+        }
+
+        let allMeasures: StaffMeasure[][] = this.graphicalMusicSheet.MeasureList;
+        if (allMeasures === undefined) {
+            return;
+        }
+        let visibleMeasureList: StaffMeasure[][] = [];
+        for (let idx: number = 0, len: number = allMeasures.length; idx < len; ++idx) {
+            let staffMeasures: StaffMeasure[] = allMeasures[idx];
+            let visibleStaffMeasures: StaffMeasure[] = [];
+            for (let idx2: number = 0, len2: number = staffMeasures.length; idx2 < len2; ++idx2) {
+                let staffMeasure: StaffMeasure = allMeasures[idx][idx2];
+                if (staffMeasure.isVisible()) {
+                    visibleStaffMeasures.push(staffMeasure);
+                }
+            }
+            visibleMeasureList.push(visibleStaffMeasures);
+        }
+
+        let numberOfStaffLines: number = 0;
+        for (let idx: number = 0, len: number = visibleMeasureList.length; idx < len; ++idx) {
+            let gmlist: StaffMeasure[] = visibleMeasureList[idx];
+            numberOfStaffLines = Math.max(gmlist.length, numberOfStaffLines);
+            break;
+        }
+        if (numberOfStaffLines === 0) {
+            return;
+        }
+        let musicSystemBuilder: MusicSystemBuilder = new MusicSystemBuilder();
+        musicSystemBuilder.initialize(this.graphicalMusicSheet, visibleMeasureList, numberOfStaffLines, this.symbolFactory);
+        musicSystemBuilder.buildMusicSystems();
+        this.checkMeasuresForWholeRestNotes();
+        if (!this.leadSheet) {
+            this.calculateBeams();
+            this.optimizeRestPlacement();
+            this.calculateStaffEntryArticulationMarks();
+            this.calculateTieCurves();
+        }
+        this.calculateSkyBottomLines();
+        this.calculateTupletNumbers();
+        for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
+            let graphicalMusicPage: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
+            for (let idx2: number = 0, len2: number = graphicalMusicPage.MusicSystems.length; idx2 < len2; ++idx2) {
+                let musicSystem: MusicSystem = graphicalMusicPage.MusicSystems[idx2];
+                this.calculateMeasureNumberPlacement(musicSystem);
+            }
+        }
+        if (!this.leadSheet) {
+            this.calculateSlurs();
+        }
+        if (!this.leadSheet) {
+            this.calculateOrnaments();
+        }
+        this.updateSkyBottomLines();
+        this.calculateChordSymbols();
+        if (!this.leadSheet) {
+            this.calculateDynamicExpressions();
+            this.optimizeStaffLineDynamicExpressionsPositions();
+            this.calculateMoodAndUnknownExpressions();
+            this.calculateOctaveShifts();
+            this.calculateWordRepetitionInstructions();
+        }
+        this.calculateRepetitionEndings();
+        if (!this.leadSheet) {
+            this.calculateTempoExpressions();
+        }
+        this.calculateLyricsPosition();
+        for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
+            let graphicalMusicPage: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
+            for (let idx2: number = 0, len2: number = graphicalMusicPage.MusicSystems.length; idx2 < len2; ++idx2) {
+                let musicSystem: MusicSystem = graphicalMusicPage.MusicSystems[idx2];
+                for (let idx3: number = 0, len3: number = musicSystem.StaffLines.length; idx3 < len3; ++idx3) {
+                    let staffLine: StaffLine = musicSystem.StaffLines[idx3];
+                    this.updateStaffLineBorders(staffLine);
+                }
+            }
+        }
+        this.calculateComments();
+        this.calculateSystemYLayout();
+        this.calculateMarkedAreas();
+        for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
+            let graphicalMusicPage: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
+            for (let idx2: number = 0, len2: number = graphicalMusicPage.MusicSystems.length; idx2 < len2; ++idx2) {
+                let musicSystem: MusicSystem = graphicalMusicPage.MusicSystems[idx2];
+                musicSystem.setMusicSystemLabelsYPosition();
+                if (!this.leadSheet) {
+                    musicSystem.setYPositionsToVerticalLineObjectsAndCreateLines(this.rules);
+                    musicSystem.createSystemLeftLine(this.rules.SystemThinLineWidth, this.rules.SystemLabelsRightMargin);
+                    musicSystem.createInstrumentBrackets(this.graphicalMusicSheet.ParentMusicSheet.Instruments, this.rules.StaffHeight);
+                    musicSystem.createGroupBrackets(this.graphicalMusicSheet.ParentMusicSheet.InstrumentalGroups, this.rules.StaffHeight, 0);
+                    musicSystem.alignBeginInstructions();
+                } else if (musicSystem === musicSystem.Parent.MusicSystems[0]) {
+                    musicSystem.createSystemLeftLine(this.rules.SystemThinLineWidth, this.rules.SystemLabelsRightMargin);
+                }
+                musicSystem.calculateBorders(this.rules);
+            }
+            let distance: number = graphicalMusicPage.MusicSystems[0].PositionAndShape.BorderTop;
+            for (let idx2: number = 0, len2: number = graphicalMusicPage.MusicSystems.length; idx2 < len2; ++idx2) {
+                let musicSystem: MusicSystem = graphicalMusicPage.MusicSystems[idx2];
+                // let newPosition: PointF2D = new PointF2D(musicSystem.PositionAndShape.RelativePosition.x,
+                // musicSystem.PositionAndShape.RelativePosition.y - distance);
+                musicSystem.PositionAndShape.RelativePosition =
+                    new PointF2D(musicSystem.PositionAndShape.RelativePosition.x, musicSystem.PositionAndShape.RelativePosition.y - distance);
+            }
+            for (let idx2: number = 0, len2: number = graphicalMusicPage.MusicSystems.length; idx2 < len2; ++idx2) {
+                let musicSystem: MusicSystem = graphicalMusicPage.MusicSystems[idx2];
+                for (let idx3: number = 0, len3: number = musicSystem.StaffLines.length; idx3 < len3; ++idx3) {
+                    let staffLine: StaffLine = musicSystem.StaffLines[idx3];
+                    staffLine.addActivitySymbolClickArea();
+                }
+            }
+            if (graphicalMusicPage === this.graphicalMusicSheet.MusicPages[0]) {
+                this.calculatePageLabels(graphicalMusicPage);
+            }
+            graphicalMusicPage.PositionAndShape.calculateTopBottomBorders();
+        }
+    }
+
+    protected updateSkyBottomLine(staffLine: StaffLine): void {
+        Logging.log("updateSkyBottomLine not implemented");
+    }
+
+    protected calculateSkyBottomLine(staffLine: StaffLine): void {
+        Logging.log("calculateSkyBottomLine not implemented");
+    }
+
+    protected calculateMarkedAreas(): void {
+        Logging.log("calculateMarkedAreas not implemented");
+    }
+
+    protected calculateComments(): void {
+        Logging.log("calculateComments not implemented");
+    }
+
+    protected optimizeStaffLineDynamicExpressionsPositions(): void {
+        return;
+    }
+
+    protected calculateChordSymbols(): void {
+        return;
+    }
+
+    protected layoutMeasureWithWholeRest(rest: GraphicalNote, gse: GraphicalStaffEntry,
+                                         measure: StaffMeasure): void {
+        return;
+    }
+
+    protected layoutBeams(staffEntry: GraphicalStaffEntry): void {
+        return;
+    }
+
+    protected layoutArticulationMarks(articulations: ArticulationEnum[], voiceEntry: VoiceEntry, graphicalStaffEntry: GraphicalStaffEntry): void {
+        return;
+    }
+
+    protected layoutOrnament(ornaments: OrnamentContainer, voiceEntry: VoiceEntry,
+                             graphicalStaffEntry: GraphicalStaffEntry): void {
+        return;
+    }
+
+    protected calculateRestNotePlacementWithinGraphicalBeam(graphicalStaffEntry: GraphicalStaffEntry,
+                                                            restNote: GraphicalNote,
+                                                            previousNote: GraphicalNote,
+                                                            nextStaffEntry: GraphicalStaffEntry,
+                                                            nextNote: GraphicalNote): void {
+        return;
+    }
+
+    protected calculateTupletNumbers(): void {
+        return;
+    }
+
+    protected calculateSlurs(): void {
+        return;
+    }
+
+    protected calculateDynamicExpressionsForSingleMultiExpression(multiExpression: MultiExpression, measureIndex: number, staffIndex: number): void {
+        return;
+    }
+
+    protected calcGraphicalRepetitionEndingsRecursively(repetition: Repetition): void {
+        return;
+    }
+
+    protected layoutSingleRepetitionEnding(start: StaffMeasure, end: StaffMeasure, numberText: string,
+                                           offset: number, leftOpen: boolean, rightOpen: boolean): void {
+        return;
+    }
+
+    protected calculateTempoExpressionsForSingleMultiTempoExpression(sourceMeasure: SourceMeasure, multiTempoExpression: MultiTempoExpression,
+                                                                     measureIndex: number): void {
+        return;
+    }
+
+    protected clearSystemsAndMeasures(): void {
+        for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
+            let graphicalMusicPage: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
+            for (let idx2: number = 0, len2: number = graphicalMusicPage.MusicSystems.length; idx2 < len2; ++idx2) {
+                let musicSystem: MusicSystem = graphicalMusicPage.MusicSystems[idx2];
+                for (let idx3: number = 0, len3: number = musicSystem.StaffLines.length; idx3 < len3; ++idx3) {
+                    let staffLine: StaffLine = musicSystem.StaffLines[idx3];
+                    for (let idx4: number = 0, len4: number = staffLine.Measures.length; idx4 < len4; ++idx4) {
+                        let graphicalMeasure: StaffMeasure = staffLine.Measures[idx4];
+                        if (graphicalMeasure.FirstInstructionStaffEntry !== undefined) {
+                            let index: number = graphicalMeasure.PositionAndShape.ChildElements.indexOf(
+                                graphicalMeasure.FirstInstructionStaffEntry.PositionAndShape
+                            );
+                            if (index > -1) {
+                                graphicalMeasure.PositionAndShape.ChildElements.splice(index, 1);
+                            }
+                            graphicalMeasure.FirstInstructionStaffEntry = undefined;
+                            graphicalMeasure.beginInstructionsWidth = 0.0;
+                        }
+                        if (graphicalMeasure.LastInstructionStaffEntry !== undefined) {
+                            let index: number = graphicalMeasure.PositionAndShape.ChildElements.indexOf(
+                                graphicalMeasure.LastInstructionStaffEntry.PositionAndShape
+                            );
+                            if (index > -1) {
+                                graphicalMeasure.PositionAndShape.ChildElements.splice(index, 1);
+                            }
+                            graphicalMeasure.LastInstructionStaffEntry = undefined;
+                            graphicalMeasure.endInstructionsWidth = 0.0;
+                        }
+                    }
+                    staffLine.Measures = [];
+                    staffLine.PositionAndShape.ChildElements = [];
+                }
+                musicSystem.StaffLines.length = 0;
+                musicSystem.PositionAndShape.ChildElements = [];
+            }
+            graphicalMusicPage.MusicSystems = [];
+            graphicalMusicPage.PositionAndShape.ChildElements = [];
+        }
+        this.graphicalMusicSheet.MusicPages = [];
+    }
+
+    protected handleVoiceEntry(voiceEntry: VoiceEntry, graphicalStaffEntry: GraphicalStaffEntry,
+                               accidentalCalculator: AccidentalCalculator, openLyricWords: LyricWord[],
+                               tieTimestampListDict: Dictionary<Tie, Fraction[]>, activeClef: ClefInstruction,
+                               openTuplets: Tuplet[], openBeams: Beam[],
+                               octaveShiftValue: OctaveEnum, grace: boolean = false, linkedNotes: Note[] = undefined,
+                               sourceStaffEntry: SourceStaffEntry = undefined): OctaveEnum {
+        let graphicalNotes: GraphicalNote[] = graphicalStaffEntry.findOrCreateGraphicalNotesListFromVoiceEntry(voiceEntry);
+        for (let idx: number = 0, len: number = voiceEntry.Notes.length; idx < len; ++idx) {
+            let note: Note = voiceEntry.Notes[idx];
+            if (sourceStaffEntry !== undefined && sourceStaffEntry.Link !== undefined && linkedNotes !== undefined && linkedNotes.indexOf(note) > -1) {
+                continue;
+            }
+            let graphicalNote: GraphicalNote;
+            let numberOfDots: number = note.calculateNumberOfNeededDots();
+            if (grace) {
+                graphicalNote = this.symbolFactory.createGraceNote(note, numberOfDots, graphicalStaffEntry, activeClef, octaveShiftValue);
+            } else {
+                graphicalNote = this.symbolFactory.createNote(note, numberOfDots, graphicalStaffEntry, activeClef, octaveShiftValue);
+            }
+            if (note.NoteTie !== undefined) {
+                MusicSheetCalculator.addTieToTieTimestampsDict(tieTimestampListDict, note);
+            }
+            if (note.Pitch !== undefined) {
+                this.checkNoteForAccidental(graphicalNote, accidentalCalculator, activeClef, octaveShiftValue, grace);
+            }
+            this.resetYPositionForLeadSheet(graphicalNote.PositionAndShape);
+            graphicalStaffEntry.addGraphicalNoteToListAtCorrectYPosition(graphicalNotes, graphicalNote);
+            graphicalStaffEntry.PositionAndShape.ChildElements.push(graphicalNote.PositionAndShape);
+            graphicalNote.PositionAndShape.calculateBoundingBox();
+            if (!this.leadSheet) {
+                if (note.NoteBeam !== undefined) {
+                    this.handleBeam(graphicalNote, note.NoteBeam, openBeams);
+                }
+                if (note.NoteTuplet !== undefined) {
+                    this.handleTuplet(graphicalNote, note.NoteTuplet, openTuplets);
+                }
+            }
+        }
+        if (voiceEntry.Articulations.length > 0) {
+            this.handleVoiceEntryArticulations(voiceEntry.Articulations, voiceEntry, graphicalStaffEntry);
+        }
+        if (voiceEntry.TechnicalInstructions.length > 0) {
+            this.checkVoiceEntriesForTechnicalInstructions(voiceEntry, graphicalStaffEntry);
+        }
+        if (voiceEntry.LyricsEntries.size() > 0) {
+            this.handleVoiceEntryLyrics(voiceEntry.LyricsEntries, voiceEntry, graphicalStaffEntry, openLyricWords);
+        }
+        if (voiceEntry.OrnamentContainer !== undefined) {
+            this.handleVoiceEntryOrnaments(voiceEntry.OrnamentContainer, voiceEntry, graphicalStaffEntry);
+        }
+        return octaveShiftValue;
+    }
+
+    protected handleVoiceEntryGraceNotes(graceEntries: VoiceEntry[], graphicalGraceEntries: GraphicalStaffEntry[], graphicalStaffEntry: GraphicalStaffEntry,
+                                         accidentalCalculator: AccidentalCalculator, activeClef: ClefInstruction,
+                                         octaveShiftValue: OctaveEnum, lyricWords: LyricWord[],
+                                         tieTimestampListDict: Dictionary<Tie, Fraction[]>,
+                                         tuplets: Tuplet[], beams: Beam[]): void {
+        if (graceEntries !== undefined) {
+            for (let idx: number = 0, len: number = graceEntries.length; idx < len; ++idx) {
+                let graceVoiceEntry: VoiceEntry = graceEntries[idx];
+                let graceStaffEntry: GraphicalStaffEntry = this.symbolFactory.createGraceStaffEntry(
+                    graphicalStaffEntry,
+                    graphicalStaffEntry.parentMeasure
+                );
+                graphicalGraceEntries.push(graceStaffEntry);
+                graphicalStaffEntry.PositionAndShape.ChildElements.push(graceStaffEntry.PositionAndShape);
+                this.handleVoiceEntry(
+                    graceVoiceEntry, graceStaffEntry, accidentalCalculator, lyricWords,
+                    tieTimestampListDict, activeClef, tuplets,
+                    beams, octaveShiftValue, true
+                );
+            }
+        }
+    }
+
+    protected handleOpenTies(measure: StaffMeasure, beams: Beam[], tieTimestampListDict: Dictionary<Tie, Fraction[]>,
+                             activeClef: ClefInstruction, octaveShiftParams: OctaveShiftParams): void {
+        CollectionUtil.removeDictElementIfTrue(tieTimestampListDict, function (openTie: Tie, tieTimestamps: Fraction[]): boolean {
+            // for (let m: number = tieTimestampListDict.size() - 1; m >= 0; m--) {
+            //     let keyValuePair: KeyValuePair<Tie, Fraction[]> = tieTimestampListDict.ElementAt(m);
+            //     let openTie: Tie = keyValuePair.Key;
+            //    let tieTimestamps: Fraction[] = keyValuePair.Value;
+            let absoluteTimestamp: Fraction = undefined;
+            let k: number;
+            let removeTie: boolean = false;
+            for (; k < tieTimestamps.length; k++) {
+                if (!openTie.NoteHasBeenCreated[k]) {
+                    absoluteTimestamp = tieTimestamps[k];
+                    if (absoluteTimestamp >= Fraction.plus(measure.parentSourceMeasure.AbsoluteTimestamp, measure.parentSourceMeasure.Duration)) {
+                        continue;
+                    }
+                    let graphicalStaffEntry: GraphicalStaffEntry = undefined;
+                    if (absoluteTimestamp !== undefined) {
+                        for (let idx: number = 0, len: number = measure.staffEntries.length; idx < len; ++idx) {
+                            let gse: GraphicalStaffEntry = measure.staffEntries[idx];
+                            if (gse.getAbsoluteTimestamp() === absoluteTimestamp) {
+                                graphicalStaffEntry = gse;
+                                break;
+                            }
+                        }
+                        if (graphicalStaffEntry === undefined) {
+                            graphicalStaffEntry = this.createStaffEntryForTieNote(measure, absoluteTimestamp, openTie);
+                        }
+                    }
+                    if (graphicalStaffEntry !== undefined) {
+                        let octaveShiftValue: OctaveEnum = OctaveEnum.NONE;
+                        if (octaveShiftParams !== undefined) {
+                            if (graphicalStaffEntry.getAbsoluteTimestamp() >= octaveShiftParams.getAbsoluteStartTimestamp &&
+                                graphicalStaffEntry.getAbsoluteTimestamp() <= octaveShiftParams.getAbsoluteEndTimestamp) {
+                                octaveShiftValue = octaveShiftParams.getOpenOctaveShift.Type;
+                            }
+                        }
+                        let isLastTieNote: boolean = k === tieTimestamps.length - 1;
+                        let tieFraction: Fraction = openTie.Fractions[k];
+                        let numberOfDots: number = openTie.Start.calculateNumberOfNeededDots();
+                        this.createGraphicalTieNote(
+                            beams, activeClef, octaveShiftValue, graphicalStaffEntry, tieFraction, numberOfDots, openTie, isLastTieNote
+                        );
+                        let tieStartNote: Note = openTie.Start;
+                        if (isLastTieNote && tieStartNote.ParentVoiceEntry.Articulations.length === 1 &&
+                            tieStartNote.ParentVoiceEntry.Articulations[0] === ArticulationEnum.fermata) {
+                            this.symbolFactory.addFermataAtTiedEndNote(tieStartNote, graphicalStaffEntry);
+                        }
+                        openTie.NoteHasBeenCreated[k] = true;
+                        if (openTie.allGraphicalNotesHaveBeenCreated()) {
+                            removeTie = true;
+                            //tieTimestampListDict.remove(openTie);
+                        }
+                    }
+                }
+            }
+            return removeTie;
+        });
+    }
+
+    protected resetYPositionForLeadSheet(psi: BoundingBox): void {
+        if (this.leadSheet) {
+            psi.RelativePosition = new PointF2D(psi.RelativePosition.x, 0.0);
+        }
+    }
+
+    protected layoutVoiceEntries(graphicalStaffEntry: GraphicalStaffEntry): void {
+        graphicalStaffEntry.PositionAndShape.RelativePosition = new PointF2D(0.0, 0.0);
+        let isGraceStaffEntry: boolean = graphicalStaffEntry.staffEntryParent !== undefined;
+        if (!this.leadSheet) {
+            let graphicalStaffEntryNotes: GraphicalNote[][] = graphicalStaffEntry.notes;
+            for (let idx4: number = 0, len4: number = graphicalStaffEntryNotes.length; idx4 < len4; ++idx4) {
+                let graphicalNotes: GraphicalNote[] = graphicalStaffEntryNotes[idx4];
+                if (graphicalNotes.length === 0) {
+                    continue;
+                }
+                let voiceEntry: VoiceEntry = graphicalNotes[0].sourceNote.ParentVoiceEntry;
+                let hasPitchedNote: boolean = graphicalNotes[0].sourceNote.Pitch !== undefined;
+                this.layoutVoiceEntry(voiceEntry, graphicalNotes, graphicalStaffEntry, hasPitchedNote, isGraceStaffEntry);
+            }
+        }
+    }
+
+    protected maxInstrNameLabelLength(): number {
+        let maxLabelLength: number = 0.0;
+        for (let instrument of this.graphicalMusicSheet.ParentMusicSheet.Instruments) {
+            if (instrument.Voices.length > 0 && instrument.Voices[0].Visible) {
+                let graphicalLabel: GraphicalLabel = new GraphicalLabel(instrument.NameLabel, this.rules.InstrumentLabelTextHeight, TextAlignment.LeftCenter);
+                graphicalLabel.setLabelPositionAndShapeBorders();
+                maxLabelLength = Math.max(maxLabelLength, graphicalLabel.PositionAndShape.MarginSize.width);
+            }
+        }
+        return maxLabelLength;
+    }
+
+    protected calculateSheetLabelBoundingBoxes(): void {
+        let musicSheet: MusicSheet = this.graphicalMusicSheet.ParentMusicSheet;
+        if (musicSheet.Title !== undefined) {
+            let title: GraphicalLabel = new GraphicalLabel(musicSheet.Title, this.rules.SheetTitleHeight, TextAlignment.CenterBottom);
+            this.graphicalMusicSheet.Title = title;
+            title.setLabelPositionAndShapeBorders();
+        }
+        if (musicSheet.Subtitle !== undefined) {
+            let subtitle: GraphicalLabel = new GraphicalLabel(musicSheet.Subtitle, this.rules.SheetSubtitleHeight, TextAlignment.CenterCenter);
+            this.graphicalMusicSheet.Subtitle = subtitle;
+            subtitle.setLabelPositionAndShapeBorders();
+        }
+        if (musicSheet.Composer !== undefined) {
+            let composer: GraphicalLabel = new GraphicalLabel(musicSheet.Composer, this.rules.SheetComposerHeight, TextAlignment.RightCenter);
+            this.graphicalMusicSheet.Composer = composer;
+            composer.setLabelPositionAndShapeBorders();
+        }
+        if (musicSheet.Lyricist !== undefined) {
+            let lyricist: GraphicalLabel = new GraphicalLabel(musicSheet.Lyricist, this.rules.SheetAuthorHeight, TextAlignment.LeftCenter);
+            this.graphicalMusicSheet.Lyricist = lyricist;
+            lyricist.setLabelPositionAndShapeBorders();
+        }
+    }
+
+    protected checkMeasuresForWholeRestNotes(): void {
+        for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
+            let musicPage: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
+            for (let idx2: number = 0, len2: number = musicPage.MusicSystems.length; idx2 < len2; ++idx2) {
+                let musicSystem: MusicSystem = musicPage.MusicSystems[idx2];
+                for (let idx3: number = 0, len3: number = musicSystem.StaffLines.length; idx3 < len3; ++idx3) {
+                    let staffLine: StaffLine = musicSystem.StaffLines[idx3];
+                    for (let idx4: number = 0, len4: number = staffLine.Measures.length; idx4 < len4; ++idx4) {
+                        let measure: StaffMeasure = staffLine.Measures[idx4];
+                        if (measure.staffEntries.length === 1) {
+                            let gse: GraphicalStaffEntry = measure.staffEntries[0];
+                            if (gse.notes.length > 0 && gse.notes[0].length > 0) {
+                                let graphicalNote: GraphicalNote = gse.notes[0][0];
+                                if (graphicalNote.sourceNote.Pitch === undefined && (new Fraction(1, 2)).lt(graphicalNote.sourceNote.Length)) {
+                                    this.layoutMeasureWithWholeRest(graphicalNote, gse, measure);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    protected optimizeRestNotePlacement(graphicalStaffEntry: GraphicalStaffEntry, measure: StaffMeasure): void {
+        if (graphicalStaffEntry.notes.length === 0) {
+            return;
+        }
+        let voice1Notes: GraphicalNote[] = graphicalStaffEntry.notes[0];
+        if (voice1Notes.length === 0) {
+            return;
+        }
+        let voice1Note1: GraphicalNote = voice1Notes[0];
+        let voice1Note1IsRest: boolean = voice1Note1.sourceNote.Pitch === undefined;
+        if (graphicalStaffEntry.notes.length === 2) {
+            let voice2Note1IsRest: boolean = false;
+            let voice2Notes: GraphicalNote[] = graphicalStaffEntry.notes[1];
+            if (voice2Notes.length > 0) {
+                let voice2Note1: GraphicalNote = voice1Notes[0];
+                voice2Note1IsRest = voice2Note1.sourceNote.Pitch === undefined;
+            }
+            if (voice1Note1IsRest && voice2Note1IsRest) {
+                this.calculateTwoRestNotesPlacementWithCollisionDetection(graphicalStaffEntry);
+            } else if (voice1Note1IsRest || voice2Note1IsRest) {
+                this.calculateRestNotePlacementWithCollisionDetectionFromGraphicalNote(graphicalStaffEntry);
+            }
+        } else if (voice1Note1IsRest && graphicalStaffEntry !== measure.staffEntries[0] &&
+            graphicalStaffEntry !== measure.staffEntries[measure.staffEntries.length - 1]) {
+            let staffEntryIndex: number = measure.staffEntries.indexOf(graphicalStaffEntry);
+            let previousStaffEntry: GraphicalStaffEntry = measure.staffEntries[staffEntryIndex - 1];
+            let nextStaffEntry: GraphicalStaffEntry = measure.staffEntries[staffEntryIndex + 1];
+            if (previousStaffEntry.notes.length === 1) {
+                let previousNote: GraphicalNote = previousStaffEntry.notes[0][0];
+                if (previousNote.sourceNote.NoteBeam !== undefined && nextStaffEntry.notes.length === 1) {
+                    let nextNote: GraphicalNote = nextStaffEntry.notes[0][0];
+                    if (nextNote.sourceNote.NoteBeam !== undefined && previousNote.sourceNote.NoteBeam === nextNote.sourceNote.NoteBeam) {
+                        this.calculateRestNotePlacementWithinGraphicalBeam(
+                            graphicalStaffEntry, voice1Note1, previousNote,
+                            nextStaffEntry, nextNote
+                        );
+                        graphicalStaffEntry.PositionAndShape.calculateBoundingBox();
+                    }
+                }
+            }
+        }
+    }
+
+    protected getRelativePositionInStaffLineFromTimestamp(timestamp: Fraction, verticalIndex: number, staffLine: StaffLine,
+                                                          multiStaffInstrument: boolean, firstVisibleMeasureRelativeX: number = 0.0): PointF2D {
+        let relative: PointF2D = new PointF2D();
+        let leftStaffEntry: GraphicalStaffEntry = undefined;
+        let rightStaffEntry: GraphicalStaffEntry = undefined;
+        let numEntries: number = this.graphicalMusicSheet.VerticalGraphicalStaffEntryContainers.length;
+        let index: number = this.graphicalMusicSheet.GetInterpolatedIndexInVerticalContainers(timestamp);
+        let leftIndex: number = <number>Math.min(Math.floor(index), numEntries - 1);
+        let rightIndex: number = <number>Math.min(Math.ceil(index), numEntries - 1);
+        if (leftIndex < 0 || verticalIndex < 0) {
+            return relative;
+        }
+        leftStaffEntry = this.getFirstLeftNotNullStaffEntryFromContainer(leftIndex, verticalIndex, multiStaffInstrument);
+        rightStaffEntry = this.getFirstRightNotNullStaffEntryFromContainer(rightIndex, verticalIndex, multiStaffInstrument);
+        if (leftStaffEntry !== undefined && rightStaffEntry !== undefined) {
+            let measureRelativeX: number = leftStaffEntry.parentMeasure.PositionAndShape.RelativePosition.x;
+            if (firstVisibleMeasureRelativeX > 0) {
+                measureRelativeX = firstVisibleMeasureRelativeX;
+            }
+            let leftX: number = leftStaffEntry.PositionAndShape.RelativePosition.x + measureRelativeX;
+            let rightX: number = rightStaffEntry.PositionAndShape.RelativePosition.x + rightStaffEntry.parentMeasure.PositionAndShape.RelativePosition.x;
+            if (firstVisibleMeasureRelativeX > 0) {
+                rightX = rightStaffEntry.PositionAndShape.RelativePosition.x + measureRelativeX;
+            }
+            let timestampQuotient: number = 0.0;
+            if (leftStaffEntry !== rightStaffEntry) {
+                let leftTimestamp: Fraction = leftStaffEntry.getAbsoluteTimestamp();
+                let rightTimestamp: Fraction = rightStaffEntry.getAbsoluteTimestamp();
+                let leftDifference: Fraction = Fraction.minus(timestamp, leftTimestamp);
+                timestampQuotient = leftDifference.RealValue / Fraction.minus(rightTimestamp, leftTimestamp).RealValue;
+            }
+            if (leftStaffEntry.parentMeasure.ParentStaffLine !== rightStaffEntry.parentMeasure.ParentStaffLine) {
+                if (leftStaffEntry.parentMeasure.ParentStaffLine === staffLine) {
+                    rightX = staffLine.PositionAndShape.Size.width;
+                } else {
+                    leftX = staffLine.PositionAndShape.RelativePosition.x;
+                }
+            }
+            relative = new PointF2D(leftX + (rightX - leftX) * timestampQuotient, 0.0);
+        }
+        return relative;
+    }
+
+    protected getRelativeXPositionFromTimestamp(timestamp: Fraction): number {
+        let numEntries: number = this.graphicalMusicSheet.VerticalGraphicalStaffEntryContainers.length;
+        let index: number = this.graphicalMusicSheet.GetInterpolatedIndexInVerticalContainers(timestamp);
+        let discreteIndex: number = <number>Math.max(0, Math.min(Math.round(index), numEntries - 1));
+        let gse: GraphicalStaffEntry = this.graphicalMusicSheet.VerticalGraphicalStaffEntryContainers[discreteIndex].getFirstNonNullStaffEntry();
+        let posX: number = gse.PositionAndShape.RelativePosition.x + gse.parentMeasure.PositionAndShape.RelativePosition.x;
+        return posX;
+    }
+
+    protected calculatePageLabels(page: GraphicalMusicPage): void {
+        let relative: PointF2D = new PointF2D();
+        let firstSystemAbsoluteTopMargin: number = 10;
+        if (page.MusicSystems.length > 0) {
+            let firstMusicSystem: MusicSystem = page.MusicSystems[0];
+            firstSystemAbsoluteTopMargin = firstMusicSystem.PositionAndShape.RelativePosition.y + firstMusicSystem.PositionAndShape.BorderTop;
+        }
+        if (this.graphicalMusicSheet.Title !== undefined) {
+            let title: GraphicalLabel = this.graphicalMusicSheet.Title;
+            title.PositionAndShape.Parent = page.PositionAndShape;
+            page.PositionAndShape.ChildElements.push(title.PositionAndShape);
+            relative.x = this.graphicalMusicSheet.ParentMusicSheet.pageWidth / 2;
+            relative.y = this.rules.TitleTopDistance + this.rules.SheetTitleHeight;
+            title.PositionAndShape.RelativePosition = relative;
+            page.Labels.push(title);
+        }
+        if (this.graphicalMusicSheet.Subtitle !== undefined) {
+            let subtitle: GraphicalLabel = this.graphicalMusicSheet.Subtitle;
+            subtitle.PositionAndShape.Parent = page.PositionAndShape;
+            page.PositionAndShape.ChildElements.push(subtitle.PositionAndShape);
+            relative.x = this.graphicalMusicSheet.ParentMusicSheet.pageWidth / 2;
+            relative.y = this.rules.TitleTopDistance + this.rules.SheetTitleHeight + this.rules.SheetMinimumDistanceBetweenTitleAndSubtitle;
+            subtitle.PositionAndShape.RelativePosition = relative;
+            page.Labels.push(subtitle);
+        }
+        if (this.graphicalMusicSheet.Composer !== undefined) {
+            let composer: GraphicalLabel = this.graphicalMusicSheet.Composer;
+            composer.PositionAndShape.Parent = page.PositionAndShape;
+            page.PositionAndShape.ChildElements.push(composer.PositionAndShape);
+            composer.setLabelPositionAndShapeBorders();
+            relative.x = this.graphicalMusicSheet.ParentMusicSheet.pageWidth - this.rules.PageRightMargin;
+            relative.y = firstSystemAbsoluteTopMargin - this.rules.SystemComposerDistance;
+            composer.PositionAndShape.RelativePosition = relative;
+            page.Labels.push(composer);
+        }
+        if (this.graphicalMusicSheet.Lyricist !== undefined) {
+            let lyricist: GraphicalLabel = this.graphicalMusicSheet.Lyricist;
+            lyricist.PositionAndShape.Parent = page.PositionAndShape;
+            page.PositionAndShape.ChildElements.push(lyricist.PositionAndShape);
+            lyricist.setLabelPositionAndShapeBorders();
+            relative.x = this.rules.PageLeftMargin;
+            relative.y = firstSystemAbsoluteTopMargin - this.rules.SystemComposerDistance;
+            lyricist.PositionAndShape.RelativePosition = relative;
+            page.Labels.push(lyricist);
+        }
+    }
+
+    protected createGraphicalTies(): void {
+        for (let measureIndex: number = 0; measureIndex < this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures.length; measureIndex++) {
+            let sourceMeasure: SourceMeasure = this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures[measureIndex];
+            for (let staffIndex: number = 0; staffIndex < sourceMeasure.CompleteNumberOfStaves; staffIndex++) {
+                for (let j: number = 0; j < sourceMeasure.VerticalSourceStaffEntryContainers.length; j++) {
+                    let sourceStaffEntry: SourceStaffEntry = sourceMeasure.VerticalSourceStaffEntryContainers[j].StaffEntries[staffIndex];
+                    if (sourceStaffEntry !== undefined) {
+                        let startStaffEntry: GraphicalStaffEntry = this.graphicalMusicSheet.findGraphicalStaffEntryFromMeasureList(
+                            staffIndex, measureIndex, sourceStaffEntry
+                        );
+                        for (let idx: number = 0, len: number = sourceStaffEntry.VoiceEntries.length; idx < len; ++idx) {
+                            let voiceEntry: VoiceEntry = sourceStaffEntry.VoiceEntries[idx];
+                            for (let idx2: number = 0, len2: number = voiceEntry.Notes.length; idx2 < len2; ++idx2) {
+                                let note: Note = voiceEntry.Notes[idx2];
+                                if (note.NoteTie !== undefined) {
+                                    let tie: Tie = note.NoteTie;
+                                    this.handleTie(tie, startStaffEntry, staffIndex, measureIndex);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private createAccidentalCalculators(): AccidentalCalculator[] {
+        let accidentalCalculators: AccidentalCalculator[] = [];
+        let firstSourceMeasure: SourceMeasure = this.graphicalMusicSheet.ParentMusicSheet.getFirstSourceMeasure();
+        if (firstSourceMeasure !== undefined) {
+            for (let i: number = 0; i < firstSourceMeasure.CompleteNumberOfStaves; i++) {
+                let accidentalCalculator: AccidentalCalculator = new AccidentalCalculator(this.symbolFactory);
+                accidentalCalculators.push(accidentalCalculator);
+                if (firstSourceMeasure.FirstInstructionsStaffEntries[i] !== undefined) {
+                    for (let idx: number = 0, len: number = firstSourceMeasure.FirstInstructionsStaffEntries[i].Instructions.length; idx < len; ++idx) {
+                        let abstractNotationInstruction: AbstractNotationInstruction = firstSourceMeasure.FirstInstructionsStaffEntries[i].Instructions[idx];
+                        if (abstractNotationInstruction instanceof KeyInstruction) {
+                            let keyInstruction: KeyInstruction = <KeyInstruction>abstractNotationInstruction;
+                            accidentalCalculator.ActiveKeyInstruction = keyInstruction;
+                        }
+                    }
+                }
+            }
+        }
+        return accidentalCalculators;
+    }
+
+    private calculateVerticalContainersList(): void {
+        let numberOfEntries: number = this.graphicalMusicSheet.MeasureList[0].length;
+        for (let i: number = 0; i < this.graphicalMusicSheet.MeasureList.length; i++) {
+            for (let j: number = 0; j < numberOfEntries; j++) {
+                let measure: StaffMeasure = this.graphicalMusicSheet.MeasureList[i][j];
+                for (let idx: number = 0, len: number = measure.staffEntries.length; idx < len; ++idx) {
+                    let graphicalStaffEntry: GraphicalStaffEntry = measure.staffEntries[idx];
+                    let verticalContainer: VerticalGraphicalStaffEntryContainer =
+                        this.graphicalMusicSheet.getOrCreateVerticalContainer(graphicalStaffEntry.getAbsoluteTimestamp());
+                    if (verticalContainer !== undefined) {
+                        verticalContainer.StaffEntries[j] = graphicalStaffEntry;
+                        graphicalStaffEntry.parentVerticalContainer = verticalContainer;
+                    } else {
+                        ;
+                    }
+                }
+            }
+        }
+    }
+
+    private setIndecesToVerticalGraphicalContainers(): void {
+        for (let i: number = 0; i < this.graphicalMusicSheet.VerticalGraphicalStaffEntryContainers.length; i++) {
+            this.graphicalMusicSheet.VerticalGraphicalStaffEntryContainers[i].Index = i;
+        }
+    }
+
+    private createGraphicalMeasuresForSourceMeasure(sourceMeasure: SourceMeasure, accidentalCalculators: AccidentalCalculator[],
+                                                    openLyricWords: LyricWord[],
+                                                    tieTimestampListDictList: Dictionary<Tie, Fraction[]>[],
+                                                    openOctaveShifts: OctaveShiftParams[], activeClefs: ClefInstruction[]): StaffMeasure[] {
+        this.initStaffMeasuresCreation();
+        let verticalMeasureList: StaffMeasure[] = [];
+        let openBeams: Beam[] = [];
+        let openTuplets: Tuplet[] = [];
+        let staffEntryLinks: StaffEntryLink[] = [];
+        for (let staffIndex: number = 0; staffIndex < sourceMeasure.CompleteNumberOfStaves; staffIndex++) {
+            let measure: StaffMeasure = this.createGraphicalMeasure(
+                sourceMeasure, tieTimestampListDictList[staffIndex], openTuplets, openBeams,
+                accidentalCalculators[staffIndex], activeClefs, openOctaveShifts, openLyricWords, staffIndex, staffEntryLinks
+            );
+            verticalMeasureList.push(measure);
+        }
+        this.graphicalMusicSheet.sourceToGraphicalMeasureLinks.setValue(sourceMeasure, verticalMeasureList);
+        return verticalMeasureList;
+    }
+
+    private createGraphicalMeasure(sourceMeasure: SourceMeasure, tieTimestampListDict: Dictionary<Tie, Fraction[]>, openTuplets: Tuplet[], openBeams: Beam[],
+                                   accidentalCalculator: AccidentalCalculator, activeClefs: ClefInstruction[],
+                                   openOctaveShifts: OctaveShiftParams[], openLyricWords: LyricWord[], staffIndex: number,
+                                   staffEntryLinks: StaffEntryLink[]): StaffMeasure {
+        let staff: Staff = this.graphicalMusicSheet.ParentMusicSheet.getStaffFromIndex(staffIndex);
+        let measure: StaffMeasure = this.symbolFactory.createStaffMeasure(sourceMeasure, staff);
+        measure.hasError = sourceMeasure.getErrorInMeasure(staffIndex);
+        if (sourceMeasure.FirstInstructionsStaffEntries[staffIndex] !== undefined) {
+            for (let idx: number = 0, len: number = sourceMeasure.FirstInstructionsStaffEntries[staffIndex].Instructions.length; idx < len; ++idx) {
+                let instruction: AbstractNotationInstruction = sourceMeasure.FirstInstructionsStaffEntries[staffIndex].Instructions[idx];
+                if (instruction instanceof KeyInstruction) {
+                    let key: KeyInstruction = KeyInstruction.copy(instruction);
+                    if (this.graphicalMusicSheet.ParentMusicSheet.Transpose !== 0 &&
+                        measure.ParentStaff.ParentInstrument.MidiInstrumentId !== MidiInstrument.Percussion &&
+                        MusicSheetCalculator.transposeCalculator !== undefined) {
+                        MusicSheetCalculator.transposeCalculator.transposeKey(
+                            key, this.graphicalMusicSheet.ParentMusicSheet.Transpose
+                        );
+                    }
+                    accidentalCalculator.ActiveKeyInstruction = key;
+                }
+            }
+        }
+        for (let idx: number = 0, len: number = sourceMeasure.StaffLinkedExpressions[staffIndex].length; idx < len; ++idx) {
+            let multiExpression: MultiExpression = sourceMeasure.StaffLinkedExpressions[staffIndex][idx];
+            if (multiExpression.OctaveShiftStart !== undefined) {
+                let openOctaveShift: OctaveShift = multiExpression.OctaveShiftStart;
+                openOctaveShifts[staffIndex] = new OctaveShiftParams(
+                    openOctaveShift, multiExpression.AbsoluteTimestamp,
+                    openOctaveShift.ParentEndMultiExpression.AbsoluteTimestamp
+                );
+            }
+        }
+        for (let entryIndex: number = 0; entryIndex < sourceMeasure.VerticalSourceStaffEntryContainers.length; entryIndex++) {
+            let sourceStaffEntry: SourceStaffEntry = sourceMeasure.VerticalSourceStaffEntryContainers[entryIndex].StaffEntries[staffIndex];
+            if (sourceStaffEntry !== undefined) {
+                for (let idx: number = 0, len: number = sourceStaffEntry.Instructions.length; idx < len; ++idx) {
+                    let abstractNotationInstruction: AbstractNotationInstruction = sourceStaffEntry.Instructions[idx];
+                    if (abstractNotationInstruction instanceof ClefInstruction) {
+                        activeClefs[staffIndex] = <ClefInstruction>abstractNotationInstruction;
+                    }
+                }
+                let graphicalStaffEntry: GraphicalStaffEntry = this.symbolFactory.createStaffEntry(sourceStaffEntry, measure);
+                if (measure.staffEntries.length > entryIndex) {
+                    measure.addGraphicalStaffEntryAtTimestamp(graphicalStaffEntry);
+                } else {
+                    measure.addGraphicalStaffEntry(graphicalStaffEntry);
+                }
+                let linkedNotes: Note[] = [];
+                if (sourceStaffEntry.Link !== undefined) {
+                    sourceStaffEntry.findLinkedNotes(linkedNotes);
+                    this.handleStaffEntryLink(graphicalStaffEntry, staffEntryLinks);
+                }
+                let octaveShiftValue: OctaveEnum = OctaveEnum.NONE;
+                if (openOctaveShifts[staffIndex] !== undefined) {
+                    let octaveShiftParams: OctaveShiftParams = openOctaveShifts[staffIndex];
+                    if (sourceStaffEntry.AbsoluteTimestamp >= octaveShiftParams.getAbsoluteStartTimestamp &&
+                        sourceStaffEntry.AbsoluteTimestamp <= octaveShiftParams.getAbsoluteEndTimestamp) {
+                        octaveShiftValue = octaveShiftParams.getOpenOctaveShift.Type;
+                    }
+                }
+                for (let idx: number = 0, len: number = sourceStaffEntry.VoiceEntries.length; idx < len; ++idx) {
+                    let voiceEntry: VoiceEntry = sourceStaffEntry.VoiceEntries[idx];
+                    this.handleVoiceEntryGraceNotes(
+                        voiceEntry.graceVoiceEntriesBefore, graphicalStaffEntry.graceStaffEntriesBefore, graphicalStaffEntry,
+                        accidentalCalculator, activeClefs[staffIndex], octaveShiftValue, openLyricWords,
+                        tieTimestampListDict, openTuplets, openBeams
+                    );
+                    octaveShiftValue = this.handleVoiceEntry(
+                        voiceEntry, graphicalStaffEntry,
+                        accidentalCalculator, openLyricWords,
+                        tieTimestampListDict,
+                        activeClefs[staffIndex], openTuplets,
+                        openBeams, octaveShiftValue, false, linkedNotes,
+                        sourceStaffEntry
+                    );
+                    this.handleVoiceEntryGraceNotes(
+                        voiceEntry.graceVoiceEntriesAfter, graphicalStaffEntry.graceStaffEntriesAfter, graphicalStaffEntry,
+                        accidentalCalculator, activeClefs[staffIndex], octaveShiftValue, openLyricWords,
+                        tieTimestampListDict, openTuplets, openBeams
+                    );
+                }
+                if (sourceStaffEntry.Instructions.length > 0) {
+                    let clefInstruction: ClefInstruction = <ClefInstruction>sourceStaffEntry.Instructions[0];
+                    this.symbolFactory.createInStaffClef(graphicalStaffEntry, clefInstruction);
+                }
+                if (sourceStaffEntry.ChordContainer !== undefined) {
+                    sourceStaffEntry.ParentStaff.ParentInstrument.HasChordSymbols = true;
+                    this.symbolFactory.createChordSymbol(sourceStaffEntry, graphicalStaffEntry, this.graphicalMusicSheet.ParentMusicSheet.Transpose);
+                }
+            }
+        }
+        if (tieTimestampListDict.size() > 0) {
+            this.handleOpenTies(
+                measure, openBeams,
+                tieTimestampListDict, activeClefs[staffIndex], openOctaveShifts[staffIndex]
+            );
+        }
+        accidentalCalculator.doCalculationsAtEndOfMeasure();
+        if (sourceMeasure.LastInstructionsStaffEntries[staffIndex] !== undefined) {
+            let lastStaffEntry: SourceStaffEntry = sourceMeasure.LastInstructionsStaffEntries[staffIndex];
+            for (let idx: number = 0, len: number = lastStaffEntry.Instructions.length; idx < len; ++idx) {
+                let abstractNotationInstruction: AbstractNotationInstruction = lastStaffEntry.Instructions[idx];
+                if (abstractNotationInstruction instanceof ClefInstruction) {
+                    activeClefs[staffIndex] = <ClefInstruction>abstractNotationInstruction;
+                }
+            }
+        }
+        for (let idx: number = 0, len: number = sourceMeasure.StaffLinkedExpressions[staffIndex].length; idx < len; ++idx) {
+            let multiExpression: MultiExpression = sourceMeasure.StaffLinkedExpressions[staffIndex][idx];
+            if (multiExpression.OctaveShiftEnd !== undefined && openOctaveShifts[staffIndex] !== undefined &&
+                multiExpression.OctaveShiftEnd === openOctaveShifts[staffIndex].getOpenOctaveShift) {
+                openOctaveShifts[staffIndex] = undefined;
+            }
+        }
+        if (measure.staffEntries.length === 0) {
+            let sourceStaffEntry: SourceStaffEntry = new SourceStaffEntry(undefined, staff);
+            let note: Note = new Note(undefined, sourceStaffEntry, Fraction.createFromFraction(sourceMeasure.Duration), undefined);
+            let graphicalStaffEntry: GraphicalStaffEntry = this.symbolFactory.createStaffEntry(sourceStaffEntry, measure);
+            measure.addGraphicalStaffEntry(graphicalStaffEntry);
+            graphicalStaffEntry.relInMeasureTimestamp = new Fraction(0, 1);
+            let graphicalNotes: GraphicalNote[] = [];
+            graphicalStaffEntry.notes.push(graphicalNotes);
+            let numberOfDots: number = note.calculateNumberOfNeededDots();
+            let graphicalNote: GraphicalNote = this.symbolFactory.createNote(   note,
+                                                                                numberOfDots,
+                                                                                graphicalStaffEntry,
+                                                                                new ClefInstruction(ClefEnum.G, 0, 2),
+                                                                                OctaveEnum.NONE);
+            graphicalNotes.push(graphicalNote);
+            graphicalStaffEntry.PositionAndShape.ChildElements.push(graphicalNote.PositionAndShape);
+        }
+        return measure;
+    }
+
+    private checkVoiceEntriesForTechnicalInstructions(voiceEntry: VoiceEntry, graphicalStaffEntry: GraphicalStaffEntry): void {
+        for (let idx: number = 0, len: number = voiceEntry.TechnicalInstructions.length; idx < len; ++idx) {
+            let technicalInstruction: TechnicalInstruction = voiceEntry.TechnicalInstructions[idx];
+            this.symbolFactory.createGraphicalTechnicalInstruction(technicalInstruction, graphicalStaffEntry);
+        }
+    }
+    private checkNoteForAccidental(graphicalNote: GraphicalNote, accidentalCalculator: AccidentalCalculator, activeClef: ClefInstruction,
+                                   octaveEnum: OctaveEnum, grace: boolean = false): void {
+        let pitch: Pitch = graphicalNote.sourceNote.Pitch;
+        let transpose: number = this.graphicalMusicSheet.ParentMusicSheet.Transpose;
+        if (transpose !== 0 && graphicalNote.sourceNote.ParentStaffEntry.ParentStaff.ParentInstrument.MidiInstrumentId !== MidiInstrument.Percussion) {
+            pitch = graphicalNote.Transpose(
+                accidentalCalculator.ActiveKeyInstruction, activeClef, transpose, octaveEnum
+            );
+            if (graphicalNote.sourceNote.NoteTie !== undefined) {
+                graphicalNote.sourceNote.NoteTie.BaseNoteYPosition = graphicalNote.PositionAndShape.RelativePosition.y;
+            }
+        }
+        graphicalNote.sourceNote.halfTone = pitch.getHalfTone();
+        let scalingFactor: number = 1.0;
+        if (grace) {
+            scalingFactor = this.rules.GraceNoteScalingFactor;
+        }
+        accidentalCalculator.checkAccidental(graphicalNote, pitch, grace, scalingFactor);
+    }
+
+    // needed to disable linter, as it doesn't recognize the existing usage of this method.
+    // ToDo: check if a newer version doesn't have the problem.
+    /* tslint:disable:no-unused-variable */
+    private createStaffEntryForTieNote(measure: StaffMeasure, absoluteTimestamp: Fraction, openTie: Tie): GraphicalStaffEntry {
+        /* tslint:enable:no-unused-variable */
+        let graphicalStaffEntry: GraphicalStaffEntry;
+        graphicalStaffEntry = this.symbolFactory.createStaffEntry(openTie.Start.ParentStaffEntry, measure);
+        graphicalStaffEntry.relInMeasureTimestamp = Fraction.minus(absoluteTimestamp, measure.parentSourceMeasure.AbsoluteTimestamp);
+        this.resetYPositionForLeadSheet(graphicalStaffEntry.PositionAndShape);
+        measure.addGraphicalStaffEntry(graphicalStaffEntry);
+        return graphicalStaffEntry;
+    }
+
+    private updateSkyBottomLines(): void {
+        for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
+            let graphicalMusicPage: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
+            for (let idx2: number = 0, len2: number = graphicalMusicPage.MusicSystems.length; idx2 < len2; ++idx2) {
+                let musicSystem: MusicSystem = graphicalMusicPage.MusicSystems[idx2];
+                for (let idx3: number = 0, len3: number = musicSystem.StaffLines.length; idx3 < len3; ++idx3) {
+                    let staffLine: StaffLine = musicSystem.StaffLines[idx3];
+                    this.updateSkyBottomLine(staffLine);
+                }
+            }
+        }
+    }
+
+    private handleStaffEntries(): void {
+        for (let idx: number = 0, len: number = this.graphicalMusicSheet.MeasureList.length; idx < len; ++idx) {
+            let measures: StaffMeasure[] = this.graphicalMusicSheet.MeasureList[idx];
+            for (let idx2: number = 0, len2: number = measures.length; idx2 < len2; ++idx2) {
+                let measure: StaffMeasure = measures[idx2];
+                for (let idx3: number = 0, len3: number = measure.staffEntries.length; idx3 < len3; ++idx3) {
+                    let graphicalStaffEntry: GraphicalStaffEntry = measure.staffEntries[idx3];
+                    if (graphicalStaffEntry.parentMeasure !== undefined && graphicalStaffEntry.notes.length > 0 && graphicalStaffEntry.notes[0].length > 0) {
+                        this.layoutVoiceEntries(graphicalStaffEntry);
+                        this.layoutStaffEntry(graphicalStaffEntry);
+                    }
+                }
+            }
+        }
+    }
+
+    private calculateSkyBottomLines(): void {
+        for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
+            let graphicalMusicPage: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
+            for (let idx2: number = 0, len2: number = graphicalMusicPage.MusicSystems.length; idx2 < len2; ++idx2) {
+                let musicSystem: MusicSystem = graphicalMusicPage.MusicSystems[idx2];
+                for (let idx3: number = 0, len3: number = musicSystem.StaffLines.length; idx3 < len3; ++idx3) {
+                    let staffLine: StaffLine = musicSystem.StaffLines[idx3];
+                    this.calculateSkyBottomLine(staffLine);
+                }
+            }
+        }
+    }
+
+    private calculateBeams(): void {
+        for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
+            let musicPage: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
+            for (let idx2: number = 0, len2: number = musicPage.MusicSystems.length; idx2 < len2; ++idx2) {
+                let musicSystem: MusicSystem = musicPage.MusicSystems[idx2];
+                for (let idx3: number = 0, len3: number = musicSystem.StaffLines.length; idx3 < len3; ++idx3) {
+                    let staffLine: StaffLine = musicSystem.StaffLines[idx3];
+                    for (let idx4: number = 0, len4: number = staffLine.Measures.length; idx4 < len4; ++idx4) {
+                        let measure: StaffMeasure = staffLine.Measures[idx4];
+                        for (let idx5: number = 0, len5: number = measure.staffEntries.length; idx5 < len5; ++idx5) {
+                            let staffEntry: GraphicalStaffEntry = measure.staffEntries[idx5];
+                            this.layoutBeams(staffEntry);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private calculateStaffEntryArticulationMarks(): void {
+        for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
+            let page: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
+            for (let idx2: number = 0, len2: number = page.MusicSystems.length; idx2 < len2; ++idx2) {
+                let system: MusicSystem = page.MusicSystems[idx2];
+                for (let idx3: number = 0, len3: number = system.StaffLines.length; idx3 < len3; ++idx3) {
+                    let line: StaffLine = system.StaffLines[idx3];
+                    for (let idx4: number = 0, len4: number = line.Measures.length; idx4 < len4; ++idx4) {
+                        let measure: StaffMeasure = line.Measures[idx4];
+                        for (let idx5: number = 0, len5: number = measure.staffEntries.length; idx5 < len5; ++idx5) {
+                            let graphicalStaffEntry: GraphicalStaffEntry = measure.staffEntries[idx5];
+                            for (let idx6: number = 0, len6: number = graphicalStaffEntry.sourceStaffEntry.VoiceEntries.length; idx6 < len6; ++idx6) {
+                                let voiceEntry: VoiceEntry = graphicalStaffEntry.sourceStaffEntry.VoiceEntries[idx6];
+                                if (voiceEntry.Articulations.length > 0) {
+                                    this.layoutArticulationMarks(voiceEntry.Articulations, voiceEntry, graphicalStaffEntry);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private calculateOrnaments(): void {
+        for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
+            let page: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
+            for (let idx2: number = 0, len2: number = page.MusicSystems.length; idx2 < len2; ++idx2) {
+                let system: MusicSystem = page.MusicSystems[idx2];
+                for (let idx3: number = 0, len3: number = system.StaffLines.length; idx3 < len3; ++idx3) {
+                    let line: StaffLine = system.StaffLines[idx3];
+                    for (let idx4: number = 0, len4: number = line.Measures.length; idx4 < len4; ++idx4) {
+                        let measure: StaffMeasure = line.Measures[idx4];
+                        for (let idx5: number = 0, len5: number = measure.staffEntries.length; idx5 < len5; ++idx5) {
+                            let graphicalStaffEntry: GraphicalStaffEntry = measure.staffEntries[idx5];
+                            for (let idx6: number = 0, len6: number = graphicalStaffEntry.sourceStaffEntry.VoiceEntries.length; idx6 < len6; ++idx6) {
+                                let voiceEntry: VoiceEntry = graphicalStaffEntry.sourceStaffEntry.VoiceEntries[idx6];
+                                if (voiceEntry.OrnamentContainer !== undefined) {
+                                    if (voiceEntry.hasTie() && graphicalStaffEntry.relInMeasureTimestamp !== voiceEntry.Timestamp) {
+                                        continue;
+                                    }
+                                    this.layoutOrnament(voiceEntry.OrnamentContainer, voiceEntry, graphicalStaffEntry);
+                                    if (!(this.staffEntriesWithOrnaments.indexOf(graphicalStaffEntry) !== -1)) {
+                                        this.staffEntriesWithOrnaments.push(graphicalStaffEntry);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private optimizeRestPlacement(): void {
+        for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
+            let page: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
+            for (let idx2: number = 0, len2: number = page.MusicSystems.length; idx2 < len2; ++idx2) {
+                let system: MusicSystem = page.MusicSystems[idx2];
+                for (let idx3: number = 0, len3: number = system.StaffLines.length; idx3 < len3; ++idx3) {
+                    let line: StaffLine = system.StaffLines[idx3];
+                    for (let idx4: number = 0, len4: number = line.Measures.length; idx4 < len4; ++idx4) {
+                        let measure: StaffMeasure = line.Measures[idx4];
+                        for (let idx5: number = 0, len5: number = measure.staffEntries.length; idx5 < len5; ++idx5) {
+                            let graphicalStaffEntry: GraphicalStaffEntry = measure.staffEntries[idx5];
+                            this.optimizeRestNotePlacement(graphicalStaffEntry, measure);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private calculateTwoRestNotesPlacementWithCollisionDetection(graphicalStaffEntry: GraphicalStaffEntry): void {
+        let firstRestNote: GraphicalNote = graphicalStaffEntry.notes[0][0];
+        let secondRestNote: GraphicalNote = graphicalStaffEntry.notes[1][0];
+        secondRestNote.PositionAndShape.RelativePosition = new PointF2D(0.0, 2.5);
+        graphicalStaffEntry.PositionAndShape.calculateAbsolutePositionsRecursiveWithoutTopelement();
+        firstRestNote.PositionAndShape.computeNonOverlappingPositionWithMargin(
+            graphicalStaffEntry.PositionAndShape, ColDirEnum.Up,
+            new PointF2D(0.0, secondRestNote.PositionAndShape.RelativePosition.y)
+        );
+        let relative: PointF2D = firstRestNote.PositionAndShape.RelativePosition;
+        relative.y -= 1.0;
+        firstRestNote.PositionAndShape.RelativePosition = relative;
+        graphicalStaffEntry.PositionAndShape.calculateBoundingBox();
+    }
+
+    private calculateRestNotePlacementWithCollisionDetectionFromGraphicalNote(graphicalStaffEntry: GraphicalStaffEntry): void {
+        let restNote: GraphicalNote;
+        let graphicalNotes: GraphicalNote[];
+        if (graphicalStaffEntry.notes[0][0].sourceNote.Pitch === undefined) {
+            restNote = graphicalStaffEntry.notes[0][0];
+            graphicalNotes = graphicalStaffEntry.notes[1];
+        } else {
+            graphicalNotes = graphicalStaffEntry.notes[0];
+            restNote = graphicalStaffEntry.notes[1][0];
+        }
+        let collision: boolean = false;
+        graphicalStaffEntry.PositionAndShape.calculateAbsolutePositionsRecursiveWithoutTopelement();
+        for (let idx: number = 0, len: number = graphicalNotes.length; idx < len; ++idx) {
+            let graphicalNote: GraphicalNote = graphicalNotes[idx];
+            if (restNote.PositionAndShape.marginCollisionDetection(graphicalNote.PositionAndShape)) {
+                collision = true;
+                break;
+            }
+        }
+        if (collision) {
+            if (restNote.sourceNote.ParentVoiceEntry.ParentVoice instanceof LinkedVoice) {
+                let bottomBorder: number = graphicalNotes[0].PositionAndShape.BorderMarginBottom + graphicalNotes[0].PositionAndShape.RelativePosition.y;
+                restNote.PositionAndShape.RelativePosition = new PointF2D(0.0, bottomBorder - restNote.PositionAndShape.BorderMarginTop + 0.5);
+            } else {
+                let last: GraphicalNote = graphicalNotes[graphicalNotes.length - 1];
+                let topBorder: number = last.PositionAndShape.BorderMarginTop + last.PositionAndShape.RelativePosition.y;
+                if (graphicalNotes[0].sourceNote.ParentVoiceEntry.ParentVoice instanceof LinkedVoice) {
+                    restNote.PositionAndShape.RelativePosition = new PointF2D(0.0, topBorder - restNote.PositionAndShape.BorderMarginBottom - 0.5);
+                } else {
+                    let bottomBorder: number = graphicalNotes[0].PositionAndShape.BorderMarginBottom + graphicalNotes[0].PositionAndShape.RelativePosition.y;
+                    if (bottomBorder < 2.0) {
+                        restNote.PositionAndShape.RelativePosition = new PointF2D(0.0, bottomBorder - restNote.PositionAndShape.BorderMarginTop + 0.5);
+                    } else {
+                        restNote.PositionAndShape.RelativePosition = new PointF2D(0.0, topBorder - restNote.PositionAndShape.BorderMarginBottom - 0.0);
+                    }
+                }
+            }
+        }
+        graphicalStaffEntry.PositionAndShape.calculateBoundingBox();
+    }
+
+    private calculateTieCurves(): void {
+        for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
+            let graphicalMusicPage: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
+            for (let idx2: number = 0, len2: number = graphicalMusicPage.MusicSystems.length; idx2 < len2; ++idx2) {
+                let musicSystem: MusicSystem = graphicalMusicPage.MusicSystems[idx2];
+                for (let idx3: number = 0, len3: number = musicSystem.StaffLines.length; idx3 < len3; ++idx3) {
+                    let staffLine: StaffLine = musicSystem.StaffLines[idx3];
+                    for (let idx4: number = 0, len5: number = staffLine.Measures.length; idx4 < len5; ++idx4) {
+                        let measure: StaffMeasure = staffLine.Measures[idx4];
+                        for (let idx6: number = 0, len6: number = measure.staffEntries.length; idx6 < len6; ++idx6) {
+                            let staffEntry: GraphicalStaffEntry = measure.staffEntries[idx6];
+                            let graphicalTies: GraphicalTie[] = staffEntry.GraphicalTies;
+                            for (let idx7: number = 0, len7: number = graphicalTies.length; idx7 < len7; ++idx7) {
+                                let graphicalTie: GraphicalTie = graphicalTies[idx7];
+                                if (graphicalTie.StartNote !== undefined && graphicalTie.StartNote.parentStaffEntry === staffEntry) {
+                                    let tieIsAtSystemBreak: boolean = (
+                                        graphicalTie.StartNote.parentStaffEntry.parentMeasure.ParentStaffLine !==
+                                        graphicalTie.EndNote.parentStaffEntry.parentMeasure.ParentStaffLine
+                                    );
+                                    this.layoutGraphicalTie(graphicalTie, tieIsAtSystemBreak);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    // Commented because unused:
+    //private calculateFingering(): void {
+    //    for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
+    //        let graphicalMusicPage: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
+    //        for (let idx2: number = 0, len2: number = graphicalMusicPage.MusicSystems.length; idx2 < len2; ++idx2) {
+    //            let musicSystem: MusicSystem = graphicalMusicPage.MusicSystems[idx2];
+    //            for (let idx3: number = 0, len3: number = musicSystem.StaffLines.length; idx3 < len3; ++idx3) {
+    //                let staffLine: StaffLine = musicSystem.StaffLines[idx3];
+    //                let skyBottomLineCalculator: SkyBottomLineCalculator = new SkyBottomLineCalculator(this.rules);
+    //                for (let idx4: number = 0, len4: number = staffLine.Measures.length; idx4 < len4; ++idx4) {
+    //                    let measure: StaffMeasure = staffLine.Measures[idx4];
+    //                    let measureRelativePosition: PointF2D = measure.PositionAndShape.RelativePosition;
+    //                    for (let idx5: number = 0, len5: number = measure.staffEntries.length; idx5 < len5; ++idx5) {
+    //                        let staffEntry: GraphicalStaffEntry = measure.staffEntries[idx5];
+    //                        let hasTechnicalInstruction: boolean = false;
+    //                        for (let idx6: number = 0, len6: number = staffEntry.sourceStaffEntry.VoiceEntries.length; idx6 < len6; ++idx6) {
+    //                            let ve: VoiceEntry = staffEntry.sourceStaffEntry.VoiceEntries[idx6];
+    //                            if (ve.TechnicalInstructions.length > 0) {
+    //                                hasTechnicalInstruction = true;
+    //                                break;
+    //                            }
+    //                        }
+    //                        if (hasTechnicalInstruction) {
+    //                            this.layoutFingering(staffLine, skyBottomLineCalculator, staffEntry, measureRelativePosition);
+    //                        }
+    //                    }
+    //                }
+    //            }
+    //        }
+    //    }
+    //}
+
+    private calculateLyricsPosition(): void {
+        for (let idx: number = 0, len: number = this.graphicalMusicSheet.ParentMusicSheet.Instruments.length; idx < len; ++idx) {
+            let instrument: Instrument = this.graphicalMusicSheet.ParentMusicSheet.Instruments[idx];
+            if (instrument.HasLyrics && instrument.LyricVersesNumbers.length > 0) {
+                instrument.LyricVersesNumbers.sort();
+            }
+        }
+        for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
+            let graphicalMusicPage: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
+            for (let idx2: number = 0, len2: number = graphicalMusicPage.MusicSystems.length; idx2 < len2; ++idx2) {
+                let musicSystem: MusicSystem = graphicalMusicPage.MusicSystems[idx2];
+                for (let idx3: number = 0, len3: number = musicSystem.StaffLines.length; idx3 < len3; ++idx3) {
+                    let staffLine: StaffLine = musicSystem.StaffLines[idx3];
+                    this.calculateSingleStaffLineLyricsPosition(staffLine, staffLine.ParentStaff.ParentInstrument.LyricVersesNumbers);
+                }
+            }
+        }
+    }
+
+    private calculateDynamicExpressions(): void {
+        for (let i: number = 0; i < this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures.length; i++) {
+            let sourceMeasure: SourceMeasure = this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures[i];
+            for (let j: number = 0; j < sourceMeasure.StaffLinkedExpressions.length; j++) {
+                if (this.graphicalMusicSheet.MeasureList[i][j].ParentStaff.ParentInstrument.Visible) {
+                    for (let k: number = 0; k < sourceMeasure.StaffLinkedExpressions[j].length; k++) {
+                        if (sourceMeasure.StaffLinkedExpressions[j][k].InstantaniousDynamic !== undefined ||
+                            (sourceMeasure.StaffLinkedExpressions[j][k].StartingContinuousDynamic !== undefined &&
+                            sourceMeasure.StaffLinkedExpressions[j][k].StartingContinuousDynamic.StartMultiExpression ===
+                            sourceMeasure.StaffLinkedExpressions[j][k] && sourceMeasure.StaffLinkedExpressions[j][k].UnknownList.length === 0)
+                        ) {
+                            this.calculateDynamicExpressionsForSingleMultiExpression(sourceMeasure.StaffLinkedExpressions[j][k], i, j);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private calculateOctaveShifts(): void {
+        for (let i: number = 0; i < this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures.length; i++) {
+            let sourceMeasure: SourceMeasure = this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures[i];
+            for (let j: number = 0; j < sourceMeasure.StaffLinkedExpressions.length; j++) {
+                if (this.graphicalMusicSheet.MeasureList[i][j].ParentStaff.ParentInstrument.Visible) {
+                    for (let k: number = 0; k < sourceMeasure.StaffLinkedExpressions[j].length; k++) {
+                        if ((sourceMeasure.StaffLinkedExpressions[j][k].OctaveShiftStart !== undefined)) {
+                            this.calculateSingleOctaveShift(sourceMeasure, sourceMeasure.StaffLinkedExpressions[j][k], i, j);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private getFirstLeftNotNullStaffEntryFromContainer(horizontalIndex: number, verticalIndex: number, multiStaffInstrument: boolean): GraphicalStaffEntry {
+        if (this.graphicalMusicSheet.VerticalGraphicalStaffEntryContainers[horizontalIndex].StaffEntries[verticalIndex] !== undefined) {
+            return this.graphicalMusicSheet.VerticalGraphicalStaffEntryContainers[horizontalIndex].StaffEntries[verticalIndex];
+        }
+        for (let i: number = horizontalIndex - 1; i >= 0; i--) {
+            if (this.graphicalMusicSheet.VerticalGraphicalStaffEntryContainers[i].StaffEntries[verticalIndex] !== undefined) {
+                return this.graphicalMusicSheet.VerticalGraphicalStaffEntryContainers[i].StaffEntries[verticalIndex];
+            }
+        }
+        return undefined;
+    }
+
+    private getFirstRightNotNullStaffEntryFromContainer(horizontalIndex: number, verticalIndex: number, multiStaffInstrument: boolean): GraphicalStaffEntry {
+        if (this.graphicalMusicSheet.VerticalGraphicalStaffEntryContainers[horizontalIndex].StaffEntries[verticalIndex] !== undefined) {
+            return this.graphicalMusicSheet.VerticalGraphicalStaffEntryContainers[horizontalIndex].StaffEntries[verticalIndex];
+        }
+        for (let i: number = horizontalIndex + 1; i < this.graphicalMusicSheet.VerticalGraphicalStaffEntryContainers.length; i++) {
+            if (this.graphicalMusicSheet.VerticalGraphicalStaffEntryContainers[i].StaffEntries[verticalIndex] !== undefined) {
+                return this.graphicalMusicSheet.VerticalGraphicalStaffEntryContainers[i].StaffEntries[verticalIndex];
+            }
+        }
+        return undefined;
+    }
+
+    private calculateWordRepetitionInstructions(): void {
+        for (let i: number = 0; i < this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures.length; i++) {
+            let sourceMeasure: SourceMeasure = this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures[i];
+            for (let idx: number = 0, len: number = sourceMeasure.FirstRepetitionInstructions.length; idx < len; ++idx) {
+                let instruction: RepetitionInstruction = sourceMeasure.FirstRepetitionInstructions[idx];
+                this.calculateWordRepetitionInstruction(instruction, i);
+            }
+            for (let idx: number = 0, len: number = sourceMeasure.LastRepetitionInstructions.length; idx < len; ++idx) {
+                let instruction: RepetitionInstruction = sourceMeasure.LastRepetitionInstructions[idx];
+                this.calculateWordRepetitionInstruction(instruction, i);
+            }
+        }
+    }
+
+    private calculateRepetitionEndings(): void {
+        let musicsheet: MusicSheet = this.graphicalMusicSheet.ParentMusicSheet;
+        for (let idx: number = 0, len: number = musicsheet.Repetitions.length; idx < len; ++idx) {
+            let partListEntry: Repetition = musicsheet.Repetitions[idx];
+            this.calcGraphicalRepetitionEndingsRecursively(partListEntry);
+        }
+    }
+
+    private calculateTempoExpressions(): void {
+        for (let i: number = 0; i < this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures.length; i++) {
+            let sourceMeasure: SourceMeasure = this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures[i];
+            for (let j: number = 0; j < sourceMeasure.TempoExpressions.length; j++) {
+                this.calculateTempoExpressionsForSingleMultiTempoExpression(sourceMeasure, sourceMeasure.TempoExpressions[j], i);
+            }
+        }
+    }
+
+    private calculateMoodAndUnknownExpressions(): void {
+        for (let i: number = 0; i < this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures.length; i++) {
+            let sourceMeasure: SourceMeasure = this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures[i];
+            for (let j: number = 0; j < sourceMeasure.StaffLinkedExpressions.length; j++) {
+                if (this.graphicalMusicSheet.MeasureList[i][j].ParentStaff.ParentInstrument.Visible) {
+                    for (let k: number = 0; k < sourceMeasure.StaffLinkedExpressions[j].length; k++) {
+                        if ((sourceMeasure.StaffLinkedExpressions[j][k].MoodList.length > 0) ||
+                            (sourceMeasure.StaffLinkedExpressions[j][k].UnknownList.length > 0)) {
+                            this.calculateMoodAndUnknownExpression(sourceMeasure.StaffLinkedExpressions[j][k], i, j);
+                        }
+                    }
+                }
+            }
+        }
+    }
+}

+ 499 - 0
src/MusicalScore/Graphical/MusicSheetDrawer.ts

@@ -0,0 +1,499 @@
+import {EngravingRules} from "./EngravingRules";
+import {ITextMeasurer} from "../Interfaces/ITextMeasurer";
+import {GraphicalMusicSheet} from "./GraphicalMusicSheet";
+import {BoundingBox} from "./BoundingBox";
+import {GraphicalLayers, OutlineAndFillStyleEnum} from "./DrawingEnums";
+import {DrawingParameters} from "./DrawingParameters";
+import {GraphicalLine} from "./GraphicalLine";
+import {RectangleF2D} from "../../Common/DataObjects/RectangleF2D";
+import {PointF2D} from "../../Common/DataObjects/PointF2D";
+import {GraphicalRectangle} from "./GraphicalRectangle";
+import {GraphicalLabel} from "./GraphicalLabel";
+import {Label} from "../Label";
+import {TextAlignment} from "../../Common/Enums/TextAlignment";
+import {ArgumentOutOfRangeException} from "../Exceptions";
+import {SelectionStartSymbol} from "./SelectionStartSymbol";
+import {SelectionEndSymbol} from "./SelectionEndSymbol";
+import {MusicSystem} from "./MusicSystem";
+import {StaffMeasure} from "./StaffMeasure";
+import {StaffLine} from "./StaffLine";
+import {SystemLine} from "./SystemLine";
+import {MusicSymbol} from "./MusicSymbol";
+import {GraphicalMusicPage} from "./GraphicalMusicPage";
+import {Instrument} from "../Instrument";
+import {MusicSymbolDrawingStyle, PhonicScoreModes} from "./DrawingMode";
+import {GraphicalOctaveShift} from "./GraphicalOctaveShift";
+import {GraphicalObject} from "./GraphicalObject";
+
+export abstract class MusicSheetDrawer {
+    public drawingParameters: DrawingParameters = new DrawingParameters();
+    public splitScreenLineColor: number;
+    public midiPlaybackAvailable: boolean;
+
+    protected rules: EngravingRules;
+    protected graphicalMusicSheet: GraphicalMusicSheet;
+    protected textMeasurer: ITextMeasurer;
+    private phonicScoreMode: PhonicScoreModes = PhonicScoreModes.Manual;
+
+    constructor(textMeasurer: ITextMeasurer,
+                isPreviewImageDrawer: boolean = false) {
+        this.textMeasurer = textMeasurer;
+        this.splitScreenLineColor = -1;
+        if (isPreviewImageDrawer) {
+            this.drawingParameters.setForThumbmail();
+        } else {
+            this.drawingParameters.setForAllOn();
+        }
+    }
+
+    public set Mode(value: PhonicScoreModes) {
+        this.phonicScoreMode = value;
+    }
+
+    public drawSheet(graphicalMusicSheet: GraphicalMusicSheet): void {
+        this.graphicalMusicSheet = graphicalMusicSheet;
+        this.rules = graphicalMusicSheet.ParentMusicSheet.Rules;
+        this.drawSplitScreenLine();
+        if (this.drawingParameters.drawCursors) {
+            for (let line of graphicalMusicSheet.Cursors) {
+                let psi: BoundingBox = new BoundingBox(line);
+                psi.AbsolutePosition = line.Start;
+                psi.BorderBottom = line.End.y - line.Start.y;
+                psi.BorderRight = line.Width / 2.0;
+                psi.BorderLeft = -line.Width / 2.0;
+                if (this.isVisible(psi)) {
+                    this.drawLineAsVerticalRectangle(line, <number>GraphicalLayers.Cursor);
+                }
+            }
+        }
+        if (this.drawingParameters.drawScrollIndicator) {
+            this.drawScrollIndicator();
+        }
+        for (let page of this.graphicalMusicSheet.MusicPages) {
+            this.drawPage(page);
+        }
+    }
+
+    public drawLineAsHorizontalRectangle(line: GraphicalLine, layer: number): void {
+        let rectangle: RectangleF2D = new RectangleF2D(line.Start.x, line.End.y - line.Width / 2, line.End.x - line.Start.x, line.Width);
+        rectangle = this.applyScreenTransformationForRect(rectangle);
+        this.renderRectangle(rectangle, layer, line.styleId);
+    }
+
+    public drawLineAsVerticalRectangle(line: GraphicalLine, layer: number): void {
+        let lineStart: PointF2D = line.Start;
+        let lineWidth: number = line.Width;
+        let rectangle: RectangleF2D = new RectangleF2D(lineStart.x - lineWidth / 2, lineStart.y, lineWidth, line.End.y - lineStart.y);
+        rectangle = this.applyScreenTransformationForRect(rectangle);
+        this.renderRectangle(rectangle, layer, line.styleId);
+    }
+
+    public drawLineAsHorizontalRectangleWithOffset(line: GraphicalLine, offset: PointF2D, layer: number): void {
+        let start: PointF2D = new PointF2D(line.Start.x + offset.x, line.Start.y + offset.y);
+        let end: PointF2D = new PointF2D(line.End.x + offset.x, line.End.y + offset.y);
+        let width: number = line.Width;
+        let rectangle: RectangleF2D = new RectangleF2D(start.x, end.y - width / 2, end.x - start.x, width);
+        rectangle = this.applyScreenTransformationForRect(rectangle);
+        this.renderRectangle(rectangle, layer, line.styleId);
+    }
+
+    public drawLineAsVerticalRectangleWithOffset(line: GraphicalLine, offset: PointF2D, layer: number): void {
+        let start: PointF2D = new PointF2D(line.Start.x + offset.x, line.Start.y + offset.y);
+        let end: PointF2D = new PointF2D(line.End.x + offset.x, line.End.y + offset.y);
+        let width: number = line.Width;
+        let rectangle: RectangleF2D = new RectangleF2D(start.x, start.y, width, end.y - start.y);
+        rectangle = this.applyScreenTransformationForRect(rectangle);
+        this.renderRectangle(rectangle, layer, line.styleId);
+    }
+
+    public drawRectangle(rect: GraphicalRectangle, layer: number): void {
+        let psi: BoundingBox = rect.PositionAndShape;
+        let rectangle: RectangleF2D = new RectangleF2D(psi.AbsolutePosition.x, psi.AbsolutePosition.y, psi.BorderRight, psi.BorderBottom);
+        rectangle = this.applyScreenTransformationForRect(rectangle);
+        this.renderRectangle(rectangle, layer, <number>rect.style);
+    }
+
+    public calculatePixelDistance(unitDistance: number): number {
+        throw new Error("not implemented");
+    }
+
+    public drawLabel(graphicalLabel: GraphicalLabel, layer: number): void {
+        if (!this.isVisible(graphicalLabel.PositionAndShape)) {
+            return;
+        }
+        let label: Label = graphicalLabel.Label;
+        if (label.text.trim() === "") {
+            return;
+        }
+        let screenPosition: PointF2D = this.applyScreenTransformation(graphicalLabel.PositionAndShape.AbsolutePosition);
+        let heightInPixel: number = this.calculatePixelDistance(label.fontHeight);
+        let widthInPixel: number = heightInPixel * this.textMeasurer.computeTextWidthToHeightRatio(label.text, label.font, label.fontStyle);
+        let bitmapWidth: number = <number>Math.ceil(widthInPixel);
+        let bitmapHeight: number = <number>Math.ceil(heightInPixel * 1.2);
+        switch (label.textAlignment) {
+            case TextAlignment.LeftTop:
+                break;
+            case TextAlignment.LeftCenter:
+                screenPosition.y -= <number>bitmapHeight / 2;
+                break;
+            case TextAlignment.LeftBottom:
+                screenPosition.y -= bitmapHeight;
+                break;
+            case TextAlignment.CenterTop:
+                screenPosition.x -= <number>bitmapWidth / 2;
+                break;
+            case TextAlignment.CenterCenter:
+                screenPosition.x -= <number>bitmapWidth / 2;
+                screenPosition.y -= <number>bitmapHeight / 2;
+                break;
+            case TextAlignment.CenterBottom:
+                screenPosition.x -= <number>bitmapWidth / 2;
+                screenPosition.y -= bitmapHeight;
+                break;
+            case TextAlignment.RightTop:
+                screenPosition.x -= bitmapWidth;
+                break;
+            case TextAlignment.RightCenter:
+                screenPosition.x -= bitmapWidth;
+                screenPosition.y -= <number>bitmapHeight / 2;
+                break;
+            case TextAlignment.RightBottom:
+                screenPosition.x -= bitmapWidth;
+                screenPosition.y -= bitmapHeight;
+                break;
+            default:
+                throw new ArgumentOutOfRangeException("");
+        }
+        this.renderLabel(graphicalLabel, layer, bitmapWidth, bitmapHeight, heightInPixel, screenPosition);
+    }
+
+    protected applyScreenTransformation(point: PointF2D): PointF2D {
+        throw new Error("not implemented");
+    }
+
+    protected applyScreenTransformations(points: PointF2D[]): PointF2D[] {
+        let transformedPoints: PointF2D[] = [];
+        for (let point of points) {
+            transformedPoints.push(this.applyScreenTransformation(point));
+        }
+        return transformedPoints;
+    }
+
+    protected applyScreenTransformationForRect(rectangle: RectangleF2D): RectangleF2D {
+        throw new Error("not implemented");
+    }
+
+    protected drawSplitScreenLine(): void {
+        // empty
+    }
+
+    protected renderRectangle(rectangle: RectangleF2D, layer: number, styleId: number): void {
+        throw new Error("not implemented");
+    }
+
+    protected drawScrollIndicator(): void {
+        // empty
+    }
+
+    protected drawSelectionStartSymbol(symbol: SelectionStartSymbol): void {
+        // empty
+    }
+
+    protected drawSelectionEndSymbol(symbol: SelectionEndSymbol): void {
+        // empty
+    }
+
+    protected renderLabel(graphicalLabel: GraphicalLabel, layer: number, bitmapWidth: number,
+                          bitmapHeight: number, heightInPixel: number, screenPosition: PointF2D): void {
+        throw new Error("not implemented");
+    }
+
+    protected renderSystemToScreen(system: MusicSystem, systemBoundingBoxInPixels: RectangleF2D,
+                                   absBoundingRectWithMargin: RectangleF2D): void {
+        // empty
+    }
+
+    protected drawMeasure(measure: StaffMeasure): void {
+        throw new Error("not implemented");
+    }
+
+    protected drawSkyLine(staffLine: StaffLine): void {
+        // empty
+    }
+
+    protected drawBottomLine(staffLine: StaffLine): void {
+        // empty
+    }
+
+    protected drawInstrumentBracket(bracket: GraphicalObject, system: MusicSystem): void {
+        // empty
+    }
+
+    protected drawGroupBracket(bracket: GraphicalObject, system: MusicSystem): void {
+        // empty
+    }
+
+    protected isVisible(psi: BoundingBox): boolean {
+        return true;
+    }
+
+    protected drawMusicSystem(system: MusicSystem): void {
+        let absBoundingRectWithMargin: RectangleF2D = this.getSystemAbsBoundingRect(system);
+        let systemBoundingBoxInPixels: RectangleF2D = this.getSytemBoundingBoxInPixels(absBoundingRectWithMargin);
+        this.drawMusicSystemComponents(system, systemBoundingBoxInPixels, absBoundingRectWithMargin);
+    }
+
+    protected getSytemBoundingBoxInPixels(absBoundingRectWithMargin: RectangleF2D): RectangleF2D {
+        let systemBoundingBoxInPixels: RectangleF2D = this.applyScreenTransformationForRect(absBoundingRectWithMargin);
+        systemBoundingBoxInPixels.x = Math.round(systemBoundingBoxInPixels.x);
+        systemBoundingBoxInPixels.y = Math.round(systemBoundingBoxInPixels.y);
+        return systemBoundingBoxInPixels;
+    }
+
+    protected getSystemAbsBoundingRect(system: MusicSystem): RectangleF2D {
+        let relBoundingRect: RectangleF2D = system.PositionAndShape.BoundingRectangle;
+        let absBoundingRectWithMargin: RectangleF2D = new RectangleF2D(
+            system.PositionAndShape.AbsolutePosition.x + system.PositionAndShape.BorderLeft - 1,
+            system.PositionAndShape.AbsolutePosition.y + system.PositionAndShape.BorderTop - 1,
+            (relBoundingRect.width + 6), (relBoundingRect.height + 2)
+        );
+        return absBoundingRectWithMargin;
+    }
+
+    protected drawMusicSystemComponents(musicSystem: MusicSystem, systemBoundingBoxInPixels: RectangleF2D,
+                                        absBoundingRectWithMargin: RectangleF2D): void {
+        let selectStartSymb: SelectionStartSymbol = this.graphicalMusicSheet.SelectionStartSymbol;
+        let selectEndSymb: SelectionEndSymbol = this.graphicalMusicSheet.SelectionEndSymbol;
+        if (this.drawingParameters.drawSelectionStartSymbol) {
+            if (selectStartSymb !== undefined && this.isVisible(selectStartSymb.PositionAndShape)) {
+                this.drawSelectionStartSymbol(selectStartSymb);
+            }
+        }
+        if (this.drawingParameters.drawSelectionEndSymbol) {
+            if (selectEndSymb !== undefined && this.isVisible(selectEndSymb.PositionAndShape)) {
+                this.drawSelectionEndSymbol(selectEndSymb);
+            }
+        }
+        for (let staffLine of musicSystem.StaffLines) {
+            this.drawStaffLine(staffLine);
+        }
+        for (let systemLine of musicSystem.SystemLines) {
+            this.drawSystemLineObject(systemLine);
+        }
+        if (musicSystem === musicSystem.Parent.MusicSystems[0] && musicSystem.Parent === musicSystem.Parent.Parent.MusicPages[0]) {
+            for (let label of musicSystem.Labels) {
+                this.drawLabel(label, <number>GraphicalLayers.Notes);
+            }
+        }
+        for (let bracket of musicSystem.InstrumentBrackets) {
+            this.drawInstrumentBracket(bracket, musicSystem);
+        }
+        for (let bracket of musicSystem.GroupBrackets) {
+            this.drawGroupBracket(bracket, musicSystem);
+        }
+        if (!this.leadSheet) {
+            for (let measureNumberLabel of musicSystem.MeasureNumberLabels) {
+                this.drawLabel(measureNumberLabel, <number>GraphicalLayers.Notes);
+            }
+        }
+        for (let staffLine of musicSystem.StaffLines) {
+            this.drawStaffLineSymbols(staffLine);
+        }
+        if (this.drawingParameters.drawMarkedAreas) {
+            this.drawMarkedAreas(musicSystem);
+        }
+        if (this.drawingParameters.drawComments) {
+            this.drawComment(musicSystem);
+        }
+    }
+
+    protected activateSystemRendering(systemId: number, absBoundingRect: RectangleF2D,
+                                      systemBoundingBoxInPixels: RectangleF2D, createNewImage: boolean): boolean {
+        return true;
+    }
+
+    protected drawSystemLineObject(systemLine: SystemLine): void {
+        // empty
+    }
+
+    protected drawStaffLine(staffLine: StaffLine): void {
+        for (let measure of staffLine.Measures) {
+            this.drawMeasure(measure);
+        }
+    }
+
+    // protected drawSlur(slur: GraphicalSlur, abs: PointF2D): void {
+    //
+    // }
+
+    protected drawOctaveShift(staffLine: StaffLine, graphicalOctaveShift: GraphicalOctaveShift): void {
+        this.drawSymbol(graphicalOctaveShift.octaveSymbol, MusicSymbolDrawingStyle.Normal, graphicalOctaveShift.PositionAndShape.AbsolutePosition);
+        let absolutePos: PointF2D = staffLine.PositionAndShape.AbsolutePosition;
+        if (graphicalOctaveShift.dashesStart.x < graphicalOctaveShift.dashesEnd.x) {
+            let horizontalLine: GraphicalLine = new GraphicalLine(graphicalOctaveShift.dashesStart, graphicalOctaveShift.dashesEnd,
+                                                                  this.rules.OctaveShiftLineWidth);
+            this.drawLineAsHorizontalRectangleWithOffset(horizontalLine, absolutePos, <number>GraphicalLayers.Notes);
+        }
+        if (!graphicalOctaveShift.endsOnDifferentStaffLine || graphicalOctaveShift.isSecondPart) {
+            let verticalLine: GraphicalLine;
+            let dashEnd: PointF2D = graphicalOctaveShift.dashesEnd;
+            let octShiftVertLineLength: number = this.rules.OctaveShiftVerticalLineLength;
+            let octShiftLineWidth: number = this.rules.OctaveShiftLineWidth;
+            if (graphicalOctaveShift.octaveSymbol === MusicSymbol.VA8 || graphicalOctaveShift.octaveSymbol === MusicSymbol.MA15) {
+                verticalLine = new GraphicalLine(dashEnd, new PointF2D(dashEnd.x, dashEnd.y + octShiftVertLineLength), octShiftLineWidth);
+            } else {
+                verticalLine = new GraphicalLine(new PointF2D(dashEnd.x, dashEnd.y - octShiftVertLineLength), dashEnd, octShiftLineWidth);
+            }
+            this.drawLineAsVerticalRectangleWithOffset(verticalLine, absolutePos, <number>GraphicalLayers.Notes);
+        }
+    }
+
+    protected drawStaffLines(staffLine: StaffLine): void {
+        if (staffLine.StaffLines !== undefined) {
+            let position: PointF2D = staffLine.PositionAndShape.AbsolutePosition;
+            for (let i: number = 0; i < 5; i++) {
+                this.drawLineAsHorizontalRectangleWithOffset(staffLine.StaffLines[i], position, <number>GraphicalLayers.Notes);
+            }
+        }
+    }
+
+    // protected drawEnding(ending: GraphicalRepetitionEnding, absolutePosition: PointF2D): void {
+    //     if (undefined !== ending.Left)
+    //         drawLineAsVerticalRectangle(ending.Left, absolutePosition, <number>GraphicalLayers.Notes);
+    //     this.drawLineAsHorizontalRectangle(ending.Top, absolutePosition, <number>GraphicalLayers.Notes);
+    //     if (undefined !== ending.Right)
+    //         drawLineAsVerticalRectangle(ending.Right, absolutePosition, <number>GraphicalLayers.Notes);
+    //     this.drawLabel(ending.Label, <number>GraphicalLayers.Notes);
+    // }
+    // protected drawInstantaniousDynamic(expression: GraphicalInstantaniousDynamicExpression): void {
+    //     expression.ExpressionSymbols.forEach(function (expressionSymbol) {
+    //         let position: PointF2D = expressionSymbol.PositionAndShape.AbsolutePosition;
+    //         let symbol: MusicSymbol = expressionSymbol.GetSymbol;
+    //         drawSymbol(symbol, MusicSymbolDrawingStyle.Normal, position);
+    //     });
+    // }
+    // protected drawContinuousDynamic(expression: GraphicalContinuousDynamicExpression,
+    //     absolute: PointF2D): void {
+    //     throw new Error("not implemented");
+    // }
+    protected drawSymbol(symbol: MusicSymbol, symbolStyle: MusicSymbolDrawingStyle, position: PointF2D,
+                         scalingFactor: number = 1, layer: number = <number>GraphicalLayers.Notes): void {
+        //empty
+    }
+
+    protected get leadSheet(): boolean {
+        return this.graphicalMusicSheet.LeadSheet;
+    }
+
+    protected set leadSheet(value: boolean) {
+        this.graphicalMusicSheet.LeadSheet = value;
+    }
+
+    private drawPage(page: GraphicalMusicPage): void {
+        if (!this.isVisible(page.PositionAndShape)) {
+            return;
+        }
+        for (let system of page.MusicSystems) {
+            if (this.isVisible(system.PositionAndShape)) {
+                this.drawMusicSystem(system);
+            }
+        }
+        if (page === page.Parent.MusicPages[0]) {
+            for (let label of page.Labels) {
+                this.drawLabel(label, <number>GraphicalLayers.Notes);
+            }
+        }
+    }
+
+    private drawMarkedAreas(system: MusicSystem): void {
+        for (let markedArea of system.GraphicalMarkedAreas) {
+            if (markedArea !== undefined) {
+                if (markedArea.systemRectangle !== undefined) {
+                    this.drawRectangle(markedArea.systemRectangle, <number>GraphicalLayers.Background);
+                }
+                if (markedArea.settings !== undefined) {
+                    this.drawLabel(markedArea.settings, <number>GraphicalLayers.Comment);
+                }
+                if (markedArea.labelRectangle !== undefined) {
+                    this.drawRectangle(markedArea.labelRectangle, <number>GraphicalLayers.Background);
+                }
+                if (markedArea.label !== undefined) {
+                    this.drawLabel(markedArea.label, <number>GraphicalLayers.Comment);
+                }
+            }
+        }
+    }
+
+    private drawComment(system: MusicSystem): void {
+        for (let comment of system.GraphicalComments) {
+            if (comment !== undefined) {
+                if (comment.settings !== undefined) {
+                    this.drawLabel(comment.settings, <number>GraphicalLayers.Comment);
+                }
+                if (comment.label !== undefined) {
+                    this.drawLabel(comment.label, <number>GraphicalLayers.Comment);
+                }
+            }
+        }
+    }
+
+    private drawStaffLineSymbols(staffLine: StaffLine): void {
+        let parentInst: Instrument = staffLine.ParentStaff.ParentInstrument;
+        let absX: number = staffLine.PositionAndShape.AbsolutePosition.x;
+        let absY: number = staffLine.PositionAndShape.AbsolutePosition.y + 2;
+        let borderRight: number = staffLine.PositionAndShape.BorderRight;
+        if (parentInst.highlight && this.drawingParameters.drawHighlights) {
+            this.drawLineAsHorizontalRectangle(
+                new GraphicalLine(
+                    new PointF2D(absX, absY),
+                    new PointF2D(absX + borderRight, absY),
+                    4,
+                    OutlineAndFillStyleEnum.Highlighted
+                ),
+                <number>GraphicalLayers.Highlight
+            );
+        }
+        let style: MusicSymbolDrawingStyle = MusicSymbolDrawingStyle.Disabled;
+        let symbol: MusicSymbol = MusicSymbol.PLAY;
+        let drawSymbols: boolean = this.drawingParameters.drawActivitySymbols;
+        switch (this.phonicScoreMode) {
+            case PhonicScoreModes.Midi:
+                symbol = MusicSymbol.PLAY;
+                if (this.midiPlaybackAvailable && staffLine.ParentStaff.audible) {
+                    style = MusicSymbolDrawingStyle.PlaybackSymbols;
+                }
+                break;
+            case PhonicScoreModes.Following:
+                symbol = MusicSymbol.MIC;
+                if (staffLine.ParentStaff.following) {
+                    style = MusicSymbolDrawingStyle.FollowSymbols;
+                }
+                break;
+            default:
+                drawSymbols = false;
+                break;
+        }
+        if (drawSymbols) {
+            let p: PointF2D = new PointF2D(absX + borderRight + 2, absY);
+            this.drawSymbol(symbol, style, p);
+        }
+        if (this.drawingParameters.drawErrors) {
+            for (let measure of staffLine.Measures) {
+                let measurePSI: BoundingBox = measure.PositionAndShape;
+                let absXPSI: number = measurePSI.AbsolutePosition.x;
+                let absYPSI: number = measurePSI.AbsolutePosition.y + 2;
+                if (measure.hasError && this.graphicalMusicSheet.ParentMusicSheet.DrawErroneousMeasures) {
+                    this.drawLineAsHorizontalRectangle(
+                        new GraphicalLine(
+                            new PointF2D(absXPSI, absYPSI),
+                            new PointF2D(absXPSI + measurePSI.BorderRight, absYPSI),
+                            4,
+                            OutlineAndFillStyleEnum.ErrorUnderlay
+                        ),
+                        <number>GraphicalLayers.MeasureError
+                    );
+                }
+            }
+        }
+    }
+}

+ 87 - 0
src/MusicalScore/Graphical/MusicSymbol.ts

@@ -0,0 +1,87 @@
+export enum MusicSymbol {
+    Unused_first_Symbol,
+    BLACK_HEAD,
+    UPWARDS_TAIL,
+    DOWNWARDS_TAIL,
+    UPWARDS_DOUBLE_TAIL,
+    DOWNWARDS_DOUBLE_TAIL,
+    UPWARDS_TRIPLE_TAIL,
+    DOWNWARDS_TRIPLE_TAIL,
+    UPWARDS_QUAD_TAIL,
+    DOWNWARDS_QUAD_TAIL,
+    ROUND_HEAD,
+    WHITE_HEAD,
+    G_CLEF,
+    F_CLEF,
+    C_CLEF,
+    BREVE,
+    BREVE_REST,
+    COMMON_TIME,
+    CUT_TIME,
+    WHOLE_REST,
+    HALF_REST,
+    QUARTER_REST,
+    EIGHTH_REST,
+    SIXTEENTH_REST,
+    THIRTYSECOND_REST,
+    SIXTYFOURTH_REST,
+    FLAT,
+    SHARP,
+    NATURAL,
+    DOUBLE_FLAT,
+    DOUBLE_SHARP,
+    ZERO,
+    ONE,
+    TWO,
+    THREE,
+    FOUR,
+    FIVE,
+    SIX,
+    SEVEN,
+    EIGHT,
+    NINE,
+    DOT,
+    FERMATA,
+    INVERTED_FERMATA,
+    SPICCATO,
+    TENUTO,
+    MARCATO,
+    MARCATISSIMO,
+    INVERTED_MARCATISSIMO,
+    P,
+    F,
+    S,
+    Z,
+    M,
+    R,
+    SEGNO,
+    CODA,
+    DRUM_CLEF,
+    G_CLEF_SUB8,
+    G_CLEF_SUPER8,
+    G_CLEF_SUB15,
+    G_CLEF_SUPER15,
+    F_CLEF_SUB8,
+    F_CLEF_SUPER8,
+    F_CLEF_SUB15,
+    F_CLEF_SUPER15,
+    DOWN_BOW,
+    MORDENT,
+    INVERTED_MORDENT,
+    TURN,
+    INVERTED_TURN,
+    LEFTHAND_PIZZICATO,
+    RELEASE_PED,
+    ENGAGE_PED,
+    VA8,
+    VB8,
+    TRILL,
+    MA15,
+    MB15,
+    HIGH,
+    PLAY,
+    MIC,
+    SNAP_PIZZICATO,
+    NATURAL_HARMONIC,
+    EditPen
+}

+ 432 - 0
src/MusicalScore/Graphical/MusicSystem.ts

@@ -0,0 +1,432 @@
+import {StaffLine} from "./StaffLine";
+import {Instrument} from "../Instrument";
+import {BoundingBox} from "./BoundingBox";
+import {Fraction} from "../../Common/DataObjects/fraction";
+import {SourceMeasure} from "../VoiceData/SourceMeasure";
+import {InstrumentalGroup} from "../InstrumentalGroup";
+import {TextAlignment} from "../../Common/Enums/TextAlignment";
+import {GraphicalMusicPage} from "./GraphicalMusicPage";
+import {GraphicalLabel} from "./GraphicalLabel";
+import {StaffMeasure} from "./StaffMeasure";
+import {GraphicalObject} from "./GraphicalObject";
+import {EngravingRules} from "./EngravingRules";
+import {PointF2D} from "../../Common/DataObjects/PointF2D";
+import {GraphicalStaffEntry} from "./GraphicalStaffEntry";
+import {SystemLinesEnum} from "./SystemLinesEnum";
+import Dictionary from "typescript-collections/dist/lib/Dictionary";
+import {CollectionUtil} from "../../Util/collectionUtil";
+import {GraphicalComment} from "./GraphicalComment";
+import {GraphicalMarkedArea} from "./GraphicalMarkedArea";
+import {SystemLine} from "./SystemLine";
+import {SystemLinePosition} from "./SystemLinePosition";
+import {Staff} from "../VoiceData/Staff";
+
+export abstract class MusicSystem extends GraphicalObject {
+    public needsToBeRedrawn: boolean = true;
+    protected parent: GraphicalMusicPage;
+    protected id: number;
+    protected staffLines: StaffLine[] = [];
+    protected graphicalMeasures: StaffMeasure[][] = [];
+    protected labels: Dictionary<GraphicalLabel, Instrument> = new Dictionary<GraphicalLabel, Instrument>();
+    protected measureNumberLabels: GraphicalLabel[] = [];
+    protected maxLabelLength: number;
+    protected objectsToRedraw: [Object[], Object][] = [];
+    protected instrumentBrackets: GraphicalObject[] = [];
+    protected groupBrackets: GraphicalObject[] = [];
+    protected graphicalMarkedAreas: GraphicalMarkedArea[] = [];
+    protected graphicalComments: GraphicalComment[] = [];
+    protected systemLines: SystemLine[] = [];
+    protected rules: EngravingRules;
+
+    constructor(parent: GraphicalMusicPage, id: number) {
+        super();
+        this.parent = parent;
+        this.id = id;
+        this.boundingBox = new BoundingBox(this, parent.PositionAndShape);
+        this.maxLabelLength = 0.0;
+        this.rules = this.parent.Parent.ParentMusicSheet.Rules;
+    }
+
+    public get Parent(): GraphicalMusicPage {
+        return this.parent;
+    }
+
+    public set Parent(value: GraphicalMusicPage) {
+        this.parent = value;
+    }
+
+    public get StaffLines(): StaffLine[] {
+        return this.staffLines;
+    }
+
+    public get GraphicalMeasures(): StaffMeasure[][] {
+        return this.graphicalMeasures;
+    }
+
+    public get MeasureNumberLabels(): GraphicalLabel[] {
+        return this.measureNumberLabels;
+    }
+
+    public get Labels(): GraphicalLabel[] {
+        return this.labels.keys();
+    }
+
+    public get ObjectsToRedraw(): [Object[], Object][] {
+        return this.objectsToRedraw;
+    }
+
+    public get InstrumentBrackets(): GraphicalObject[] {
+        return this.instrumentBrackets;
+    }
+
+    public get GroupBrackets(): GraphicalObject[] {
+        return this.groupBrackets;
+    }
+
+    public get GraphicalMarkedAreas(): GraphicalMarkedArea[] {
+        return this.graphicalMarkedAreas;
+    }
+
+    public get GraphicalComments(): GraphicalComment[] {
+        return this.graphicalComments;
+    }
+
+    public get SystemLines(): SystemLine[] {
+        return this.systemLines;
+    }
+
+    public get Id(): number {
+        return this.id;
+    }
+
+    /**
+     * This method creates the left vertical Line connecting all staves of the MusicSystem.
+     * @param lineWidth
+     * @param systemLabelsRightMargin
+     */
+    public createSystemLeftLine(lineWidth: number, systemLabelsRightMargin: number): void {
+        let xPosition: number = -lineWidth / 2;
+        if (this === this.parent.MusicSystems[0] && this.parent === this.parent.Parent.MusicPages[0]) {
+            xPosition = this.maxLabelLength + systemLabelsRightMargin - lineWidth / 2;
+        }
+        let top: StaffMeasure = this.staffLines[0].Measures[0];
+        let bottom: StaffMeasure = undefined;
+        if (this.staffLines.length > 1) {
+            bottom = this.staffLines[this.staffLines.length - 1].Measures[0];
+        }
+        let leftSystemLine: SystemLine = this.createSystemLine(xPosition, lineWidth, SystemLinesEnum.SingleThin,
+                                                               SystemLinePosition.MeasureBegin, this, top, bottom);
+        this.SystemLines.push(leftSystemLine);
+        this.boundingBox.ChildElements.push(leftSystemLine.PositionAndShape);
+        leftSystemLine.PositionAndShape.RelativePosition = new PointF2D(xPosition, 0);
+        leftSystemLine.PositionAndShape.BorderLeft = 0;
+        leftSystemLine.PositionAndShape.BorderRight = lineWidth;
+        leftSystemLine.PositionAndShape.BorderTop = 0;
+        leftSystemLine.PositionAndShape.BorderBottom = this.boundingBox.Size.height;
+        this.createLinesForSystemLine(leftSystemLine);
+    }
+
+    /**
+     * This method creates the vertical Lines after the End of all StaffLine's Measures
+     * @param xPosition
+     * @param lineWidth
+     * @param lineType
+     * @param linePosition indicates if the line belongs to start or end of measure
+     * @param measureIndex the measure index within the staffline
+     * @param measure
+     */
+    public createVerticalLineForMeasure(xPosition: number, lineWidth: number, lineType: SystemLinesEnum, linePosition: SystemLinePosition,
+                                        measureIndex: number, measure: StaffMeasure): void {
+        let staffLine: StaffLine = measure.ParentStaffLine;
+        let staffLineRelative: PointF2D = new PointF2D(staffLine.PositionAndShape.RelativePosition.x,
+                                                       staffLine.PositionAndShape.RelativePosition.y);
+        let staves: Staff[] = staffLine.ParentStaff.ParentInstrument.Staves;
+        if (staffLine.ParentStaff === staves[0]) {
+            let bottomMeasure: StaffMeasure = undefined;
+            if (staves.length > 1) {
+                bottomMeasure = this.getBottomStaffLine(staffLine).Measures[measureIndex];
+            }
+            let singleVerticalLineAfterMeasure: SystemLine = this.createSystemLine(xPosition, lineWidth, lineType, linePosition, this, measure, bottomMeasure);
+            let systemXPosition: number = staffLineRelative.x + xPosition;
+            singleVerticalLineAfterMeasure.PositionAndShape.RelativePosition = new PointF2D(systemXPosition, 0);
+            singleVerticalLineAfterMeasure.PositionAndShape.BorderLeft = 0;
+            singleVerticalLineAfterMeasure.PositionAndShape.BorderRight = lineWidth;
+            this.SystemLines.push(singleVerticalLineAfterMeasure);
+            this.boundingBox.ChildElements.push(singleVerticalLineAfterMeasure.PositionAndShape);
+        }
+    }
+
+    public setYPositionsToVerticalLineObjectsAndCreateLines(rules: EngravingRules): void {
+        // empty
+    }
+
+    public calculateBorders(rules: EngravingRules): void {
+        // empty
+    }
+
+    public alignBeginInstructions(): void {
+        // empty
+    }
+
+    public GetLeftBorderAbsoluteXPosition(): number {
+        return this.StaffLines[0].PositionAndShape.AbsolutePosition.x + this.StaffLines[0].Measures[0].beginInstructionsWidth;
+    }
+
+    public GetRightBorderAbsoluteXPosition(): number {
+        return this.StaffLines[0].PositionAndShape.AbsolutePosition.x + this.StaffLines[0].StaffLines[0].End.x;
+    }
+
+    public AddStaffMeasures(graphicalMeasures: StaffMeasure[]): void {
+        for (let idx: number = 0, len: number = graphicalMeasures.length; idx < len; ++idx) {
+            let graphicalMeasure: StaffMeasure = graphicalMeasures[idx];
+            graphicalMeasure.parentMusicSystem = this;
+        }
+        this.graphicalMeasures.push(graphicalMeasures);
+    }
+
+    public GetSystemsFirstTimeStamp(): Fraction {
+        return this.graphicalMeasures[0][0].parentSourceMeasure.AbsoluteTimestamp;
+    }
+
+    public GetSystemsLastTimeStamp(): Fraction {
+        let m: SourceMeasure = this.graphicalMeasures[this.graphicalMeasures.length - 1][0].parentSourceMeasure;
+        return Fraction.plus(m.AbsoluteTimestamp, m.Duration);
+    }
+
+    public createInstrumentBrackets(instruments: Instrument[], staffHeight: number): void {
+        for (let idx: number = 0, len: number = instruments.length; idx < len; ++idx) {
+            let instrument: Instrument = instruments[idx];
+            if (instrument.Staves.length > 1) {
+                let firstStaffLine: StaffLine = undefined, lastStaffLine: StaffLine = undefined;
+                for (let idx2: number = 0, len2: number = this.staffLines.length; idx2 < len2; ++idx2) {
+                    let staffLine: StaffLine = this.staffLines[idx2];
+                    if (staffLine.ParentStaff === instrument.Staves[0]) {
+                        firstStaffLine = staffLine;
+                    }
+                    if (staffLine.ParentStaff === instrument.Staves[instrument.Staves.length - 1]) {
+                        lastStaffLine = staffLine;
+                    }
+                }
+                if (firstStaffLine !== undefined && lastStaffLine !== undefined) {
+                    let rightUpper: PointF2D = new PointF2D(
+                        firstStaffLine.PositionAndShape.RelativePosition.x,
+                        firstStaffLine.PositionAndShape.RelativePosition.y
+                    );
+                    let rightLower: PointF2D = new PointF2D(
+                        lastStaffLine.PositionAndShape.RelativePosition.x,
+                        lastStaffLine.PositionAndShape.RelativePosition.y + staffHeight
+                    );
+                    this.createInstrumentBracket(rightUpper, rightLower);
+                }
+            }
+        }
+    }
+
+    public createGroupBrackets(instrumentGroups: InstrumentalGroup[], staffHeight: number, recursionDepth: number): void {
+        for (let idx: number = 0, len: number = instrumentGroups.length; idx < len; ++idx) {
+            let instrumentGroup: InstrumentalGroup = instrumentGroups[idx];
+            if (instrumentGroup.InstrumentalGroups.length < 1) {
+                continue;
+            }
+            let instrument1: Instrument = this.findFirstVisibleInstrumentInInstrumentalGroup(instrumentGroup);
+            let instrument2: Instrument = this.findLastVisibleInstrumentInInstrumentalGroup(instrumentGroup);
+            if (instrument1 === undefined || instrument2 === undefined) {
+                continue;
+            }
+            let firstStaffLine: StaffLine = undefined, lastStaffLine: StaffLine = undefined;
+            for (let idx2: number = 0, len2: number = this.staffLines.length; idx2 < len2; ++idx2) {
+                let staffLine: StaffLine = this.staffLines[idx2];
+                if (staffLine.ParentStaff === instrument1.Staves[0]) {
+                    firstStaffLine = staffLine;
+                }
+                if (staffLine.ParentStaff === CollectionUtil.last(instrument2.Staves)) {
+                    lastStaffLine = staffLine;
+                }
+            }
+            if (firstStaffLine !== undefined && lastStaffLine !== undefined) {
+                let rightUpper: PointF2D = new PointF2D(
+                    firstStaffLine.PositionAndShape.RelativePosition.x,
+                    firstStaffLine.PositionAndShape.RelativePosition.y
+                );
+                let rightLower: PointF2D = new PointF2D(
+                    lastStaffLine.PositionAndShape.RelativePosition.x,
+                    lastStaffLine.PositionAndShape.RelativePosition.y + staffHeight
+                );
+                this.createGroupBracket(rightUpper, rightLower, staffHeight, recursionDepth);
+            }
+            if (instrumentGroup.InstrumentalGroups.length < 1) {
+                continue;
+            }
+            this.createGroupBrackets(instrumentGroup.InstrumentalGroups, staffHeight, recursionDepth + 1);
+        }
+    }
+
+    public createMusicSystemLabel(instrumentLabelTextHeight: number, systemLabelsRightMargin: number, labelMarginBorderFactor: number): void {
+        if (this.parent === this.parent.Parent.MusicPages[0] && this === this.parent.MusicSystems[0]) {
+            let instruments: Instrument[] = this.parent.Parent.ParentMusicSheet.getVisibleInstruments();
+            for (let idx: number = 0, len: number = instruments.length; idx < len; ++idx) {
+                let instrument: Instrument = instruments[idx];
+                let graphicalLabel: GraphicalLabel = new GraphicalLabel(
+                    instrument.NameLabel, instrumentLabelTextHeight, TextAlignment.LeftCenter, this.boundingBox
+                );
+                graphicalLabel.setLabelPositionAndShapeBorders();
+                this.labels.setValue(graphicalLabel, instrument);
+                this.boundingBox.ChildElements.push(graphicalLabel.PositionAndShape);
+                graphicalLabel.PositionAndShape.RelativePosition = new PointF2D(0.0, 0.0);
+            }
+            this.maxLabelLength = 0.0;
+            let labels: GraphicalLabel[] = this.labels.keys();
+            for (let idx: number = 0, len: number = labels.length; idx < len; ++idx) {
+                let label: GraphicalLabel = labels[idx];
+                if (label.PositionAndShape.Size.width > this.maxLabelLength) {
+                    this.maxLabelLength = label.PositionAndShape.Size.width;
+                }
+            }
+            this.updateMusicSystemStaffLineXPosition(systemLabelsRightMargin);
+        }
+    }
+
+    public setMusicSystemLabelsYPosition(): void {
+        if (this.parent === this.parent.Parent.MusicPages[0] && this === this.parent.MusicSystems[0]) {
+            this.labels.forEach((key: GraphicalLabel, value: Instrument): void => {
+                let ypositionSum: number = 0;
+                let staffCounter: number = 0;
+                for (let i: number = 0; i < this.staffLines.length; i++) {
+                    if (this.staffLines[i].ParentStaff.ParentInstrument === value) {
+                        for (let j: number = i; j < this.staffLines.length; j++) {
+                            let staffLine: StaffLine = this.staffLines[j];
+                            if (staffLine.ParentStaff.ParentInstrument !== value) {
+                                break;
+                            }
+                            ypositionSum += staffLine.PositionAndShape.RelativePosition.y;
+                            staffCounter++;
+                        }
+                        break;
+                    }
+                }
+                if (staffCounter > 0) {
+                    key.PositionAndShape.RelativePosition = new PointF2D(0.0, ypositionSum / staffCounter + 2.0);
+                }
+            });
+        }
+    }
+
+    public checkStaffEntriesForStaffEntryLink(): boolean {
+        let first: boolean = false;
+        let second: boolean = false;
+        for (let i: number = 0; i < this.staffLines.length - 1; i++) {
+            for (let idx: number = 0, len: number = this.staffLines[i].Measures.length; idx < len; ++idx) {
+                let measure: StaffMeasure = this.staffLines[i].Measures[idx];
+                for (let idx2: number = 0, len2: number = measure.staffEntries.length; idx2 < len2; ++idx2) {
+                    let staffEntry: GraphicalStaffEntry = measure.staffEntries[idx2];
+                    if (staffEntry.sourceStaffEntry.Link !== undefined) {
+                        first = true;
+                    }
+                }
+            }
+            for (let idx: number = 0, len: number = this.staffLines[i + 1].Measures.length; idx < len; ++idx) {
+                let measure: StaffMeasure = this.staffLines[i + 1].Measures[idx];
+                for (let idx2: number = 0, len2: number = measure.staffEntries.length; idx2 < len2; ++idx2) {
+                    let staffEntry: GraphicalStaffEntry = measure.staffEntries[idx2];
+                    if (staffEntry.sourceStaffEntry.Link !== undefined) {
+                        second = true;
+                    }
+                }
+            }
+        }
+        if (first && second) {
+            return true;
+        }
+        return false;
+    }
+
+    public getBottomStaffLine(topStaffLine: StaffLine): StaffLine {
+        let staves: Staff[] = topStaffLine.ParentStaff.ParentInstrument.Staves;
+        let last: Staff = staves[staves.length - 1];
+        for (let line of topStaffLine.ParentMusicSystem.staffLines) {
+            if (line.ParentStaff === last) {
+                return line;
+            }
+        }
+        return undefined;
+    }
+
+    /**
+     * Here the system line is generated, which acts as the container of graphical lines and dots that will be finally rendered.
+     * It holds al the logical parameters of the system line.
+     * @param xPosition The x position within the system
+     * @param lineWidth The total x width
+     * @param lineType The line type enum
+     * @param linePosition indicates if the line belongs to start or end of measure
+     * @param musicSystem
+     * @param topMeasure
+     * @param bottomMeasure
+     */
+    protected createSystemLine(xPosition: number, lineWidth: number, lineType: SystemLinesEnum, linePosition: SystemLinePosition,
+                               musicSystem: MusicSystem, topMeasure: StaffMeasure, bottomMeasure: StaffMeasure = undefined): SystemLine {
+        throw new Error("not implemented");
+    }
+
+    /// <summary>
+    /// This method creates all the graphical lines and dots needed to render a system line (e.g. bold-thin-dots..).
+    /// </summary>
+    /// <param name="psSystemLine"></param>
+    protected createLinesForSystemLine(systemLine: SystemLine): void {
+        //Empty
+    }
+
+    protected calcInstrumentsBracketsWidth(): number {
+        throw new Error("not implemented");
+    }
+
+    protected createInstrumentBracket(rightUpper: PointF2D, rightLower: PointF2D): void {
+        throw new Error("not implemented");
+    }
+
+    protected createGroupBracket(rightUpper: PointF2D, rightLower: PointF2D, staffHeight: number, recursionDepth: number): void {
+        throw new Error("not implemented");
+    }
+
+    private findFirstVisibleInstrumentInInstrumentalGroup(instrumentalGroup: InstrumentalGroup): Instrument {
+        for (let idx: number = 0, len: number = instrumentalGroup.InstrumentalGroups.length; idx < len; ++idx) {
+            let groupOrInstrument: InstrumentalGroup = instrumentalGroup.InstrumentalGroups[idx];
+            if (groupOrInstrument instanceof Instrument) {
+                if ((<Instrument>groupOrInstrument).Visible === true) {
+                    return <Instrument>groupOrInstrument;
+                }
+                continue;
+            }
+            return this.findFirstVisibleInstrumentInInstrumentalGroup(groupOrInstrument);
+        }
+        return undefined;
+    }
+
+    private findLastVisibleInstrumentInInstrumentalGroup(instrumentalGroup: InstrumentalGroup): Instrument {
+        let groupOrInstrument: InstrumentalGroup;
+        for (let i: number = instrumentalGroup.InstrumentalGroups.length - 1; i >= 0; i--) {
+            groupOrInstrument = instrumentalGroup.InstrumentalGroups[i];
+            if (groupOrInstrument instanceof Instrument) {
+                if ((<Instrument>groupOrInstrument).Visible === true) {
+                    return <Instrument>groupOrInstrument;
+                }
+                continue;
+            }
+            return this.findLastVisibleInstrumentInInstrumentalGroup(groupOrInstrument);
+        }
+        return undefined;
+    }
+
+    private updateMusicSystemStaffLineXPosition(systemLabelsRightMargin: number): void {
+        for (let idx: number = 0, len: number = this.StaffLines.length; idx < len; ++idx) {
+            let staffLine: StaffLine = this.StaffLines[idx];
+            let relative: PointF2D = staffLine.PositionAndShape.RelativePosition;
+            relative.x = this.maxLabelLength + systemLabelsRightMargin;
+            staffLine.PositionAndShape.RelativePosition = relative;
+            staffLine.PositionAndShape.BorderRight = this.boundingBox.Size.width - this.maxLabelLength - systemLabelsRightMargin;
+            for (let i: number = 0; i < staffLine.StaffLines.length; i++) {
+                let lineEnd: PointF2D = new PointF2D(staffLine.PositionAndShape.Size.width, staffLine.StaffLines[i].End.y);
+                staffLine.StaffLines[i].End = lineEnd;
+            }
+        }
+    }
+}

+ 798 - 0
src/MusicalScore/Graphical/MusicSystemBuilder.ts

@@ -0,0 +1,798 @@
+import {StaffMeasure} from "./StaffMeasure";
+import {GraphicalMusicPage} from "./GraphicalMusicPage";
+import {EngravingRules} from "./EngravingRules";
+import {RhythmInstruction} from "../VoiceData/Instructions/RhythmInstruction";
+import {KeyInstruction} from "../VoiceData/Instructions/KeyInstruction";
+import {ClefInstruction} from "../VoiceData/Instructions/ClefInstruction";
+import {SourceMeasure} from "../VoiceData/SourceMeasure";
+import {MusicSystem} from "./MusicSystem";
+import {BoundingBox} from "./BoundingBox";
+import {Staff} from "../VoiceData/Staff";
+import {Instrument} from "../Instrument";
+import {PointF2D} from "../../Common/DataObjects/PointF2D";
+import {StaffLine} from "./StaffLine";
+import {GraphicalLine} from "./GraphicalLine";
+import {SourceStaffEntry} from "../VoiceData/SourceStaffEntry";
+import {AbstractNotationInstruction} from "../VoiceData/Instructions/AbstractNotationInstruction";
+import {SystemLinesEnum} from "./SystemLinesEnum";
+import {GraphicalMusicSheet} from "./GraphicalMusicSheet";
+import {IGraphicalSymbolFactory} from "../Interfaces/IGraphicalSymbolFactory";
+import {MusicSheetCalculator} from "./MusicSheetCalculator";
+import {MidiInstrument} from "../VoiceData/Instructions/ClefInstruction";
+import {CollectionUtil} from "../../Util/collectionUtil";
+import {SystemLinePosition} from "./SystemLinePosition";
+
+export class MusicSystemBuilder {
+    private measureList: StaffMeasure[][];
+    private graphicalMusicSheet: GraphicalMusicSheet;
+    private currentMusicPage: GraphicalMusicPage;
+    private currentPageHeight: number;
+    private currentSystemParams: SystemBuildParameters;
+    private numberOfVisibleStaffLines: number;
+    private rules: EngravingRules;
+    private measureListIndex: number;
+    private visibleStaffIndices: number[];
+    private activeRhythm: RhythmInstruction[];
+    private activeKeys: KeyInstruction[];
+    private activeClefs: ClefInstruction[];
+    private globalSystemIndex: number = 0;
+    private leadSheet: boolean = false;
+    private symbolFactory: IGraphicalSymbolFactory;
+
+    public initialize(
+        graphicalMusicSheet: GraphicalMusicSheet, measureList: StaffMeasure[][], numberOfStaffLines: number, symbolFactory: IGraphicalSymbolFactory
+    ): void {
+        this.leadSheet = graphicalMusicSheet.LeadSheet;
+        this.graphicalMusicSheet = graphicalMusicSheet;
+        this.rules = this.graphicalMusicSheet.ParentMusicSheet.rules;
+        this.measureList = measureList;
+        this.symbolFactory = symbolFactory;
+        this.currentMusicPage = this.createMusicPage();
+        this.currentPageHeight = 0.0;
+        this.numberOfVisibleStaffLines = numberOfStaffLines;
+        this.activeRhythm = new Array(this.numberOfVisibleStaffLines);
+        this.activeKeys = new Array(this.numberOfVisibleStaffLines);
+        this.activeClefs = new Array(this.numberOfVisibleStaffLines);
+        this.initializeActiveInstructions(this.measureList[0]);
+    }
+
+    public buildMusicSystems(): void {
+        let previousMeasureEndsSystem: boolean = false;
+        let systemMaxWidth: number = this.getFullPageSystemWidth();
+        this.measureListIndex = 0;
+        this.currentSystemParams = new SystemBuildParameters();
+        this.currentSystemParams.currentSystem = this.initMusicSystem();
+        this.layoutSystemStaves();
+        this.currentSystemParams.currentSystem.createMusicSystemLabel(
+            this.rules.InstrumentLabelTextHeight,
+            this.rules.SystemLabelsRightMargin,
+            this.rules.LabelMarginBorderFactor
+        );
+        this.currentPageHeight += this.currentSystemParams.currentSystem.PositionAndShape.RelativePosition.y;
+
+        let numberOfMeasures: number = 0;
+        for (let idx: number = 0, len: number = this.measureList.length; idx < len; ++idx) {
+            if (this.measureList[idx].length > 0) {
+                numberOfMeasures++;
+            }
+        }
+
+        while (this.measureListIndex < numberOfMeasures) {
+            let staffMeasures: StaffMeasure[] = this.measureList[this.measureListIndex];
+            for (let idx: number = 0, len: number = staffMeasures.length; idx < len; ++idx) {
+                staffMeasures[idx].resetLayout();
+            }
+            let sourceMeasure: SourceMeasure = staffMeasures[0].parentSourceMeasure;
+            let sourceMeasureEndsSystem: boolean = sourceMeasure.BreakSystemAfter;
+            let isSystemStartMeasure: boolean = this.currentSystemParams.IsSystemStartMeasure();
+            let isFirstSourceMeasure: boolean = sourceMeasure === this.graphicalMusicSheet.ParentMusicSheet.getFirstSourceMeasure();
+            let currentMeasureBeginInstructionsWidth: number = this.rules.MeasureLeftMargin;
+            let currentMeasureEndInstructionsWidth: number = 0;
+            let measureStartLine: SystemLinesEnum = this.getMeasureStartLine();
+            currentMeasureBeginInstructionsWidth += this.getLineWidth(staffMeasures[0], measureStartLine, isSystemStartMeasure);
+            if (!this.leadSheet) {
+                currentMeasureBeginInstructionsWidth += this.addBeginInstructions(staffMeasures, isSystemStartMeasure, isFirstSourceMeasure);
+                currentMeasureEndInstructionsWidth += this.addEndInstructions(staffMeasures);
+            }
+            let currentMeasureVarWidth: number = 0;
+            for (let i: number = 0; i < this.numberOfVisibleStaffLines; i++) {
+                currentMeasureVarWidth = Math.max(currentMeasureVarWidth, staffMeasures[i].minimumStaffEntriesWidth);
+            }
+            let measureEndLine: SystemLinesEnum = this.getMeasureEndLine();
+            currentMeasureEndInstructionsWidth += this.getLineWidth(staffMeasures[0], measureEndLine, isSystemStartMeasure);
+            let nextMeasureBeginInstructionWidth: number = this.rules.MeasureLeftMargin;
+            if (this.measureListIndex + 1 < this.measureList.length) {
+                let nextStaffMeasures: StaffMeasure[] = this.measureList[this.measureListIndex + 1];
+                let nextSourceMeasure: SourceMeasure = nextStaffMeasures[0].parentSourceMeasure;
+                if (nextSourceMeasure.hasBeginInstructions()) {
+                    nextMeasureBeginInstructionWidth += this.addBeginInstructions(nextStaffMeasures, false, false);
+                }
+            }
+            let totalMeasureWidth: number = currentMeasureBeginInstructionsWidth + currentMeasureEndInstructionsWidth + currentMeasureVarWidth;
+            let measureFitsInSystem: boolean = this.currentSystemParams.currentWidth + totalMeasureWidth + nextMeasureBeginInstructionWidth < systemMaxWidth;
+            if (isSystemStartMeasure || measureFitsInSystem) {
+                this.addMeasureToSystem(
+                    staffMeasures, measureStartLine, measureEndLine, totalMeasureWidth,
+                    currentMeasureBeginInstructionsWidth, currentMeasureVarWidth, currentMeasureEndInstructionsWidth
+                );
+                this.updateActiveClefs(sourceMeasure, staffMeasures);
+                this.measureListIndex++;
+            } else {
+                this.finalizeCurrentAndCreateNewSystem(staffMeasures, previousMeasureEndsSystem);
+            }
+            previousMeasureEndsSystem = sourceMeasureEndsSystem;
+        }
+        this.finalizeCurrentAndCreateNewSystem(this.measureList[this.measureList.length - 1], true);
+    }
+
+    private setMeasureWidth(staffMeasures: StaffMeasure[], width: number, beginInstrWidth: number, endInstrWidth: number): void {
+        for (let idx: number = 0, len: number = staffMeasures.length; idx < len; ++idx) {
+            let measure: StaffMeasure = staffMeasures[idx];
+            measure.setWidth(width);
+            if (beginInstrWidth > 0) {
+                measure.beginInstructionsWidth = beginInstrWidth;
+            }
+            if (endInstrWidth > 0) {
+                measure.endInstructionsWidth = endInstrWidth;
+            }
+        }
+    }
+
+    private finalizeCurrentAndCreateNewSystem(measures: StaffMeasure[], isPartEndingSystem: boolean = false): void {
+        this.adaptRepetitionLineWithIfNeeded();
+        if (!isPartEndingSystem) {
+            this.checkAndCreateExtraInstructionMeasure(measures);
+        }
+        this.stretchMusicSystem(isPartEndingSystem);
+        if (this.currentPageHeight + this.currentSystemParams.currentSystem.PositionAndShape.Size.height + this.rules.SystemDistance <= this.rules.PageHeight) {
+            this.currentPageHeight += this.currentSystemParams.currentSystem.PositionAndShape.Size.height + this.rules.SystemDistance;
+            if (
+                this.currentPageHeight + this.currentSystemParams.currentSystem.PositionAndShape.Size.height
+                + this.rules.SystemDistance >= this.rules.PageHeight
+            ) {
+                this.currentMusicPage = this.createMusicPage();
+                this.currentPageHeight = this.rules.PageTopMargin + this.rules.TitleTopDistance;
+            }
+        } else {
+            this.currentMusicPage = this.createMusicPage();
+            this.currentPageHeight = this.rules.PageTopMargin + this.rules.TitleTopDistance;
+        }
+        this.currentSystemParams = new SystemBuildParameters();
+        if (this.measureListIndex < this.measureList.length) {
+            this.currentSystemParams.currentSystem = this.initMusicSystem();
+            this.layoutSystemStaves();
+        }
+    }
+
+    private adaptRepetitionLineWithIfNeeded(): void {
+        let systemMeasures: MeasureBuildParameters[] = this.currentSystemParams.systemMeasures;
+        if (systemMeasures.length >= 1) {
+            let measures: StaffMeasure[] =
+                this.currentSystemParams.currentSystem.GraphicalMeasures[this.currentSystemParams.currentSystem.GraphicalMeasures.length - 1];
+            let measureParams: MeasureBuildParameters = systemMeasures[systemMeasures.length - 1];
+            let diff: number = 0.0;
+            if (measureParams.endLine === SystemLinesEnum.DotsBoldBoldDots) {
+                measureParams.endLine = SystemLinesEnum.DotsThinBold;
+                diff = measures[0].getLineWidth(SystemLinesEnum.DotsBoldBoldDots) / 2 - measures[0].getLineWidth(SystemLinesEnum.DotsThinBold);
+            }
+            this.currentSystemParams.currentSystemFixWidth -= diff;
+            for (let idx: number = 0, len: number = measures.length; idx < len; ++idx) {
+                let measure: StaffMeasure = measures[idx];
+                measure.endInstructionsWidth -= diff;
+            }
+        }
+    }
+
+    private addMeasureToSystem(
+        staffMeasures: StaffMeasure[], measureStartLine: SystemLinesEnum, measureEndLine: SystemLinesEnum,
+        totalMeasureWidth: number, currentMeasureBeginInstructionsWidth: number, currentVarWidth: number, currentMeasureEndInstructionsWidth: number
+    ): void {
+        this.currentSystemParams.systemMeasures.push({beginLine: measureStartLine, endLine: measureEndLine});
+        this.setMeasureWidth(
+            staffMeasures, totalMeasureWidth, currentMeasureBeginInstructionsWidth, currentMeasureEndInstructionsWidth
+        );
+        this.addStaveMeasuresToSystem(staffMeasures);
+        this.currentSystemParams.currentWidth += totalMeasureWidth;
+        this.currentSystemParams.currentSystemFixWidth += currentMeasureBeginInstructionsWidth + currentMeasureEndInstructionsWidth;
+        this.currentSystemParams.currentSystemVarWidth += currentVarWidth;
+        this.currentSystemParams.systemMeasureIndex++;
+    }
+
+    private createMusicPage(): GraphicalMusicPage {
+        let page: GraphicalMusicPage = new GraphicalMusicPage(this.graphicalMusicSheet);
+        this.graphicalMusicSheet.MusicPages.push(page);
+        page.PositionAndShape.BorderLeft = 0.0;
+        page.PositionAndShape.BorderRight = this.graphicalMusicSheet.ParentMusicSheet.pageWidth;
+        page.PositionAndShape.BorderTop = 0.0;
+        page.PositionAndShape.BorderBottom = this.rules.PageHeight;
+        page.PositionAndShape.RelativePosition = new PointF2D(0.0, 0.0);
+        return page;
+    }
+
+    private initMusicSystem(): MusicSystem {
+        let musicSystem: MusicSystem = this.symbolFactory.createMusicSystem(this.currentMusicPage, this.globalSystemIndex++);
+        this.currentMusicPage.MusicSystems.push(musicSystem);
+        let boundingBox: BoundingBox = musicSystem.PositionAndShape;
+        this.currentMusicPage.PositionAndShape.ChildElements.push(boundingBox);
+        return musicSystem;
+    }
+
+    private getFullPageSystemWidth(): number {
+        return this.currentMusicPage.PositionAndShape.Size.width - this.rules.PageLeftMargin
+            - this.rules.PageRightMargin - this.rules.SystemLeftMargin - this.rules.SystemRightMargin;
+    }
+
+    private layoutSystemStaves(): void {
+        let systemWidth: number = this.getFullPageSystemWidth();
+        let musicSystem: MusicSystem = this.currentSystemParams.currentSystem;
+        let boundingBox: BoundingBox = musicSystem.PositionAndShape;
+        boundingBox.BorderLeft = 0.0;
+        boundingBox.BorderRight = systemWidth;
+        boundingBox.BorderTop = 0.0;
+        let staffList: Staff[] = [];
+        let instruments: Instrument[] = this.graphicalMusicSheet.ParentMusicSheet.Instruments;
+        for (let idx: number = 0, len: number = instruments.length; idx < len; ++idx) {
+            let instrument: Instrument = instruments[idx];
+            if (instrument.Voices.length === 0 || !instrument.Visible) {
+                continue;
+            }
+            for (let idx2: number = 0, len2: number = instrument.Staves.length; idx2 < len2; ++idx2) {
+                let staff: Staff = instrument.Staves[idx2];
+                staffList.push(staff);
+            }
+        }
+        let multiLyrics: boolean = false;
+        if (this.leadSheet) {
+            for (let idx: number = 0, len: number = staffList.length; idx < len; ++idx) {
+                let staff: Staff = staffList[idx];
+                if (staff.ParentInstrument.LyricVersesNumbers.length > 1) {
+                    multiLyrics = true;
+                    break;
+                }
+            }
+        }
+        let yOffsetSum: number = 0;
+        for (let i: number = 0; i < staffList.length; i++) {
+            this.addStaffLineToMusicSystem(musicSystem, yOffsetSum, staffList[i]);
+            yOffsetSum += this.rules.StaffHeight;
+            if (i + 1 < staffList.length) {
+                let yOffset: number = 0;
+                if (this.leadSheet && !multiLyrics) {
+                    yOffset = 2.5;
+                } else {
+                    if (staffList[i].ParentInstrument === staffList[i + 1].ParentInstrument) {
+                        yOffset = this.rules.BetweenStaffDistance;
+                    } else {
+                        yOffset = this.rules.StaffDistance;
+                    }
+                }
+                yOffsetSum += yOffset;
+            }
+        }
+        boundingBox.BorderBottom = yOffsetSum;
+    }
+
+    private addStaffLineToMusicSystem(musicSystem: MusicSystem, relativeYPosition: number, staff: Staff): void {
+        if (musicSystem !== undefined) {
+            let staffLine: StaffLine = this.symbolFactory.createStaffLine(musicSystem, staff);
+            musicSystem.StaffLines.push(staffLine);
+            let boundingBox: BoundingBox = staffLine.PositionAndShape;
+            musicSystem.PositionAndShape.ChildElements.push(boundingBox);
+            let relativePosition: PointF2D = new PointF2D();
+            if (musicSystem.Parent.MusicSystems[0] === musicSystem && musicSystem.Parent === musicSystem.Parent.Parent.MusicPages[0]) {
+                relativePosition.x = this.rules.FirstSystemMargin;
+            } else {
+                relativePosition.x = 0.0;
+            }
+            relativePosition.y = relativeYPosition;
+            boundingBox.RelativePosition = relativePosition;
+            if (musicSystem.Parent.MusicSystems[0] === musicSystem && musicSystem.Parent === musicSystem.Parent.Parent.MusicPages[0]) {
+                boundingBox.BorderRight = musicSystem.PositionAndShape.Size.width - this.rules.FirstSystemMargin;
+            } else {
+                boundingBox.BorderRight = musicSystem.PositionAndShape.Size.width;
+            }
+            boundingBox.BorderLeft = 0.0;
+            boundingBox.BorderTop = 0.0;
+            boundingBox.BorderBottom = this.rules.StaffHeight;
+            for (let i: number = 0; i < 5; i++) {
+                let start: PointF2D = new PointF2D();
+                start.x = 0.0;
+                start.y = i * this.rules.StaffHeight / 4;
+                let end: PointF2D = new PointF2D();
+                end.x = staffLine.PositionAndShape.Size.width;
+                end.y = i * this.rules.StaffHeight / 4;
+                if (this.leadSheet) {
+                    start.y = end.y = 0;
+                }
+                staffLine.StaffLines[i] = new GraphicalLine(start, end, this.rules.StaffLineWidth);
+            }
+        }
+    }
+
+    private initializeActiveInstructions(measureList: StaffMeasure[]): void {
+        let firstSourceMeasure: SourceMeasure = this.graphicalMusicSheet.ParentMusicSheet.getFirstSourceMeasure();
+        if (firstSourceMeasure !== undefined) {
+            this.visibleStaffIndices = this.graphicalMusicSheet.getVisibleStavesIndecesFromSourceMeasure(measureList);
+            for (let i: number = 0, len: number = this.visibleStaffIndices.length; i < len; i++) {
+                let staffIndex: number = this.visibleStaffIndices[i];
+                let graphicalMeasure: StaffMeasure = this.graphicalMusicSheet.getGraphicalMeasureFromSourceMeasureAndIndex(firstSourceMeasure, staffIndex);
+                this.activeClefs[i] = <ClefInstruction>firstSourceMeasure.FirstInstructionsStaffEntries[staffIndex].Instructions[0];
+                let keyInstruction: KeyInstruction = KeyInstruction.copy(
+                    <KeyInstruction>firstSourceMeasure.FirstInstructionsStaffEntries[staffIndex].Instructions[1]);
+                keyInstruction = this.transposeKeyInstruction(keyInstruction, graphicalMeasure);
+                this.activeKeys[i] = keyInstruction;
+                this.activeRhythm[i] = <RhythmInstruction>firstSourceMeasure.FirstInstructionsStaffEntries[staffIndex].Instructions[2];
+            }
+        }
+    }
+
+    private transposeKeyInstruction(keyInstruction: KeyInstruction, graphicalMeasure: StaffMeasure): KeyInstruction {
+        if (this.graphicalMusicSheet.ParentMusicSheet.Transpose !== 0
+            && graphicalMeasure.ParentStaff.ParentInstrument.MidiInstrumentId !== MidiInstrument.Percussion
+            && MusicSheetCalculator.transposeCalculator !== undefined
+        ) {
+            MusicSheetCalculator.transposeCalculator.transposeKey(
+                keyInstruction,
+                this.graphicalMusicSheet.ParentMusicSheet.Transpose
+            );
+        }
+        return keyInstruction;
+    }
+
+    private addBeginInstructions(measures: StaffMeasure[], isSystemFirstMeasure: boolean, isFirstSourceMeasure: boolean): number {
+        let measureCount: number = measures.length;
+        if (measureCount === 0) {
+            return 0;
+        }
+        let totalBeginInstructionLengthX: number = 0.0;
+        let sourceMeasure: SourceMeasure = measures[0].parentSourceMeasure;
+        for (let idx: number = 0; idx < measureCount; ++idx) {
+            let measure: StaffMeasure = measures[idx];
+            let staffIndex: number = this.visibleStaffIndices[idx];
+            let beginInstructionsStaffEntry: SourceStaffEntry = sourceMeasure.FirstInstructionsStaffEntries[staffIndex];
+            let beginInstructionLengthX: number = this.AddInstructionsAtMeasureBegin(
+                beginInstructionsStaffEntry, measure,
+                idx, isFirstSourceMeasure,
+                isSystemFirstMeasure
+            );
+            totalBeginInstructionLengthX = Math.max(totalBeginInstructionLengthX, beginInstructionLengthX);
+        }
+        return totalBeginInstructionLengthX;
+    }
+
+    private addEndInstructions(measures: StaffMeasure[]): number {
+        let measureCount: number = measures.length;
+        if (measureCount === 0) {
+            return 0;
+        }
+        let totalEndInstructionLengthX: number = 0.5;
+        let sourceMeasure: SourceMeasure = measures[0].parentSourceMeasure;
+        for (let idx: number = 0; idx < measureCount; idx++) {
+            let measure: StaffMeasure = measures[idx];
+            let staffIndex: number = this.visibleStaffIndices[idx];
+            let endInstructionsStaffEntry: SourceStaffEntry = sourceMeasure.LastInstructionsStaffEntries[staffIndex];
+            let endInstructionLengthX: number = this.addInstructionsAtMeasureEnd(endInstructionsStaffEntry, measure);
+            totalEndInstructionLengthX = Math.max(totalEndInstructionLengthX, endInstructionLengthX);
+        }
+        return totalEndInstructionLengthX;
+    }
+
+    private AddInstructionsAtMeasureBegin(firstEntry: SourceStaffEntry, measure: StaffMeasure,
+                                          visibleStaffIdx: number, isFirstSourceMeasure: boolean, isSystemStartMeasure: boolean): number {
+        let instructionsLengthX: number = 0;
+        let currentClef: ClefInstruction = undefined;
+        let currentKey: KeyInstruction = undefined;
+        let currentRhythm: RhythmInstruction = undefined;
+        if (firstEntry !== undefined) {
+            for (let idx: number = 0, len: number = firstEntry.Instructions.length; idx < len; ++idx) {
+                let abstractNotationInstruction: AbstractNotationInstruction = firstEntry.Instructions[idx];
+                if (abstractNotationInstruction instanceof ClefInstruction) {
+                    currentClef = <ClefInstruction>abstractNotationInstruction;
+                } else if (abstractNotationInstruction instanceof KeyInstruction) {
+                    currentKey = <KeyInstruction>abstractNotationInstruction;
+                } else if (abstractNotationInstruction instanceof RhythmInstruction) {
+                    currentRhythm = <RhythmInstruction>abstractNotationInstruction;
+                }
+            }
+        }
+        if (isSystemStartMeasure) {
+            if (currentClef === undefined) {
+                currentClef = this.activeClefs[visibleStaffIdx];
+            }
+            if (currentKey === undefined) {
+                currentKey = this.activeKeys[visibleStaffIdx];
+            }
+            if (isFirstSourceMeasure && currentRhythm === undefined) {
+                currentRhythm = this.activeRhythm[visibleStaffIdx];
+            }
+        }
+        let clefAdded: boolean = false;
+        let keyAdded: boolean = false;
+        let rhythmAdded: boolean = false;
+        if (currentClef !== undefined) {
+            measure.addClefAtBegin(currentClef);
+            clefAdded = true;
+        } else {
+            currentClef = this.activeClefs[visibleStaffIdx];
+        }
+        if (currentKey !== undefined) {
+            currentKey = this.transposeKeyInstruction(currentKey, measure);
+            let previousKey: KeyInstruction = isSystemStartMeasure ? undefined : this.activeKeys[visibleStaffIdx];
+            measure.addKeyAtBegin(currentKey, previousKey, currentClef);
+            keyAdded = true;
+        }
+        if (currentRhythm !== undefined) {
+            measure.addRhythmAtBegin(currentRhythm);
+            rhythmAdded = true;
+        }
+        if (clefAdded || keyAdded || rhythmAdded) {
+            instructionsLengthX += measure.beginInstructionsWidth;
+            if (rhythmAdded) {
+                instructionsLengthX += this.rules.RhythmRightMargin;
+            }
+        }
+        return instructionsLengthX;
+    }
+
+    private addInstructionsAtMeasureEnd(lastEntry: SourceStaffEntry, measure: StaffMeasure): number {
+        if (lastEntry === undefined || lastEntry.Instructions === undefined || lastEntry.Instructions.length === 0) {
+            return 0;
+        }
+        for (let idx: number = 0, len: number = lastEntry.Instructions.length; idx < len; ++idx) {
+            let abstractNotationInstruction: AbstractNotationInstruction = lastEntry.Instructions[idx];
+            if (abstractNotationInstruction instanceof ClefInstruction) {
+                let activeClef: ClefInstruction = <ClefInstruction>abstractNotationInstruction;
+                measure.addClefAtEnd(activeClef);
+            }
+        }
+        return this.rules.MeasureRightMargin + measure.endInstructionsWidth;
+    }
+
+    private updateActiveClefs(measure: SourceMeasure, staffMeasures: StaffMeasure[]): void {
+        for (let visStaffIdx: number = 0, len: number = staffMeasures.length; visStaffIdx < len; visStaffIdx++) {
+            let staffIndex: number = this.visibleStaffIndices[visStaffIdx];
+            let firstEntry: SourceStaffEntry = measure.FirstInstructionsStaffEntries[staffIndex];
+            if (firstEntry !== undefined) {
+                for (let idx: number = 0, len2: number = firstEntry.Instructions.length; idx < len2; ++idx) {
+                    let abstractNotationInstruction: AbstractNotationInstruction = firstEntry.Instructions[idx];
+                    if (abstractNotationInstruction instanceof ClefInstruction) {
+                        this.activeClefs[visStaffIdx] = <ClefInstruction>abstractNotationInstruction;
+                    } else if (abstractNotationInstruction instanceof KeyInstruction) {
+                        this.activeKeys[visStaffIdx] = <KeyInstruction>abstractNotationInstruction;
+                    } else if (abstractNotationInstruction instanceof RhythmInstruction) {
+                        this.activeRhythm[visStaffIdx] = <RhythmInstruction>abstractNotationInstruction;
+                    }
+                }
+            }
+            let entries: SourceStaffEntry[] = measure.getEntriesPerStaff(staffIndex);
+            for (let idx: number = 0, len2: number = entries.length; idx < len2; ++idx) {
+                let staffEntry: SourceStaffEntry = entries[idx];
+                if (staffEntry.Instructions !== undefined) {
+                    for (let idx2: number = 0, len3: number = staffEntry.Instructions.length; idx2 < len3; ++idx2) {
+                        let abstractNotationInstruction: AbstractNotationInstruction = staffEntry.Instructions[idx2];
+                        if (abstractNotationInstruction instanceof ClefInstruction) {
+                            this.activeClefs[visStaffIdx] = <ClefInstruction>abstractNotationInstruction;
+                        }
+                    }
+                }
+            }
+            let lastEntry: SourceStaffEntry = measure.LastInstructionsStaffEntries[staffIndex];
+            if (lastEntry !== undefined) {
+                let instructions: AbstractNotationInstruction[] = lastEntry.Instructions;
+                for (let idx: number = 0, len3: number = instructions.length; idx < len3; ++idx) {
+                    let abstractNotationInstruction: AbstractNotationInstruction = instructions[idx];
+                    if (abstractNotationInstruction instanceof ClefInstruction) {
+                        this.activeClefs[visStaffIdx] = <ClefInstruction>abstractNotationInstruction;
+                    }
+                }
+            }
+        }
+    }
+
+    private checkAndCreateExtraInstructionMeasure(measures: StaffMeasure[]): void {
+        let firstStaffEntries: SourceStaffEntry[] = measures[0].parentSourceMeasure.FirstInstructionsStaffEntries;
+        let visibleInstructionEntries: SourceStaffEntry[] = [];
+        for (let idx: number = 0, len: number = measures.length; idx < len; ++idx) {
+            let measure: StaffMeasure = measures[idx];
+            visibleInstructionEntries.push(firstStaffEntries[measure.ParentStaff.idInMusicSheet]);
+        }
+        let maxMeasureWidth: number = 0;
+        for (let visStaffIdx: number = 0, len: number = visibleInstructionEntries.length; visStaffIdx < len; ++visStaffIdx) {
+            let sse: SourceStaffEntry = visibleInstructionEntries[visStaffIdx];
+            if (sse === undefined) {
+                continue;
+            }
+            let instructions: AbstractNotationInstruction[] = sse.Instructions;
+            let keyInstruction: KeyInstruction = undefined;
+            let rhythmInstruction: RhythmInstruction = undefined;
+            for (let idx2: number = 0, len2: number = instructions.length; idx2 < len2; ++idx2) {
+                let instruction: AbstractNotationInstruction = instructions[idx2];
+                if (instruction instanceof KeyInstruction && (<KeyInstruction>instruction).Key !== this.activeKeys[visStaffIdx].Key) {
+                    keyInstruction = <KeyInstruction>instruction;
+                }
+                if (instruction instanceof RhythmInstruction && (<RhythmInstruction>instruction) !== this.activeRhythm[visStaffIdx]) {
+                    rhythmInstruction = <RhythmInstruction>instruction;
+                }
+            }
+            if (keyInstruction !== undefined || rhythmInstruction !== undefined) {
+                let measureWidth: number = this.addExtraInstructionMeasure(visStaffIdx, keyInstruction, rhythmInstruction);
+                maxMeasureWidth = Math.max(maxMeasureWidth, measureWidth);
+            }
+        }
+        if (maxMeasureWidth > 0) {
+            this.currentSystemParams.systemMeasures.push({
+                beginLine: SystemLinesEnum.None,
+                endLine: SystemLinesEnum.None,
+            });
+            this.currentSystemParams.currentWidth += maxMeasureWidth;
+            this.currentSystemParams.currentSystemFixWidth += maxMeasureWidth;
+        }
+    }
+
+    private addExtraInstructionMeasure(visStaffIdx: number, keyInstruction: KeyInstruction, rhythmInstruction: RhythmInstruction): number {
+        let currentSystem: MusicSystem = this.currentSystemParams.currentSystem;
+        let measures: StaffMeasure[] = [];
+        let measure: StaffMeasure = this.symbolFactory.createExtraStaffMeasure(currentSystem.StaffLines[visStaffIdx]);
+        measures.push(measure);
+        if (keyInstruction !== undefined) {
+            measure.addKeyAtBegin(keyInstruction, this.activeKeys[visStaffIdx], this.activeClefs[visStaffIdx]);
+        }
+        if (rhythmInstruction !== undefined) {
+            measure.addRhythmAtBegin(rhythmInstruction);
+        }
+        measure.PositionAndShape.BorderLeft = 0.0;
+        measure.PositionAndShape.BorderTop = 0.0;
+        measure.PositionAndShape.BorderBottom = this.rules.StaffHeight;
+        let width: number = this.rules.MeasureLeftMargin + measure.beginInstructionsWidth + this.rules.MeasureRightMargin;
+        measure.PositionAndShape.BorderRight = width;
+        currentSystem.StaffLines[visStaffIdx].Measures.push(measure);
+        measure.ParentStaffLine = currentSystem.StaffLines[visStaffIdx];
+        currentSystem.StaffLines[visStaffIdx].PositionAndShape.ChildElements.push(measure.PositionAndShape);
+        return width;
+    }
+
+    private addStaveMeasuresToSystem(staffMeasures: StaffMeasure[]): void {
+        if (staffMeasures[0] !== undefined) {
+            let gmeasures: StaffMeasure[] = [];
+            for (let i: number = 0; i < staffMeasures.length; i++) {
+                gmeasures.push(staffMeasures[i]);
+            }
+            let currentSystem: MusicSystem = this.currentSystemParams.currentSystem;
+            for (let visStaffIdx: number = 0; visStaffIdx < this.numberOfVisibleStaffLines; visStaffIdx++) {
+                let measure: StaffMeasure = gmeasures[visStaffIdx];
+                currentSystem.StaffLines[visStaffIdx].Measures.push(measure);
+                measure.ParentStaffLine = currentSystem.StaffLines[visStaffIdx];
+                currentSystem.StaffLines[visStaffIdx].PositionAndShape.ChildElements.push(measure.PositionAndShape);
+            }
+            currentSystem.AddStaffMeasures(gmeasures);
+        }
+    }
+
+    private getMeasureStartLine(): SystemLinesEnum {
+        let thisMeasureBeginsLineRep: boolean = this.thisMeasureBeginsLineRepetition();
+        if (thisMeasureBeginsLineRep) {
+            let isSystemStartMeasure: boolean = this.currentSystemParams.IsSystemStartMeasure();
+            let isGlobalFirstMeasure: boolean = this.measureListIndex === 0;
+            if (this.previousMeasureEndsLineRepetition() && !isSystemStartMeasure) {
+                return SystemLinesEnum.DotsBoldBoldDots;
+            }
+            if (!isGlobalFirstMeasure) {
+                return SystemLinesEnum.BoldThinDots;
+            }
+        }
+        return SystemLinesEnum.None;
+    }
+
+    private getMeasureEndLine(): SystemLinesEnum {
+        if (this.nextMeasureBeginsLineRepetition() && this.thisMeasureEndsLineRepetition()) {
+            return SystemLinesEnum.DotsBoldBoldDots;
+        }
+        if (this.thisMeasureEndsLineRepetition()) {
+            return SystemLinesEnum.DotsThinBold;
+        }
+        if (this.measureListIndex === this.measureList.length - 1 || this.measureList[this.measureListIndex][0].parentSourceMeasure.endsPiece) {
+            return SystemLinesEnum.ThinBold;
+        }
+        if (this.nextMeasureHasKeyInstructionChange() || this.thisMeasureEndsWordRepetition() || this.nextMeasureBeginsWordRepetition()) {
+            return SystemLinesEnum.DoubleThin;
+        }
+        return SystemLinesEnum.SingleThin;
+    }
+
+    private getLineWidth(measure: StaffMeasure, systemLineEnum: SystemLinesEnum, isSystemStartMeasure: boolean): number {
+        let width: number = measure.getLineWidth(systemLineEnum);
+        if (systemLineEnum === SystemLinesEnum.DotsBoldBoldDots) {
+            width /= 2;
+        }
+        if (isSystemStartMeasure && systemLineEnum === SystemLinesEnum.BoldThinDots) {
+            width += this.rules.DistanceBetweenLastInstructionAndRepetitionBarline;
+        }
+        return width;
+    }
+
+    private previousMeasureEndsLineRepetition(): boolean {
+        if (this.measureListIndex === 0) {
+            return false;
+        }
+        for (let idx: number = 0, len: number = this.measureList[this.measureListIndex - 1].length; idx < len; ++idx) {
+            let measure: StaffMeasure = this.measureList[this.measureListIndex - 1][idx];
+            if (measure.endsWithLineRepetition()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private thisMeasureBeginsLineRepetition(): boolean {
+        for (let idx: number = 0, len: number = this.measureList[this.measureListIndex].length; idx < len; ++idx) {
+            let measure: StaffMeasure = this.measureList[this.measureListIndex][idx];
+            if (measure.beginsWithLineRepetition()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private nextMeasureBeginsLineRepetition(): boolean {
+        let nextMeasureIndex: number = this.measureListIndex + 1;
+        if (nextMeasureIndex >= this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures.length) {
+            return false;
+        }
+        for (let idx: number = 0, len: number = this.measureList[nextMeasureIndex].length; idx < len; ++idx) {
+            let measure: StaffMeasure = this.measureList[nextMeasureIndex][idx];
+            if (measure.beginsWithLineRepetition()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private thisMeasureEndsLineRepetition(): boolean {
+        for (let idx: number = 0, len: number = this.measureList[this.measureListIndex].length; idx < len; ++idx) {
+            let measure: StaffMeasure = this.measureList[this.measureListIndex][idx];
+            if (measure.endsWithLineRepetition()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private nextMeasureBeginsWordRepetition(): boolean {
+        let nextMeasureIndex: number = this.measureListIndex + 1;
+        if (nextMeasureIndex >= this.graphicalMusicSheet.ParentMusicSheet.SourceMeasures.length) {
+            return false;
+        }
+        for (let idx: number = 0, len: number = this.measureList[nextMeasureIndex].length; idx < len; ++idx) {
+            let measure: StaffMeasure = this.measureList[nextMeasureIndex][idx];
+            if (measure.beginsWithWordRepetition()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private thisMeasureEndsWordRepetition(): boolean {
+        for (let idx: number = 0, len: number = this.measureList[this.measureListIndex].length; idx < len; ++idx) {
+            let measure: StaffMeasure = this.measureList[this.measureListIndex][idx];
+            if (measure.endsWithWordRepetition()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private nextMeasureHasKeyInstructionChange(): boolean {
+        return this.getNextMeasureKeyInstruction() !== undefined;
+    }
+
+    private getNextMeasureKeyInstruction(): KeyInstruction {
+        if (this.measureListIndex < this.measureList.length - 1) {
+            for (let visIndex: number = 0; visIndex < this.measureList[this.measureListIndex].length; visIndex++) {
+                let sourceMeasure: SourceMeasure = this.measureList[this.measureListIndex + 1][visIndex].parentSourceMeasure;
+                if (sourceMeasure === undefined) {
+                    return undefined;
+                }
+                return sourceMeasure.getKeyInstruction(this.visibleStaffIndices[visIndex]);
+            }
+        }
+        return undefined;
+    }
+
+    private calculateXScalingFactor(systemFixWidth: number, systemVarWidth: number): number {
+        if (Math.abs(systemVarWidth - 0) < 0.00001 || Math.abs(systemFixWidth - 0) < 0.00001) {
+            return 1.0;
+        }
+        let systemEndX: number;
+        let currentSystem: MusicSystem = this.currentSystemParams.currentSystem;
+        systemEndX = currentSystem.StaffLines[0].PositionAndShape.Size.width;
+        let scalingFactor: number = (systemEndX - systemFixWidth) / systemVarWidth;
+        return scalingFactor;
+    }
+
+    private stretchMusicSystem(isPartEndingSystem: boolean): void {
+        let scalingFactor: number = this.calculateXScalingFactor(
+            this.currentSystemParams.currentSystemFixWidth, this.currentSystemParams.currentSystemVarWidth
+        );
+        if (isPartEndingSystem) {
+            scalingFactor = Math.min(scalingFactor, this.rules.LastSystemMaxScalingFactor);
+        }
+        let currentSystem: MusicSystem = this.currentSystemParams.currentSystem;
+        for (let visStaffIdx: number = 0, len: number = currentSystem.StaffLines.length; visStaffIdx < len; ++visStaffIdx) {
+            let staffLine: StaffLine = currentSystem.StaffLines[visStaffIdx];
+            let currentXPosition: number = 0.0;
+            for (let measureIndex: number = 0; measureIndex < staffLine.Measures.length; measureIndex++) {
+                let measure: StaffMeasure = staffLine.Measures[measureIndex];
+                measure.setPositionInStaffline(currentXPosition);
+                measure.setWidth(measure.beginInstructionsWidth + measure.minimumStaffEntriesWidth * scalingFactor + measure.endInstructionsWidth);
+                if (measureIndex < this.currentSystemParams.systemMeasures.length) {
+                    let startLine: SystemLinesEnum = this.currentSystemParams.systemMeasures[measureIndex].beginLine;
+                    let lineWidth: number = measure.getLineWidth(SystemLinesEnum.BoldThinDots);
+                    switch (startLine) {
+                        case SystemLinesEnum.BoldThinDots:
+                            let xPosition: number = currentXPosition;
+                            if (measureIndex === 0) {
+                                xPosition = currentXPosition + measure.beginInstructionsWidth - lineWidth;
+                            }
+
+                            currentSystem.createVerticalLineForMeasure(xPosition, lineWidth, startLine, SystemLinePosition.MeasureBegin, measureIndex, measure);
+                            break;
+                        default:
+                    }
+                }
+                measure.staffEntriesScaleFactor = scalingFactor;
+                measure.layoutSymbols();
+                let nextMeasureHasRepStartLine: boolean = measureIndex + 1 < this.currentSystemParams.systemMeasures.length
+                    && this.currentSystemParams.systemMeasures[measureIndex + 1].beginLine === SystemLinesEnum.BoldThinDots;
+                if (!nextMeasureHasRepStartLine) {
+                    let endLine: SystemLinesEnum = SystemLinesEnum.SingleThin;
+                    if (measureIndex < this.currentSystemParams.systemMeasures.length) {
+                        endLine = this.currentSystemParams.systemMeasures[measureIndex].endLine;
+                    }
+                    let lineWidth: number = measure.getLineWidth(endLine);
+                    let xPos: number = measure.PositionAndShape.RelativePosition.x + measure.PositionAndShape.BorderRight - lineWidth;
+                    if (endLine === SystemLinesEnum.DotsBoldBoldDots) {
+                        xPos -= lineWidth / 2;
+                    }
+                    currentSystem.createVerticalLineForMeasure(xPos, lineWidth, endLine, SystemLinePosition.MeasureEnd, measureIndex, measure);
+                }
+                currentXPosition = measure.PositionAndShape.RelativePosition.x + measure.PositionAndShape.BorderRight;
+            }
+        }
+        if (isPartEndingSystem) {
+            this.decreaseMusicSystemBorders();
+        }
+    }
+
+    private decreaseMusicSystemBorders(): void {
+        let currentSystem: MusicSystem = this.currentSystemParams.currentSystem;
+        let bb: BoundingBox = CollectionUtil.last(currentSystem.StaffLines[0].Measures).PositionAndShape;
+        let width: number = bb.RelativePosition.x + bb.Size.width;
+        for (let idx: number = 0, len: number = currentSystem.StaffLines.length; idx < len; ++idx) {
+            let staffLine: StaffLine = currentSystem.StaffLines[idx];
+            staffLine.PositionAndShape.BorderRight = width;
+            for (let idx2: number = 0, len2: number = staffLine.StaffLines.length; idx2 < len2; ++idx2) {
+                let graphicalLine: GraphicalLine = staffLine.StaffLines[idx2];
+                graphicalLine.End = new PointF2D(width, graphicalLine.End.y);
+            }
+        }
+        currentSystem.PositionAndShape.BorderRight = width + this.currentSystemParams.maxLabelLength + this.rules.SystemLabelsRightMargin;
+    }
+}
+export class SystemBuildParameters {
+    public currentSystem: MusicSystem;
+    public systemMeasures: MeasureBuildParameters[] = [];
+    public systemMeasureIndex: number = 0;
+    public currentWidth: number = 0;
+    public currentSystemFixWidth: number = 0;
+    public currentSystemVarWidth: number = 0;
+    public maxLabelLength: number = 0;
+
+    public IsSystemStartMeasure(): boolean {
+        return this.systemMeasureIndex === 0;
+    }
+}
+
+export class MeasureBuildParameters {
+    public beginLine: SystemLinesEnum;
+    public endLine: SystemLinesEnum;
+}

+ 13 - 0
src/MusicalScore/Graphical/OctaveShiftParams.ts

@@ -0,0 +1,13 @@
+import {Fraction} from "../../Common/DataObjects/fraction";
+import {OctaveShift} from "../VoiceData/Expressions/ContinuousExpressions/octaveShift";
+
+export class OctaveShiftParams {
+    constructor(openOctaveShift: OctaveShift, absoluteStartTimestamp: Fraction, absoluteEndTimestamp: Fraction) {
+        this.getOpenOctaveShift = openOctaveShift;
+        this.getAbsoluteStartTimestamp = absoluteStartTimestamp;
+        this.getAbsoluteEndTimestamp = absoluteEndTimestamp;
+    }
+    public getOpenOctaveShift: OctaveShift;
+    public getAbsoluteStartTimestamp: Fraction;
+    public getAbsoluteEndTimestamp: Fraction;
+}

+ 68 - 0
src/MusicalScore/Graphical/SelectionEndSymbol.ts

@@ -0,0 +1,68 @@
+import {GraphicalObject} from "./GraphicalObject";
+import {MusicSystem} from "./MusicSystem";
+import {OutlineAndFillStyleEnum} from "./DrawingEnums";
+import {StaffLine} from "./StaffLine";
+import {PointF2D} from "../../Common/DataObjects/PointF2D";
+import {BoundingBox} from "./BoundingBox";
+import {GraphicalLine} from "./GraphicalLine";
+import {CollectionUtil} from "../../Util/collectionUtil";
+
+export class SelectionEndSymbol extends GraphicalObject {
+    constructor(system: MusicSystem, xPosition: number) {
+        super();
+        let xCoordinate: number = xPosition;
+        let yCoordinate: number = system.PositionAndShape.AbsolutePosition.y;
+        let lineThickness: number = 0.4;
+        let height: number = CollectionUtil.last(system.StaffLines).PositionAndShape.RelativePosition.y + 4;
+        this.verticalLine = new GraphicalLine(
+            new PointF2D(xCoordinate, yCoordinate),
+            new PointF2D(xCoordinate, yCoordinate + height),
+            lineThickness,
+            OutlineAndFillStyleEnum.SelectionSymbol
+        );
+        for (let idx: number = 0, len: number = system.StaffLines.length; idx < len; ++idx) {
+            let staffLine: StaffLine = system.StaffLines[idx];
+            let anchor: PointF2D = new PointF2D(xCoordinate, yCoordinate + staffLine.PositionAndShape.RelativePosition.y);
+            let arrowPoints: PointF2D[] = new Array(3);
+            anchor.y -= .2;
+            arrowPoints[0].x = anchor.x - 3;
+            arrowPoints[0].y = anchor.y + 1.2;
+            arrowPoints[1].x = anchor.x - 2;
+            arrowPoints[1].y = anchor.y + 0.4;
+            arrowPoints[2].x = anchor.x - 2;
+            arrowPoints[2].y = anchor.y + 2;
+            this.arrows.push(arrowPoints);
+            let linePoints: PointF2D[] = new Array(8);
+            let arrowThickness: number = .8;
+            anchor.x -= .1;
+            anchor.y += .3;
+            let hilfsVar: number = .2;
+            linePoints[0].x = anchor.x - 2;
+            linePoints[0].y = anchor.y + 1.5 - hilfsVar;
+            linePoints[1].x = anchor.x - 1;
+            linePoints[1].y = anchor.y + 1.5 - hilfsVar;
+            linePoints[2].x = anchor.x - 1;
+            linePoints[2].y = anchor.y + 2.5;
+            linePoints[3].x = anchor.x - 2;
+            linePoints[3].y = anchor.y + 2.5;
+            linePoints[4].x = linePoints[0].x;
+            linePoints[4].y = linePoints[0].y - arrowThickness;
+            linePoints[5].x = linePoints[4].x + arrowThickness + 1;
+            linePoints[5].y = linePoints[4].y;
+            linePoints[6].x = linePoints[5].x;
+            linePoints[6].y = linePoints[3].y + arrowThickness;
+            linePoints[7].x = linePoints[3].x;
+            linePoints[7].y = linePoints[6].y;
+            this.arrowlines.push(linePoints);
+        }
+        this.boundingBox = new BoundingBox(this);
+        this.boundingBox.AbsolutePosition = new PointF2D(xCoordinate, yCoordinate);
+        this.boundingBox.BorderLeft = -lineThickness;
+        this.boundingBox.BorderRight = 4;
+        this.boundingBox.BorderBottom = height;
+    }
+
+    public verticalLine: GraphicalLine;
+    public arrows: PointF2D[][];
+    public arrowlines: PointF2D[][];
+}

+ 52 - 0
src/MusicalScore/Graphical/SelectionStartSymbol.ts

@@ -0,0 +1,52 @@
+import {PointF2D} from "../../Common/DataObjects/PointF2D";
+import {StaffLine} from "./StaffLine";
+import {OutlineAndFillStyleEnum} from "./DrawingEnums";
+import {GraphicalLine} from "./GraphicalLine";
+import {MusicSystem} from "./MusicSystem";
+import {GraphicalObject} from "./GraphicalObject";
+import {BoundingBox} from "./BoundingBox";
+import {CollectionUtil} from "../../Util/collectionUtil";
+
+export class SelectionStartSymbol extends GraphicalObject {
+    constructor(system: MusicSystem, xPosition: number) {
+        super();
+        let xCoordinate: number = xPosition;
+        let yCoordinate: number = system.PositionAndShape.AbsolutePosition.y;
+        let lineThickness: number = 0.4;
+        let height: number = CollectionUtil.last(system.StaffLines).PositionAndShape.RelativePosition.y + 4;
+        this.verticalLine = new GraphicalLine(
+            new PointF2D(xCoordinate, yCoordinate),
+            new PointF2D(xCoordinate, yCoordinate + height),
+            lineThickness,
+            OutlineAndFillStyleEnum.SelectionSymbol
+        );
+        for (let idx: number = 0, len: number = system.StaffLines.length; idx < len; ++idx) {
+            let staffLine: StaffLine = system.StaffLines[idx];
+            let anchor: PointF2D = new PointF2D(xCoordinate, yCoordinate + staffLine.PositionAndShape.RelativePosition.y);
+            let arrowPoints: PointF2D[] = new Array(7);
+            arrowPoints[0].x = anchor.x + 4;
+            arrowPoints[0].y = anchor.y + 2;
+            arrowPoints[1].x = anchor.x + 2.5;
+            arrowPoints[1].y = anchor.y + 0.5;
+            arrowPoints[2].x = anchor.x + 2.5;
+            arrowPoints[2].y = anchor.y + 1.3;
+            arrowPoints[3].x = anchor.x + 1;
+            arrowPoints[3].y = anchor.y + 1.3;
+            arrowPoints[4].x = anchor.x + 1;
+            arrowPoints[4].y = anchor.y + 2.7;
+            arrowPoints[5].x = anchor.x + 2.5;
+            arrowPoints[5].y = anchor.y + 2.7;
+            arrowPoints[6].x = anchor.x + 2.5;
+            arrowPoints[6].y = anchor.y + 3.5;
+            this.arrows.push(arrowPoints);
+        }
+        this.boundingBox = new BoundingBox(this);
+        this.boundingBox.AbsolutePosition = new PointF2D(xCoordinate, yCoordinate);
+        this.boundingBox.BorderLeft = -lineThickness;
+        this.boundingBox.BorderRight = 4;
+        this.boundingBox.BorderBottom = height;
+    }
+
+    public verticalLine: GraphicalLine;
+    public arrows: PointF2D[][];
+}

+ 110 - 0
src/MusicalScore/Graphical/StaffLine.ts

@@ -0,0 +1,110 @@
+import {Staff} from "../VoiceData/Staff";
+import {BoundingBox} from "./BoundingBox";
+import {Instrument} from "../Instrument";
+import {GraphicalLine} from "./GraphicalLine";
+import {GraphicalStaffEntry} from "./GraphicalStaffEntry";
+import {GraphicalObject} from "./GraphicalObject";
+import {StaffMeasure} from "./StaffMeasure";
+import {MusicSystem} from "./MusicSystem";
+import {StaffLineActivitySymbol} from "./StaffLineActivitySymbol";
+import {PointF2D} from "../../Common/DataObjects/PointF2D";
+
+export abstract class StaffLine extends GraphicalObject {
+    protected measures: StaffMeasure[] = [];
+    protected staffLines: GraphicalLine[] = new Array(5);
+    protected parentMusicSystem: MusicSystem;
+    protected parentStaff: Staff;
+    protected skyLine: number[];
+    protected bottomLine: number[];
+
+    constructor(parentSystem: MusicSystem, parentStaff: Staff) {
+        super();
+        this.parentMusicSystem = parentSystem;
+        this.parentStaff = parentStaff;
+        this.boundingBox = new BoundingBox(this, parentSystem.PositionAndShape);
+    }
+
+    public get Measures(): StaffMeasure[] {
+        return this.measures;
+    }
+
+    public set Measures(value: StaffMeasure[]) {
+        this.measures = value;
+    }
+
+    public get StaffLines(): GraphicalLine[] {
+        return this.staffLines;
+    }
+
+    public set StaffLines(value: GraphicalLine[]) {
+        this.staffLines = value;
+    }
+
+    public get ParentMusicSystem(): MusicSystem {
+        return this.parentMusicSystem;
+    }
+
+    public set ParentMusicSystem(value: MusicSystem) {
+        this.parentMusicSystem = value;
+    }
+
+    public get ParentStaff(): Staff {
+        return this.parentStaff;
+    }
+
+    public set ParentStaff(value: Staff) {
+        this.parentStaff = value;
+    }
+
+    public get SkyLine(): number[] {
+        return this.skyLine;
+    }
+
+    public set SkyLine(value: number[]) {
+        this.skyLine = value;
+    }
+
+    public get BottomLine(): number[] {
+        return this.bottomLine;
+    }
+
+    public set BottomLine(value: number[]) {
+        this.bottomLine = value;
+    }
+
+    public addActivitySymbolClickArea(): void {
+        let activitySymbol: StaffLineActivitySymbol = new StaffLineActivitySymbol(this);
+        let staffLinePsi: BoundingBox = this.PositionAndShape;
+        activitySymbol.PositionAndShape.RelativePosition =
+            new PointF2D(staffLinePsi.RelativePosition.x + staffLinePsi.BorderRight + 0.5, staffLinePsi.RelativePosition.y + 0.5);
+        this.parentMusicSystem.PositionAndShape.ChildElements.push(activitySymbol.PositionAndShape);
+    }
+
+    public isPartOfMultiStaffInstrument(): boolean {
+        let instrument: Instrument = this.parentStaff.ParentInstrument;
+        if (instrument.Staves.length > 1) {
+            return true;
+        }
+        return false;
+    }
+
+    public findClosestStaffEntry(xPosition: number): GraphicalStaffEntry {
+        let closestStaffentry: GraphicalStaffEntry = undefined;
+        let difference: number = Number.MAX_VALUE;
+        for (let idx: number = 0, len: number = this.Measures.length; idx < len; ++idx) {
+            let graphicalMeasure: StaffMeasure = this.Measures[idx];
+            for (let idx2: number = 0, len2: number = graphicalMeasure.staffEntries.length; idx2 < len2; ++idx2) {
+                let graphicalStaffEntry: GraphicalStaffEntry = graphicalMeasure.staffEntries[idx2];
+                if (
+                    Math.abs(graphicalStaffEntry.PositionAndShape.RelativePosition.x - xPosition + graphicalMeasure.PositionAndShape.RelativePosition.x) < 5.0
+                ) {
+                    difference = Math.abs(
+                        graphicalStaffEntry.PositionAndShape.RelativePosition.x - xPosition + graphicalMeasure.PositionAndShape.RelativePosition.x
+                    );
+                    closestStaffentry = graphicalStaffEntry;
+                }
+            }
+        }
+        return closestStaffentry;
+    }
+}

+ 17 - 0
src/MusicalScore/Graphical/StaffLineActivitySymbol.ts

@@ -0,0 +1,17 @@
+import {GraphicalObject} from "./GraphicalObject";
+import {StaffLine} from "./StaffLine";
+import {BoundingBox} from "./BoundingBox";
+
+export class StaffLineActivitySymbol extends GraphicalObject {
+    constructor(staffLine: StaffLine) {
+        super();
+        this.parentStaffLine = staffLine;
+        let staffLinePsi: BoundingBox = staffLine.PositionAndShape;
+        this.boundingBox = new BoundingBox(this, staffLinePsi);
+        this.boundingBox.BorderRight = 6;
+        this.boundingBox.BorderBottom = 4.5;
+        this.boundingBox.BorderLeft = -1.5;
+        this.boundingBox.BorderTop = -1.5;
+    }
+    public parentStaffLine: StaffLine;
+}

+ 265 - 0
src/MusicalScore/Graphical/StaffMeasure.ts

@@ -0,0 +1,265 @@
+import {MusicSystem} from "./MusicSystem";
+import {GraphicalStaffEntry} from "./GraphicalStaffEntry";
+import {SourceMeasure} from "../VoiceData/SourceMeasure";
+import {StaffLine} from "./StaffLine";
+import {Staff} from "../VoiceData/Staff";
+import {GraphicalObject} from "./GraphicalObject";
+import {ClefInstruction} from "../VoiceData/Instructions/ClefInstruction";
+import {KeyInstruction} from "../VoiceData/Instructions/KeyInstruction";
+import {RhythmInstruction} from "../VoiceData/Instructions/RhythmInstruction";
+import {Fraction} from "../../Common/DataObjects/fraction";
+import {Voice} from "../VoiceData/Voice";
+import {VoiceEntry} from "../VoiceData/VoiceEntry";
+import {GraphicalNote} from "./GraphicalNote";
+import {SystemLinesEnum} from "./SystemLinesEnum";
+import {BoundingBox} from "./BoundingBox";
+import {PointF2D} from "../../Common/DataObjects/PointF2D";
+
+export abstract class StaffMeasure extends GraphicalObject {
+    protected firstInstructionStaffEntry: GraphicalStaffEntry;
+    protected lastInstructionStaffEntry: GraphicalStaffEntry;
+
+    constructor(staff: Staff = undefined, parentSourceMeasure: SourceMeasure = undefined, staffLine: StaffLine = undefined) {
+        super();
+        this.parentStaff = staff;
+        this.parentSourceMeasure = parentSourceMeasure;
+        this.parentStaffLine = staffLine;
+        if (staffLine !== undefined) {
+            this.parentStaff = staffLine.ParentStaff;
+            this.PositionAndShape = new BoundingBox(this, staffLine.PositionAndShape);
+        } else {
+            this.PositionAndShape = new BoundingBox(this);
+        }
+        this.PositionAndShape.BorderBottom = 4;
+        if (this.parentSourceMeasure !== undefined) {
+            this.measureNumber = this.parentSourceMeasure.MeasureNumber;
+        }
+
+        this.staffEntries = [];
+    }
+
+    public parentSourceMeasure: SourceMeasure;
+    public staffEntries: GraphicalStaffEntry[];
+    public parentMusicSystem: MusicSystem;
+    public beginInstructionsWidth: number;
+    public minimumStaffEntriesWidth: number;
+    public staffEntriesScaleFactor: number;
+    public endInstructionsWidth: number;
+    public hasError: boolean;
+
+    private parentStaff: Staff;
+    private measureNumber: number = -1;
+    private parentStaffLine: StaffLine;
+
+    public get ParentStaff(): Staff {
+        return this.parentStaff;
+    }
+
+    public get MeasureNumber(): number {
+        return this.measureNumber;
+    }
+
+    public get FirstInstructionStaffEntry(): GraphicalStaffEntry {
+        return this.firstInstructionStaffEntry;
+    }
+
+    public set FirstInstructionStaffEntry(value: GraphicalStaffEntry) {
+        this.firstInstructionStaffEntry = value;
+    }
+
+    public get LastInstructionStaffEntry(): GraphicalStaffEntry {
+        return this.lastInstructionStaffEntry;
+    }
+
+    public set LastInstructionStaffEntry(value: GraphicalStaffEntry) {
+        this.lastInstructionStaffEntry = value;
+    }
+
+    public get ParentStaffLine(): StaffLine {
+        return this.parentStaffLine;
+    }
+
+    public set ParentStaffLine(value: StaffLine) {
+        this.parentStaffLine = value;
+        if (this.parentStaffLine !== undefined) {
+            this.PositionAndShape.Parent = this.parentStaffLine.PositionAndShape;
+        }
+    }
+
+    public resetLayout(): void {
+        throw new Error("not implemented");
+    }
+
+    public getLineWidth(line: SystemLinesEnum): number {
+        throw new Error("not implemented");
+    }
+
+    public addClefAtBegin(clef: ClefInstruction): void {
+        throw new Error("not implemented");
+    }
+
+    public addKeyAtBegin(currentKey: KeyInstruction, previousKey: KeyInstruction, currentClef: ClefInstruction): void {
+        throw new Error("not implemented");
+    }
+
+    public addRhythmAtBegin(rhythm: RhythmInstruction): void {
+        throw new Error("not implemented");
+    }
+
+    public addClefAtEnd(clef: ClefInstruction): void {
+        throw new Error("not implemented");
+    }
+
+    public setPositionInStaffline(xPos: number): void {
+        this.PositionAndShape.RelativePosition = new PointF2D(xPos, 0);
+    }
+
+    public setWidth(width: number): void {
+        this.PositionAndShape.BorderRight = width;
+    }
+
+    public layoutSymbols(): void {
+        throw new Error("not implemented");
+    }
+
+    public findGraphicalStaffEntryFromTimestamp(relativeTimestamp: Fraction): GraphicalStaffEntry {
+        for (let idx: number = 0, len: number = this.staffEntries.length; idx < len; ++idx) {
+            let graphicalStaffEntry: GraphicalStaffEntry = this.staffEntries[idx];
+            if (graphicalStaffEntry.relInMeasureTimestamp === relativeTimestamp) {
+                return graphicalStaffEntry;
+            }
+        }
+        return undefined;
+    }
+
+    public findGraphicalStaffEntryFromVerticalContainerTimestamp(absoluteTimestamp: Fraction): GraphicalStaffEntry {
+        for (let idx: number = 0, len: number = this.staffEntries.length; idx < len; ++idx) {
+            let graphicalStaffEntry: GraphicalStaffEntry = this.staffEntries[idx];
+            if (graphicalStaffEntry.sourceStaffEntry.VerticalContainerParent.getAbsoluteTimestamp() === absoluteTimestamp) {
+                return graphicalStaffEntry;
+            }
+        }
+        return undefined;
+    }
+
+    public hasSameDurationWithSourceMeasureParent(): boolean {
+        let duration: Fraction = new Fraction(0, 1);
+        for (let idx: number = 0, len: number = this.staffEntries.length; idx < len; ++idx) {
+            let graphicalStaffEntry: GraphicalStaffEntry = this.staffEntries[idx];
+            duration.Add(graphicalStaffEntry.findStaffEntryMinNoteLength());
+        }
+        return duration === this.parentSourceMeasure.Duration;
+    }
+
+    public hasMultipleVoices(): boolean {
+        if (this.staffEntries.length === 0) {
+            return false;
+        }
+        let voices: Voice[] = [];
+        for (let idx: number = 0, len: number = this.staffEntries.length; idx < len; ++idx) {
+            let staffEntry: GraphicalStaffEntry = this.staffEntries[idx];
+            for (let idx2: number = 0, len2: number = staffEntry.sourceStaffEntry.VoiceEntries.length; idx2 < len2; ++idx2) {
+                let voiceEntry: VoiceEntry = staffEntry.sourceStaffEntry.VoiceEntries[idx2];
+                if (voices.indexOf(voiceEntry.ParentVoice) < 0) {
+                    voices.push(voiceEntry.ParentVoice);
+                }
+            }
+        }
+        if (voices.length > 1) {
+            return true;
+        }
+        return false;
+    }
+
+    public isVisible(): boolean {
+        return this.ParentStaff.ParentInstrument.Visible;
+    }
+
+    public getGraphicalMeasureDurationFromStaffEntries(): Fraction {
+        let duration: Fraction = new Fraction(0, 1);
+        let voices: Voice[] = [];
+        for (let idx: number = 0, len: number = this.staffEntries.length; idx < len; ++idx) {
+            let graphicalStaffEntry: GraphicalStaffEntry = this.staffEntries[idx];
+            for (let idx2: number = 0, len2: number = graphicalStaffEntry.sourceStaffEntry.VoiceEntries.length; idx2 < len2; ++idx2) {
+                let voiceEntry: VoiceEntry = graphicalStaffEntry.sourceStaffEntry.VoiceEntries[idx2];
+                if (voices.indexOf(voiceEntry.ParentVoice) < 0) {
+                    voices.push(voiceEntry.ParentVoice);
+                }
+            }
+        }
+        for (let idx: number = 0, len: number = voices.length; idx < len; ++idx) {
+            let voice: Voice = voices[idx];
+            let voiceDuration: Fraction = new Fraction(0, 1);
+            for (let idx2: number = 0, len2: number = this.staffEntries.length; idx2 < len2; ++idx2) {
+                let graphicalStaffEntry: GraphicalStaffEntry = this.staffEntries[idx2];
+                for (let idx3: number = 0, len3: number = graphicalStaffEntry.notes.length; idx3 < len3; ++idx3) {
+                    let graphicalNotes: GraphicalNote[] = graphicalStaffEntry.notes[idx3];
+                    if (graphicalNotes.length > 0 && graphicalNotes[0].sourceNote.ParentVoiceEntry.ParentVoice === voice) {
+                        voiceDuration.Add(graphicalNotes[0].graphicalNoteLength);
+                    }
+                }
+            }
+            if (voiceDuration > duration) {
+                duration = Fraction.createFromFraction(voiceDuration);
+            }
+        }
+        return duration;
+    }
+
+    public addGraphicalStaffEntry(graphicalStaffEntry: GraphicalStaffEntry): void {
+        this.staffEntries.push(graphicalStaffEntry);
+        this.PositionAndShape.ChildElements.push(graphicalStaffEntry.PositionAndShape);
+    }
+
+    public addGraphicalStaffEntryAtTimestamp(staffEntry: GraphicalStaffEntry): void {
+        if (staffEntry !== undefined) {
+            if (this.staffEntries.length === 0 || this.staffEntries[this.staffEntries.length - 1].relInMeasureTimestamp < staffEntry.relInMeasureTimestamp) {
+                this.staffEntries.push(staffEntry);
+            } else {
+                for (let i: number = this.staffEntries.length - 1; i >= 0; i--) {
+                    if (this.staffEntries[i].relInMeasureTimestamp < staffEntry.relInMeasureTimestamp) {
+                        this.staffEntries.splice(i + 1, 0, staffEntry);
+                        break;
+                    }
+                    if (i === 0) {
+                        this.staffEntries.splice(i, 0, staffEntry);
+                    }
+                }
+            }
+            this.PositionAndShape.ChildElements.push(staffEntry.PositionAndShape);
+        }
+    }
+
+    public beginsWithLineRepetition(): boolean {
+        let sourceMeasure: SourceMeasure = this.parentSourceMeasure;
+        if (sourceMeasure === undefined) {
+            return false;
+        }
+        return sourceMeasure.beginsWithLineRepetition();
+    }
+
+    public endsWithLineRepetition(): boolean {
+        let sourceMeasure: SourceMeasure = this.parentSourceMeasure;
+        if (sourceMeasure === undefined) {
+            return false;
+        }
+        return sourceMeasure.endsWithLineRepetition();
+    }
+
+    public beginsWithWordRepetition(): boolean {
+        let sourceMeasure: SourceMeasure = this.parentSourceMeasure;
+        if (sourceMeasure === undefined) {
+            return false;
+        }
+        return sourceMeasure.beginsWithWordRepetition();
+    }
+
+    public endsWithWordRepetition(): boolean {
+        let sourceMeasure: SourceMeasure = this.parentSourceMeasure;
+        if (sourceMeasure === undefined) {
+            return false;
+        }
+        return sourceMeasure.endsWithWordRepetition();
+    }
+}
+

+ 48 - 0
src/MusicalScore/Graphical/SystemLine.ts

@@ -0,0 +1,48 @@
+import {StaffMeasure} from "./StaffMeasure";
+import {StaffLine} from "./StaffLine";
+import {MusicSystem} from "./MusicSystem";
+import {SystemLinePosition} from "./SystemLinePosition";
+import {SystemLinesEnum} from "./SystemLinesEnum";
+import {BoundingBox} from "./BoundingBox";
+import {GraphicalObject} from "./GraphicalObject";
+import {EngravingRules} from "./EngravingRules";
+export class SystemLine extends GraphicalObject {
+    constructor(lineType: SystemLinesEnum, linePosition: SystemLinePosition, musicSystem: MusicSystem,
+                topMeasure: StaffMeasure, bottomMeasure: StaffMeasure = undefined) {
+        super();
+        this.lineType = lineType;
+        this.linePosition = linePosition;
+        this.parentMusicSystem = musicSystem;
+        this.topMeasure = topMeasure;
+        this.bottomMeasure = bottomMeasure;
+        this.parentTopStaffLine = topMeasure.ParentStaffLine;
+        this.boundingBox = new BoundingBox(this, musicSystem.PositionAndShape);
+    }
+    public lineType: SystemLinesEnum;
+    public linePosition: SystemLinePosition;
+    public parentMusicSystem: MusicSystem;
+    public parentTopStaffLine: StaffLine;
+    public topMeasure: StaffMeasure;
+    public bottomMeasure: StaffMeasure;
+    public static getObjectWidthForLineType(rules: EngravingRules, systemLineType: SystemLinesEnum): number {
+        switch (systemLineType) {
+            case SystemLinesEnum.SingleThin:
+                return rules.SystemThinLineWidth;
+            case SystemLinesEnum.DoubleThin:
+                return rules.SystemThinLineWidth * 2 + rules.DistanceBetweenVerticalSystemLines;
+            case SystemLinesEnum.ThinBold:
+                return rules.SystemThinLineWidth + rules.SystemBoldLineWidth + rules.DistanceBetweenVerticalSystemLines;
+            case SystemLinesEnum.BoldThinDots:
+                return rules.SystemThinLineWidth + rules.SystemBoldLineWidth + rules.DistanceBetweenVerticalSystemLines + rules.SystemDotWidth +
+                    rules.DistanceBetweenDotAndLine;
+            case SystemLinesEnum.DotsThinBold:
+                return rules.SystemThinLineWidth + rules.SystemBoldLineWidth + rules.DistanceBetweenVerticalSystemLines + rules.SystemDotWidth +
+                    rules.DistanceBetweenDotAndLine;
+            case SystemLinesEnum.DotsBoldBoldDots:
+                return 2 * rules.SystemBoldLineWidth + 2 * rules.SystemDotWidth + 2 * rules.DistanceBetweenDotAndLine +
+                    rules.DistanceBetweenVerticalSystemLines;
+            default:
+                return 0;
+        }
+    }
+}

+ 4 - 0
src/MusicalScore/Graphical/SystemLinePosition.ts

@@ -0,0 +1,4 @@
+export enum SystemLinePosition {
+    MeasureBegin,
+    MeasureEnd
+}

+ 9 - 0
src/MusicalScore/Graphical/SystemLinesEnum.ts

@@ -0,0 +1,9 @@
+export enum SystemLinesEnum {
+    SingleThin = 0,
+    DoubleThin = 1,
+    ThinBold = 2,
+    BoldThinDots = 3,
+    DotsThinBold = 4,
+    DotsBoldBoldDots = 5,
+    None = 6
+}

+ 65 - 0
src/MusicalScore/Graphical/VerticalGraphicalStaffEntryContainer.ts

@@ -0,0 +1,65 @@
+import {Fraction} from "../../Common/DataObjects/fraction";
+import {GraphicalStaffEntry} from "./GraphicalStaffEntry";
+
+export class VerticalGraphicalStaffEntryContainer {
+    constructor(numberOfEntries: number, absoluteTimestamp: Fraction) {
+        this.absoluteTimestamp = absoluteTimestamp;
+        for (let i: number = 0; i < numberOfEntries; i++) {
+            this.staffEntries.push(undefined);
+        }
+    }
+
+    public relativeInMeasureTimestamp: Fraction;
+    private index: number;
+    private absoluteTimestamp: Fraction;
+    private staffEntries: GraphicalStaffEntry[] = [];
+
+    public get Index(): number {
+        return this.index;
+    }
+
+    public set Index(value: number) {
+        this.index = value;
+    }
+
+    public get AbsoluteTimestamp(): Fraction {
+        return this.absoluteTimestamp;
+    }
+
+    public set AbsoluteTimestamp(value: Fraction) {
+        this.absoluteTimestamp = value;
+    }
+
+    public get StaffEntries(): GraphicalStaffEntry[] {
+        return this.staffEntries;
+    }
+
+    public set StaffEntries(value: GraphicalStaffEntry[]) {
+        this.staffEntries = value;
+    }
+
+    public static compareByTimestamp(x: VerticalGraphicalStaffEntryContainer, y: VerticalGraphicalStaffEntryContainer): number {
+        let xValue: number = x.absoluteTimestamp.RealValue;
+        let yValue: number = y.absoluteTimestamp.RealValue;
+
+        if (xValue < yValue) {
+            return -1;
+        } else if (xValue > yValue) {
+            return 1;
+        } else {
+            return 0;
+        }
+    }
+
+    public getFirstNonNullStaffEntry(): GraphicalStaffEntry {
+        for (let idx: number = 0, len: number = this.staffEntries.length; idx < len; ++idx) {
+            let graphicalStaffEntry: GraphicalStaffEntry = this.staffEntries[idx];
+            if (graphicalStaffEntry !== undefined) {
+                return graphicalStaffEntry;
+            }
+        }
+        return undefined;
+    }
+}
+
+

+ 204 - 0
src/MusicalScore/Graphical/VexFlow/VexFlowConverter.ts

@@ -0,0 +1,204 @@
+import Vex = require("vexflow");
+import {ClefEnum} from "../../VoiceData/Instructions/ClefInstruction";
+import {ClefInstruction} from "../../VoiceData/Instructions/ClefInstruction";
+import {Pitch} from "../../../Common/DataObjects/pitch";
+import {Fraction} from "../../../Common/DataObjects/fraction";
+import {RhythmInstruction} from "../../VoiceData/Instructions/RhythmInstruction";
+import {RhythmSymbolEnum} from "../../VoiceData/Instructions/RhythmInstruction";
+import {KeyInstruction} from "../../VoiceData/Instructions/KeyInstruction";
+import {KeyEnum} from "../../VoiceData/Instructions/KeyInstruction";
+import {AccidentalEnum} from "../../../Common/DataObjects/pitch";
+import {NoteEnum} from "../../../Common/DataObjects/pitch";
+import {VexFlowGraphicalNote} from "./VexFlowGraphicalNote";
+import {GraphicalNote} from "../GraphicalNote";
+import {SystemLinesEnum} from "../SystemLinesEnum";
+
+export class VexFlowConverter {
+    private static majorMap: {[_: number]: string; } = {
+        "0": "C", 1: "G", 2: "D", 3: "A", 4: "E", 5: "B", 6: "F#", 7: "C#",
+        8: "G#", "-1": "F", "-8": "Fb", "-7": "Cb", "-6": "Gb", "-5": "Db", "-4": "Ab", "-3": "Eb", "-2": "Bb",
+    };
+    private static minorMap: {[_: number]: string; } = {
+        "1": "E", "7": "A#", "0": "A", "6": "D#", "3": "F#", "-5": "Bb", "-4": "F", "-7": "Ab", "-6": "Eb",
+        "-1": "D", "4": "C#", "-3": "C", "-2": "G", "2": "B", "5": "G#", "-8": "Db", "8": "E#",
+    };
+
+    public static duration(fraction: Fraction): string {
+        let dur: number = fraction.RealValue;
+        if (dur >= 1) {
+            return "w";
+        } else if (dur < 1 && dur >= 0.5) {
+            return "h";
+        } else if (dur < 0.5 && dur >= 0.25) {
+            return "q";
+        } else if (dur < 0.25 && dur >= 0.125) {
+            return "8";
+        } else if (dur < 0.125 && dur >= 0.0625) {
+            return "16";
+        } else if (dur < 0.0625 && dur >= 0.03125) {
+            return "32";
+        }
+        return "128";
+    }
+
+    /**
+     * Takes a Pitch and returns a string representing a VexFlow pitch,
+     * which has the form "b/4", plus its alteration (accidental)
+     * @param pitch
+     * @returns {string[]}
+     */
+    public static pitch(pitch: Pitch, clef: ClefInstruction): [string, string, ClefInstruction] {
+        let fund: string = NoteEnum[pitch.FundamentalNote].toLowerCase();
+        // The octave seems to need a shift of three FIXME?
+        let octave: number = pitch.Octave + clef.OctaveOffset + 3;
+        let acc: string = VexFlowConverter.accidental(pitch.Accidental);
+        return [fund + "n/" + octave, acc, clef];
+    }
+
+    /**
+     * Converts AccidentalEnum to vexFlow accidental string
+     * @param accidental
+     * @returns {string}
+     */
+    public static accidental(accidental: AccidentalEnum): string {
+        let acc: string;
+        switch (accidental) {
+            case AccidentalEnum.NONE:
+                acc = "n";
+                break;
+            case AccidentalEnum.FLAT:
+                acc = "b";
+                break;
+            case AccidentalEnum.SHARP:
+                acc = "#";
+                break;
+            case AccidentalEnum.DOUBLESHARP:
+                acc = "##";
+                break;
+            case AccidentalEnum.DOUBLEFLAT:
+                acc = "bb";
+                break;
+            default:
+        }
+        return acc;
+    }
+
+
+    public static StaveNote(notes: GraphicalNote[]): Vex.Flow.StaveNote {
+        let keys: string[] = [];
+        let accidentals: string[] = [];
+        let frac: Fraction = notes[0].sourceNote.Length;
+        let duration: string = VexFlowConverter.duration(frac);
+        let vfclef: string;
+        for (let note of notes) {
+            let res: [string, string, ClefInstruction] = (note as VexFlowGraphicalNote).vfpitch;
+            if (res === undefined) {
+                keys = ["b/4"];
+                duration += "r";
+                break;
+            }
+            keys.push(res[0]);
+            accidentals.push(res[1]);
+            if (!vfclef) {
+                vfclef = VexFlowConverter.Clef(res[2]);
+            }
+        }
+        let vfnote: Vex.Flow.StaveNote = new Vex.Flow.StaveNote({
+            auto_stem: true,
+            clef: vfclef,
+            duration: duration,
+            duration_override: {
+                denominator: frac.Denominator,
+                numerator: frac.Numerator,
+            },
+            keys: keys,
+        });
+        for (let i: number = 0, len: number = notes.length; i < len; i += 1) {
+            (notes[i] as VexFlowGraphicalNote).setIndex(vfnote, i);
+            if (accidentals[i]) {
+                vfnote.addAccidental(i, new Vex.Flow.Accidental(accidentals[i]));
+            }
+        }
+        return vfnote;
+    }
+
+    public static Clef(clef: ClefInstruction): string {
+        let type: string;
+        switch (clef.ClefType) {
+            case ClefEnum.G:
+                type = "treble";
+                break;
+            case ClefEnum.F:
+                type = "bass";
+                break;
+            case ClefEnum.C:
+                type = "alto";
+                break;
+            case ClefEnum.percussion:
+                type = "percussion";
+                break;
+            case ClefEnum.TAB:
+                type = "tab";
+                break;
+            default:
+        }
+        return type;
+    }
+
+    public static TimeSignature(rhythm: RhythmInstruction): Vex.Flow.TimeSignature {
+        let timeSpec: string;
+        switch (rhythm.SymbolEnum) {
+            case RhythmSymbolEnum.NONE:
+                timeSpec = rhythm.Rhythm.Numerator + "/" + rhythm.Rhythm.Denominator;
+                break;
+            case RhythmSymbolEnum.COMMON:
+                timeSpec = "C";
+                break;
+            case RhythmSymbolEnum.CUT:
+                timeSpec = "C|";
+                break;
+            default:
+        }
+        return new Vex.Flow.TimeSignature(timeSpec);
+    }
+
+    public static keySignature(key: KeyInstruction): string {
+        if (key === undefined) {
+            return undefined;
+        }
+        let ret: string;
+        switch (key.Mode) {
+            case KeyEnum.none:
+                ret = undefined;
+                break;
+            case KeyEnum.minor:
+                ret = VexFlowConverter.minorMap[key.Key] + "m";
+                break;
+            case KeyEnum.major:
+                ret = VexFlowConverter.majorMap[key.Key];
+                break;
+            default:
+        }
+        return ret;
+    }
+
+    public static line(lineType: SystemLinesEnum): any {
+        switch (lineType) {
+            case SystemLinesEnum.SingleThin:
+                return Vex.Flow.StaveConnector.type.SINGLE;
+            case SystemLinesEnum.DoubleThin:
+                return Vex.Flow.StaveConnector.type.DOUBLE;
+            case SystemLinesEnum.ThinBold:
+                return Vex.Flow.StaveConnector.type.SINGLE;
+            case SystemLinesEnum.BoldThinDots:
+                return Vex.Flow.StaveConnector.type.DOUBLE;
+            case SystemLinesEnum.DotsThinBold:
+                return Vex.Flow.StaveConnector.type.DOUBLE;
+            case SystemLinesEnum.DotsBoldBoldDots:
+                return Vex.Flow.StaveConnector.type.DOUBLE;
+            case SystemLinesEnum.None:
+                return Vex.Flow.StaveConnector.type.NONE;
+            default:
+        }
+    }
+}

+ 46 - 0
src/MusicalScore/Graphical/VexFlow/VexFlowGraphicalNote.ts

@@ -0,0 +1,46 @@
+import Vex = require("vexflow");
+import {GraphicalNote} from "../GraphicalNote";
+import {Note} from "../../VoiceData/Note";
+import {GraphicalStaffEntry} from "../GraphicalStaffEntry";
+import {ClefInstruction} from "../../VoiceData/Instructions/ClefInstruction";
+import {VexFlowConverter} from "./VexFlowConverter";
+import {Pitch} from "../../../Common/DataObjects/pitch";
+
+export class VexFlowGraphicalNote extends GraphicalNote {
+    constructor(note: Note, parent: GraphicalStaffEntry, activeClef: ClefInstruction) {
+        super(note, parent);
+        this.clef = activeClef;
+        if (note.Pitch) {
+            this.vfpitch = VexFlowConverter.pitch(note.Pitch, this.clef);
+            this.vfpitch[1] = undefined;
+        }
+    }
+
+    public vfpitch: [string, string, ClefInstruction];
+    private vfnote: [Vex.Flow.StaveNote, number];
+    private clef: ClefInstruction;
+
+    public setPitch(pitch: Pitch): void {
+        if (this.vfnote) {
+            let acc: string = VexFlowConverter.accidental(pitch.Accidental);
+            if (acc) {
+                alert(acc);
+                this.vfnote[0].addAccidental(this.vfnote[1], new Vex.Flow.Accidental(acc));
+            }
+        } else {
+            this.vfpitch = VexFlowConverter.pitch(pitch, this.clef);
+        }
+    }
+
+    /**
+     * Set the corresponding VexFlow StaveNote together with its index
+     * @param note
+     * @param index
+     */
+    public setIndex(note: Vex.Flow.StaveNote, index: number): void {
+        this.vfnote = [note, index];
+        //if (this.vfpitch && this.vfpitch[1]) {
+        //    note.addAccidental(index, new Vex.Flow.Accidental(this.vfpitch[1]));
+        //}
+    }
+}

+ 174 - 0
src/MusicalScore/Graphical/VexFlow/VexFlowGraphicalSymbolFactory.ts

@@ -0,0 +1,174 @@
+import {IGraphicalSymbolFactory} from "../../Interfaces/IGraphicalSymbolFactory";
+import {GraphicalMusicPage} from "../GraphicalMusicPage";
+import {MusicSystem} from "../MusicSystem";
+import {VexFlowMusicSystem} from "./VexFlowMusicSystem";
+import {Staff} from "../../VoiceData/Staff";
+import {StaffLine} from "../StaffLine";
+import {VexFlowStaffLine} from "./VexFlowStaffLine";
+import {SourceMeasure} from "../../VoiceData/SourceMeasure";
+import {StaffMeasure} from "../StaffMeasure";
+import {VexFlowMeasure} from "./VexFlowMeasure";
+import {SourceStaffEntry} from "../../VoiceData/SourceStaffEntry";
+import {GraphicalStaffEntry} from "../GraphicalStaffEntry";
+import {VexFlowStaffEntry} from "./VexFlowStaffEntry";
+import {Note} from "../../VoiceData/Note";
+import {ClefInstruction} from "../../VoiceData/Instructions/ClefInstruction";
+import {OctaveEnum} from "../../VoiceData/Expressions/ContinuousExpressions/octaveShift";
+import {GraphicalNote} from "../GraphicalNote";
+import {Pitch} from "../../../Common/DataObjects/pitch";
+import {TechnicalInstruction} from "../../VoiceData/Instructions/TechnicalInstruction";
+import {VexFlowGraphicalNote} from "./VexFlowGraphicalNote";
+
+export class VexFlowGraphicalSymbolFactory implements IGraphicalSymbolFactory {
+    /**
+     * Create a new music system for the given page.
+     * Currently only one vertically endless page exists where all systems are put to.
+     * @param page
+     * @param systemIndex
+     * @returns {VexFlowMusicSystem}
+     */
+    public createMusicSystem(page: GraphicalMusicPage, systemIndex: number): MusicSystem {
+        return new VexFlowMusicSystem(page, systemIndex);
+    }
+
+    /**
+     * Create a staffline object containing all staff measures belonging to a given system and staff.
+     * @param parentSystem
+     * @param parentStaff
+     * @returns {VexFlowStaffLine}
+     */
+    public createStaffLine(parentSystem: MusicSystem, parentStaff: Staff): StaffLine {
+        return new VexFlowStaffLine(parentSystem, parentStaff);
+    }
+
+    /**
+     * Construct an empty staffMeasure from the given source measure and staff.
+     * @param sourceMeasure
+     * @param staff
+     * @returns {VexFlowMeasure}
+     */
+    public createStaffMeasure(sourceMeasure: SourceMeasure, staff: Staff): StaffMeasure {
+        return new VexFlowMeasure(staff, undefined, sourceMeasure);
+    }
+
+    /**
+     * Create empty measure, which will be used to show key, rhythm changes at the end of the system.
+     * @param staffLine
+     * @returns {VexFlowMeasure}
+     */
+    public createExtraStaffMeasure(staffLine: StaffLine): StaffMeasure {
+        return new VexFlowMeasure(staffLine.ParentStaff, staffLine);
+    }
+
+    /**
+     * Create a staffEntry in the given measure for a given sourceStaffEntry.
+     * @param sourceStaffEntry
+     * @param measure
+     * @returns {VexFlowStaffEntry}
+     */
+    public createStaffEntry(sourceStaffEntry: SourceStaffEntry, measure: StaffMeasure): GraphicalStaffEntry {
+        return new VexFlowStaffEntry(<VexFlowMeasure>measure, sourceStaffEntry, undefined);
+    }
+
+    /**
+     * Create an empty staffEntry which will be used for grace notes.
+     * it will be linked to the given staffEntryParent, which is a staffEntry for normal notes.
+     * Grace notes are always given before (rarely also after) normal notes.
+     * @param staffEntryParent
+     * @param measure
+     * @returns {VexFlowStaffEntry}
+     */
+    public createGraceStaffEntry(staffEntryParent: GraphicalStaffEntry, measure: StaffMeasure): GraphicalStaffEntry {
+        return new VexFlowStaffEntry(<VexFlowMeasure>measure, undefined, <VexFlowStaffEntry>staffEntryParent);
+    }
+
+    /**
+     * Create a Graphical Note for given note and clef and as part of graphicalStaffEntry.
+     * @param note
+     * @param numberOfDots  The number of dots the note has to increase its musical duration.
+     * @param graphicalStaffEntry
+     * @param activeClef    The currently active clef, needed for positioning the note vertically
+     * @param octaveShift   The currently active octave transposition enum, needed for positioning the note vertically
+     * @returns {GraphicalNote}
+     */
+    public createNote(note: Note, numberOfDots: number, graphicalStaffEntry: GraphicalStaffEntry,
+                      activeClef: ClefInstruction, octaveShift: OctaveEnum = OctaveEnum.NONE): GraphicalNote {
+        // Creates the note:
+        let graphicalNote: GraphicalNote = new VexFlowGraphicalNote(note, graphicalStaffEntry, activeClef);
+        // Adds the note to the right (graphical) voice (mynotes)
+        let voiceID: number = note.ParentVoiceEntry.ParentVoice.VoiceId;
+        let mynotes: { [id: number]: GraphicalNote[]; } = (graphicalStaffEntry as VexFlowStaffEntry).graphicalNotes;
+        if (!(voiceID in mynotes)) {
+            mynotes[voiceID] = [];
+        }
+        mynotes[voiceID].push(graphicalNote);
+        return graphicalNote;
+    }
+
+    /**
+     * Create a Graphical Grace Note (smaller head, stem...) for given note and clef and as part of graphicalStaffEntry.
+     * @param note
+     * @param numberOfDots
+     * @param graphicalStaffEntry
+     * @param activeClef
+     * @param octaveShift
+     * @returns {GraphicalNote}
+     */
+    public createGraceNote(note: Note, numberOfDots: number, graphicalStaffEntry: GraphicalStaffEntry,
+                           activeClef: ClefInstruction, octaveShift: OctaveEnum = OctaveEnum.NONE): GraphicalNote {
+        return new GraphicalNote(note, graphicalStaffEntry);
+    }
+
+    /**
+     * Sets a pitch which will be used for rendering the given graphical note (not changing the original pitch of the note!!!).
+     * Will be only called if the displayed accidental is different from the original (e.g. a C# with C# as key instruction)
+     * @param graphicalNote
+     * @param pitch The pitch which will be rendered.
+     * @param grace
+     * @param graceScalingFactor
+     */
+    public addGraphicalAccidental(graphicalNote: GraphicalNote, pitch: Pitch, grace: boolean, graceScalingFactor: number): void {
+        // ToDo: set accidental here from pitch.Accidental
+        let note: VexFlowGraphicalNote = <VexFlowGraphicalNote> graphicalNote;
+        note.setPitch(pitch);
+    }
+
+    /**
+     * Adds a Fermata symbol at the last note of the given tied Note.
+     * The last graphical note of this tied note is located at the given graphicalStaffEntry.
+     * A Fermata has to be located at the last tied note.
+     * @param tiedNote
+     * @param graphicalStaffEntry
+     */
+    public addFermataAtTiedEndNote(tiedNote: Note, graphicalStaffEntry: GraphicalStaffEntry): void {
+        return;
+    }
+
+    /**
+     * Adds a technical instruction at the given staff entry.
+     * @param technicalInstruction
+     * @param graphicalStaffEntry
+     */
+    public createGraphicalTechnicalInstruction(technicalInstruction: TechnicalInstruction, graphicalStaffEntry: GraphicalStaffEntry): void {
+        return;
+    }
+
+    /**
+     * Adds a clef change within a measure before the given staff entry.
+     * @param graphicalStaffEntry
+     * @param clefInstruction
+     */
+    public createInStaffClef(graphicalStaffEntry: GraphicalStaffEntry, clefInstruction: ClefInstruction): void {
+        return;
+    }
+
+    /**
+     * Adds a chord symbol at the given staff entry
+     * @param sourceStaffEntry
+     * @param graphicalStaffEntry
+     * @param transposeHalftones
+     */
+    public createChordSymbol(sourceStaffEntry: SourceStaffEntry, graphicalStaffEntry: GraphicalStaffEntry, transposeHalftones: number): void {
+        return;
+    }
+}

+ 301 - 0
src/MusicalScore/Graphical/VexFlow/VexFlowMeasure.ts

@@ -0,0 +1,301 @@
+import Vex = require("vexflow");
+import {StaffMeasure} from "../StaffMeasure";
+import {SourceMeasure} from "../../VoiceData/SourceMeasure";
+import {Staff} from "../../VoiceData/Staff";
+import {StaffLine} from "../StaffLine";
+import {SystemLinesEnum} from "../SystemLinesEnum";
+import {ClefInstruction} from "../../VoiceData/Instructions/ClefInstruction";
+import {KeyInstruction} from "../../VoiceData/Instructions/KeyInstruction";
+import {RhythmInstruction} from "../../VoiceData/Instructions/RhythmInstruction";
+import {VexFlowConverter} from "./VexFlowConverter";
+import {VexFlowStaffEntry} from "./VexFlowStaffEntry";
+import {Beam} from "../../VoiceData/Beam";
+import {GraphicalNote} from "../GraphicalNote";
+import {GraphicalStaffEntry} from "../GraphicalStaffEntry";
+import StaveConnector = Vex.Flow.StaveConnector;
+import StaveModifier = Vex.Flow.StaveModifier;
+import StaveNote = Vex.Flow.StaveNote;
+
+export class VexFlowMeasure extends StaffMeasure {
+    constructor(staff: Staff, staffLine: StaffLine = undefined, sourceMeasure: SourceMeasure = undefined) {
+        super(staff, sourceMeasure, staffLine);
+        this.minimumStaffEntriesWidth = -1;
+        this.resetLayout();
+    }
+
+    // octaveOffset according to active clef
+    public octaveOffset: number = 3;
+    // The VexFlow Voices in the measure
+    public vfVoices: { [voiceID: number]: Vex.Flow.Voice; } = {};
+    // Call this function (if present) to x-format all the voices in the measure
+    public formatVoices: (width: number) => void;
+    // The unit
+    public unit: number = 10.0;
+
+    // The VexFlow Stave (one measure in one line)
+    private stave: Vex.Flow.Stave;
+    // VexFlow StaveConnectors (vertical lines)
+    private connectors: Vex.Flow.StaveConnector[] = [];
+    // Intermediate object to construct beams
+    private beams: { [voiceID: number]: [Beam, VexFlowStaffEntry[]][]; } = {};
+    // VexFlow Beams
+    private vfbeams: { [voiceID: number]: Vex.Flow.Beam[]; } = {};
+
+    // Sets the absolute coordinates of the VFStave on the canvas
+    public setAbsoluteCoordinates(x: number, y: number): void {
+        this.stave.setX(x).setY(y);
+    }
+
+    /**
+     * Reset all the geometric values and parameters of this measure and put it in an initialized state.
+     * This is needed to evaluate a measure a second time by system builder.
+     */
+    public resetLayout(): void {
+        // Take into account some space for the begin and end lines of the stave
+        // Will be changed when repetitions will be implemented
+        this.beginInstructionsWidth = 20 / this.unit;
+        this.endInstructionsWidth = 20 / this.unit;
+        this.stave = new Vex.Flow.Stave(0, 0, 0);
+    }
+
+    public clean(): void {
+        //this.beams = {};
+        //this.vfbeams = {};
+        this.connectors = [];
+        console.log("clean!");
+    }
+
+    /**
+     * returns the x-width of a given measure line.
+     * @param line
+     * @returns {SystemLinesEnum} the x-width
+     */
+    public getLineWidth(line: SystemLinesEnum): number {
+        // FIXME: See values in VexFlow's stavebarline.js
+        let vfline: any = VexFlowConverter.line(line);
+        switch (vfline) {
+            case Vex.Flow.StaveConnector.type.SINGLE:
+                return 1.0 / this.unit;
+            case Vex.Flow.StaveConnector.type.DOUBLE:
+                return 3.0 / this.unit;
+            default:
+                return 0;
+        }
+    }
+
+    /**
+     * adds the given clef to the begin of the measure.
+     * This has to update/increase BeginInstructionsWidth.
+     * @param clef
+     */
+    public addClefAtBegin(clef: ClefInstruction): void {
+        this.octaveOffset = clef.OctaveOffset;
+        let vfclef: string = VexFlowConverter.Clef(clef);
+        this.stave.addClef(vfclef, undefined, undefined, Vex.Flow.Modifier.Position.BEGIN);
+        this.increaseBeginInstructionWidth();
+    }
+
+    /**
+     * adds the given key to the begin of the measure.
+     * This has to update/increase BeginInstructionsWidth.
+     * @param currentKey the new valid key.
+     * @param previousKey the old cancelled key. Needed to show which accidentals are not valid any more.
+     * @param currentClef the valid clef. Needed to put the accidentals on the right y-positions.
+     */
+    public addKeyAtBegin(currentKey: KeyInstruction, previousKey: KeyInstruction, currentClef: ClefInstruction): void {
+        let keySig: Vex.Flow.KeySignature = new Vex.Flow.KeySignature(
+            VexFlowConverter.keySignature(currentKey),
+            VexFlowConverter.keySignature(previousKey)
+        );
+        this.stave.addModifier(keySig, Vex.Flow.Modifier.Position.BEGIN);
+    }
+
+    /**
+     * adds the given rhythm to the begin of the measure.
+     * This has to update/increase BeginInstructionsWidth.
+     * @param rhythm
+     */
+    public addRhythmAtBegin(rhythm: RhythmInstruction): void {
+        let timeSig: Vex.Flow.TimeSignature = VexFlowConverter.TimeSignature(rhythm);
+        this.stave.addModifier(
+            timeSig,
+            Vex.Flow.Modifier.Position.BEGIN
+        );
+        this.increaseBeginInstructionWidth();
+    }
+
+    /**
+     * adds the given clef to the end of the measure.
+     * This has to update/increase EndInstructionsWidth.
+     * @param clef
+     */
+    public addClefAtEnd(clef: ClefInstruction): void {
+        let vfclef: string = VexFlowConverter.Clef(clef);
+        this.stave.setEndClef(vfclef, undefined, undefined);
+        this.increaseEndInstructionWidth();
+    }
+
+    /**
+     * Sets the overall x-width of the measure.
+     * @param width
+     */
+    public setWidth(width: number): void {
+        super.setWidth(width);
+        // Set the width of the Vex.Flow.Stave
+        this.stave.setWidth(width * this.unit);
+        // If this is the first stave in the vertical measure, call the format
+        // method to set the width of all the voices
+        if (this.formatVoices) {
+            // The width of the voices does not include the instructions (StaveModifiers)
+            this.formatVoices((width - this.beginInstructionsWidth - this.endInstructionsWidth) * this.unit);
+        }
+    }
+
+    /**
+     * This method is called after the StaffEntriesScaleFactor has been set.
+     * Here the final x-positions of the staff entries have to be set.
+     * (multiply the minimal positions with the scaling factor, considering the BeginInstructionsWidth)
+     */
+    public layoutSymbols(): void {
+        this.stave.format();
+    }
+
+    //public addGraphicalStaffEntry(entry: VexFlowStaffEntry): void {
+    //    super.addGraphicalStaffEntry(entry);
+    //}
+    //
+    //public addGraphicalStaffEntryAtTimestamp(entry: VexFlowStaffEntry): void {
+    //    super.addGraphicalStaffEntryAtTimestamp(entry);
+    //    // TODO
+    //}
+
+    /**
+     * Draw this measure on a VexFlow CanvasContext
+     * @param ctx
+     */
+    public draw(ctx: Vex.Flow.CanvasContext): void {
+        // Draw stave lines
+        this.stave.setContext(ctx).draw();
+        // Draw all voices
+        for (let voiceID in this.vfVoices) {
+            if (this.vfVoices.hasOwnProperty(voiceID)) {
+                this.vfVoices[voiceID].draw(ctx, this.stave);
+            }
+        }
+        // Draw beams
+        for (let voiceID in this.vfbeams) {
+            if (this.vfbeams.hasOwnProperty(voiceID)) {
+                for (let beam of this.vfbeams[voiceID]) {
+                    beam.setContext(ctx).draw();
+                }
+            }
+        }
+        // Draw vertical lines
+        for (let connector of this.connectors) {
+            connector.setContext(ctx).draw();
+        }
+    }
+
+    /**
+     * Add a note to a beam
+     * @param graphicalNote
+     * @param beam
+     */
+    public handleBeam(graphicalNote: GraphicalNote, beam: Beam): void {
+        let voiceID: number = graphicalNote.sourceNote.ParentVoiceEntry.ParentVoice.VoiceId;
+        let beams: [Beam, VexFlowStaffEntry[]][] = this.beams[voiceID];
+        if (beams === undefined) {
+            beams = this.beams[voiceID] = [];
+        }
+        let data: [Beam, VexFlowStaffEntry[]];
+        for (let mybeam of beams) {
+            if (mybeam[0] === beam) {
+                data = mybeam;
+            }
+        }
+        if (data === undefined) {
+            data = [beam, []];
+            beams.push(data);
+        }
+        let parent: VexFlowStaffEntry = graphicalNote.parentStaffEntry as VexFlowStaffEntry;
+        if (data[1].indexOf(parent) === -1) {
+            data[1].push(parent);
+        }
+    }
+
+    /**
+     * Complete the creation of VexFlow Beams in this measure
+     */
+    public finalizeBeams(): void {
+        for (let voiceID in this.beams) {
+            if (this.beams.hasOwnProperty(voiceID)) {
+                let vfbeams: Vex.Flow.Beam[] = this.vfbeams[voiceID];
+                if (vfbeams === undefined) {
+                    vfbeams = this.vfbeams[voiceID] = [];
+                }
+                for (let beam of this.beams[voiceID]) {
+                    let notes: Vex.Flow.StaveNote[] = [];
+                    for (let entry of beam[1]) {
+                        notes.push((<VexFlowStaffEntry>entry).vfNotes[voiceID]);
+                    }
+                    if (notes.length > 1) {
+                        vfbeams.push(new Vex.Flow.Beam(notes, true));
+                    } else {
+                        console.log("Warning! Beam with no notes! Trying to ignore, but this is a serious problem.");
+                    }
+                }
+            }
+        }
+    }
+
+    public layoutStaffEntry(graphicalStaffEntry: GraphicalStaffEntry): void {
+        let gnotes: { [voiceID: number]: GraphicalNote[]; } = (graphicalStaffEntry as VexFlowStaffEntry).graphicalNotes;
+        let vfVoices: { [voiceID: number]: Vex.Flow.Voice; } = this.vfVoices;
+        for (let voiceID in gnotes) {
+            if (gnotes.hasOwnProperty(voiceID)) {
+                if (!(voiceID in vfVoices)) {
+                    vfVoices[voiceID] = new Vex.Flow.Voice({
+                        beat_value: 4, //this.parentSourceMeasure.Duration.Denominator,
+                        num_beats: 3, //this.parentSourceMeasure.Duration.Numerator,
+                        resolution: Vex.Flow.RESOLUTION,
+                    }).setMode(Vex.Flow.Voice.Mode.SOFT);
+                }
+                let vfnote: StaveNote = VexFlowConverter.StaveNote(gnotes[voiceID]);
+                (graphicalStaffEntry as VexFlowStaffEntry).vfNotes[voiceID] = vfnote;
+                vfVoices[voiceID].addTickable(vfnote);
+            }
+        }
+    }
+
+    /**
+     * Creates a line from 'top' to this measure, of type 'lineType'
+     * @param top
+     * @param lineType
+     */
+    public lineTo(top: VexFlowMeasure, lineType: any): void {
+        let connector: StaveConnector = new Vex.Flow.StaveConnector(top.getVFStave(), this.stave);
+        connector.setType(lineType);
+        this.connectors.push(connector);
+    }
+
+    public getVFStave(): Vex.Flow.Stave {
+        return this.stave;
+    }
+
+    private increaseBeginInstructionWidth(): void {
+        let modifiers: StaveModifier[] = this.stave.getModifiers();
+        let modifier: StaveModifier = modifiers[modifiers.length - 1];
+        //let padding: number = modifier.getCategory() === "keysignatures" ? modifier.getPadding(2) : 0;
+        let padding: number = modifier.getPadding(20);
+        let width: number = modifier.getWidth();
+        this.beginInstructionsWidth += (padding + width) / this.unit;
+    }
+
+    private increaseEndInstructionWidth(): void {
+        let modifiers: StaveModifier[] = this.stave.getModifiers();
+        let modifier: StaveModifier = modifiers[modifiers.length - 1];
+        let padding: number = 0;
+        let width: number = modifier.getWidth();
+        this.endInstructionsWidth += (padding + width) / this.unit;
+    }
+}

+ 222 - 0
src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetCalculator.ts

@@ -0,0 +1,222 @@
+import {MusicSheetCalculator} from "../MusicSheetCalculator";
+import {VexFlowGraphicalSymbolFactory} from "./VexFlowGraphicalSymbolFactory";
+import {StaffMeasure} from "../StaffMeasure";
+import {StaffLine} from "../StaffLine";
+import {VoiceEntry} from "../../VoiceData/VoiceEntry";
+import {MusicSystem} from "../MusicSystem";
+import {GraphicalNote} from "../GraphicalNote";
+import {GraphicalStaffEntry} from "../GraphicalStaffEntry";
+import {GraphicalMusicPage} from "../GraphicalMusicPage";
+import {GraphicalTie} from "../GraphicalTie";
+import {Tie} from "../../VoiceData/Tie";
+import {SourceMeasure} from "../../VoiceData/SourceMeasure";
+import {MultiExpression} from "../../VoiceData/Expressions/multiExpression";
+import {RepetitionInstruction} from "../../VoiceData/Instructions/RepetitionInstruction";
+import {Beam} from "../../VoiceData/Beam";
+import {ClefInstruction} from "../../VoiceData/Instructions/ClefInstruction";
+import {OctaveEnum} from "../../VoiceData/Expressions/ContinuousExpressions/octaveShift";
+import {Fraction} from "../../../Common/DataObjects/fraction";
+import {LyricsEntry} from "../../VoiceData/Lyrics/LyricsEntry";
+import {LyricWord} from "../../VoiceData/Lyrics/LyricsWord";
+import {OrnamentContainer} from "../../VoiceData/OrnamentContainer";
+import {ArticulationEnum} from "../../VoiceData/VoiceEntry";
+import {Tuplet} from "../../VoiceData/Tuplet";
+import Dictionary from "typescript-collections/dist/lib/Dictionary";
+import {VexFlowMeasure} from "./VexFlowMeasure";
+import {VexFlowTextMeasurer} from "./VexFlowTextMeasurer";
+
+import Vex = require("vexflow");
+
+export class VexFlowMusicSheetCalculator extends MusicSheetCalculator {
+    constructor() {
+        super(new VexFlowGraphicalSymbolFactory());
+        MusicSheetCalculator.TextMeasurer = new VexFlowTextMeasurer();
+    }
+
+    protected clearRecreatedObjects(): void {
+        super.clearRecreatedObjects();
+        for (let staffMeasures of this.graphicalMusicSheet.MeasureList) {
+            for (let staffMeasure of staffMeasures) {
+                (<VexFlowMeasure>staffMeasure).clean();
+            }
+        }
+    }
+
+    //protected clearSystemsAndMeasures(): void {
+    //    for (let measure of measures) {
+    //
+    //    }
+    //}
+
+    /**
+     * Calculates the x layout of the staff entries within the staff measures belonging to one source measure.
+     * All staff entries are x-aligned throughout all vertically aligned staff measures.
+     * This method is called within calculateXLayout.
+     * The staff entries are aligned with minimum needed x distances.
+     * The MinimumStaffEntriesWidth of every measure will be set - needed for system building.
+     * @param measures
+     * @returns the minimum required x width of the source measure (=list of staff measures)
+     */
+    protected calculateMeasureXLayout(measures: StaffMeasure[]): number {
+        // Finalize beams
+        for (let measure of measures) {
+            (measure as VexFlowMeasure).finalizeBeams();
+        }
+        // Format the voices
+        let allVoices: Vex.Flow.Voice[] = [];
+        let formatter: Vex.Flow.Formatter = new Vex.Flow.Formatter();
+        for (let measure of measures) {
+            let mvoices:  { [voiceID: number]: Vex.Flow.Voice; } = (measure as VexFlowMeasure).vfVoices;
+            let voices: Vex.Flow.Voice[] = [];
+            for (let voiceID in mvoices) {
+                if (mvoices.hasOwnProperty(voiceID)) {
+                    voices.push(mvoices[voiceID]);
+                    allVoices.push(mvoices[voiceID]);
+                }
+            }
+            if (voices.length === 0) {
+                console.warn("Found a measure with no voices... Continuing anyway.", mvoices);
+                continue;
+            }
+            formatter.joinVoices(voices);
+        }
+        let firstMeasure: VexFlowMeasure = measures[0] as VexFlowMeasure;
+        let width: number = formatter.preCalculateMinTotalWidth(allVoices) / firstMeasure.unit;
+        for (let measure of measures) {
+            measure.minimumStaffEntriesWidth = width;
+            (measure as VexFlowMeasure).formatVoices = undefined;
+        }
+        firstMeasure.formatVoices = (w: number) => {
+            formatter.format(allVoices, w);
+        };
+        return width;
+    }
+
+    protected updateStaffLineBorders(staffLine: StaffLine): void {
+        return;
+    }
+
+    protected calculateMeasureNumberPlacement(musicSystem: MusicSystem): void {
+        return;
+    }
+
+    /**
+     * Can be used to calculate stem directions, helper(ledger) lines, and overlapping note x-displacement.
+     * Is Excecuted per voice entry of a staff entry.
+     * After that layoutStaffEntry is called.
+     * @param voiceEntry
+     * @param graphicalNotes
+     * @param graphicalStaffEntry
+     * @param hasPitchedNote
+     * @param isGraceStaffEntry
+     */
+    protected layoutVoiceEntry(voiceEntry: VoiceEntry, graphicalNotes: GraphicalNote[], graphicalStaffEntry: GraphicalStaffEntry,
+                               hasPitchedNote: boolean, isGraceStaffEntry: boolean): void {
+        return;
+    }
+
+    /**
+     * Do all layout calculations that have to be done per staff entry, like dots, ornaments, arpeggios....
+     * This method is called after the voice entries are handled by layoutVoiceEntry().
+     * @param graphicalStaffEntry
+     */
+    protected layoutStaffEntry(graphicalStaffEntry: GraphicalStaffEntry): void {
+        (graphicalStaffEntry.parentMeasure as VexFlowMeasure).layoutStaffEntry(graphicalStaffEntry);
+    }
+
+    /**
+     * calculates the y positions of the staff lines within a system and
+     * furthermore the y positions of the systems themselves.
+     */
+    protected calculateSystemYLayout(): void {
+        for (let idx: number = 0, len: number = this.graphicalMusicSheet.MusicPages.length; idx < len; ++idx) {
+            let graphicalMusicPage: GraphicalMusicPage = this.graphicalMusicSheet.MusicPages[idx];
+            if (!this.leadSheet) {
+                let globalY: number = 0;
+                for (let idx2: number = 0, len2: number = graphicalMusicPage.MusicSystems.length; idx2 < len2; ++idx2) {
+                    let musicSystem: MusicSystem = graphicalMusicPage.MusicSystems[idx2];
+                    // calculate y positions of stafflines within system
+                    let y: number = 0;
+                    for (let line of musicSystem.StaffLines) {
+                        line.PositionAndShape.RelativePosition.y = y;
+                        y += 10;
+                    }
+                    // set y positions of systems using the previous system and a fixed distance.
+                    musicSystem.PositionAndShape.BorderBottom = y + 10;
+                    musicSystem.PositionAndShape.RelativePosition.y = globalY;
+                    globalY += y + 10;
+                }
+            }
+        }
+    }
+
+    /**
+     * Is called at the begin of the method for creating the vertically aligned staff measures belonging to one source measure.
+     */
+    protected initStaffMeasuresCreation(): void {
+        return;
+    }
+
+    protected handleTie(tie: Tie, startGraphicalStaffEntry: GraphicalStaffEntry, staffIndex: number, measureIndex: number): void {
+        return;
+    }
+
+    protected layoutGraphicalTie(tie: GraphicalTie, tieIsAtSystemBreak: boolean): void {
+        return;
+    }
+
+    protected calculateSingleStaffLineLyricsPosition(staffLine: StaffLine, lyricVersesNumber: number[]): void {
+        return;
+    }
+
+    protected calculateSingleOctaveShift(sourceMeasure: SourceMeasure, multiExpression: MultiExpression, measureIndex: number, staffIndex: number): void {
+        return;
+    }
+
+    protected calculateWordRepetitionInstruction(repetitionInstruction: RepetitionInstruction, measureIndex: number): void {
+        return;
+    }
+
+    protected calculateMoodAndUnknownExpression(multiExpression: MultiExpression, measureIndex: number, staffIndex: number): void {
+        return;
+    }
+
+    protected createGraphicalTieNote(beams: Beam[], activeClef: ClefInstruction,
+                                     octaveShiftValue: OctaveEnum, graphicalStaffEntry: GraphicalStaffEntry, duration: Fraction, numberOfDots: number,
+                                     openTie: Tie, isLastTieNote: boolean): void {
+        return;
+    }
+
+    /**
+     * Is called if a note is part of a beam.
+     * @param graphicalNote
+     * @param beam
+     * @param openBeams a list of all currently open beams
+     */
+    protected handleBeam(graphicalNote: GraphicalNote, beam: Beam, openBeams: Beam[]): void {
+        (graphicalNote.parentStaffEntry.parentMeasure as VexFlowMeasure).handleBeam(graphicalNote, beam);
+    }
+
+    protected handleVoiceEntryLyrics(lyricsEntries: Dictionary<number, LyricsEntry>, voiceEntry: VoiceEntry,
+                                     graphicalStaffEntry: GraphicalStaffEntry, openLyricWords: LyricWord[]): void {
+        return;
+    }
+
+    protected handleVoiceEntryOrnaments(ornamentContainer: OrnamentContainer, voiceEntry: VoiceEntry, graphicalStaffEntry: GraphicalStaffEntry): void {
+        return;
+    }
+
+    protected handleVoiceEntryArticulations(articulations: ArticulationEnum[], voiceEntry: VoiceEntry, graphicalStaffEntry: GraphicalStaffEntry): void {
+        return;
+    }
+
+    /**
+     * Is called if a note is part of a tuplet.
+     * @param graphicalNote
+     * @param tuplet
+     * @param openTuplets a list of all currently open tuplets
+     */
+    protected handleTuplet(graphicalNote: GraphicalNote, tuplet: Tuplet, openTuplets: Tuplet[]): void {
+        return;
+    }
+}

+ 100 - 0
src/MusicalScore/Graphical/VexFlow/VexFlowMusicSheetDrawer.ts

@@ -0,0 +1,100 @@
+import Vex = require("vexflow");
+import {MusicSheetDrawer} from "../MusicSheetDrawer";
+import {RectangleF2D} from "../../../Common/DataObjects/RectangleF2D";
+import {VexFlowMeasure} from "./VexFlowMeasure";
+import {ITextMeasurer} from "../../Interfaces/ITextMeasurer";
+import {PointF2D} from "../../../Common/DataObjects/PointF2D";
+import {GraphicalLabel} from "../GraphicalLabel";
+/**
+ * Created by Matthias on 22.06.2016.
+ */
+export class VexFlowMusicSheetDrawer extends MusicSheetDrawer {
+    private renderer: Vex.Flow.Renderer;
+    private ctx: Vex.Flow.CanvasContext;
+
+    constructor(canvas: HTMLCanvasElement, textMeasurer: ITextMeasurer, isPreviewImageDrawer: boolean = false) {
+        super(textMeasurer, isPreviewImageDrawer);
+        this.renderer = new Vex.Flow.Renderer(canvas, Vex.Flow.Renderer.Backends.CANVAS);
+        this.ctx = this.renderer.getContext();
+    }
+
+    public scale(k: number): void {
+        this.ctx.scale(k, k);
+    }
+
+    public resize(x: number, y: number): void {
+        this.renderer.resize(x, y);
+    }
+
+    public translate(x: number, y: number): void {
+        // FIXME
+        (this.ctx as any).vexFlowCanvasContext.translate(x, y);
+    }
+
+    /**
+     * Converts a distance from unit to pixel space.
+     * @param unitDistance the distance in units
+     * @returns {number} the distance in pixels
+     */
+    public calculatePixelDistance(unitDistance: number): number {
+        // ToDo: implement!
+        return unitDistance * 10.0;
+    }
+
+    protected drawMeasure(measure: VexFlowMeasure): void {
+        measure.setAbsoluteCoordinates(
+            measure.PositionAndShape.AbsolutePosition.x * (measure as VexFlowMeasure).unit,
+            measure.PositionAndShape.AbsolutePosition.y * (measure as VexFlowMeasure).unit
+        );
+        return measure.draw(this.ctx);
+    }
+
+    /**
+     * Renders a Label to the screen (e.g. Title, composer..)
+     * @param graphicalLabel holds the label string, the text height in units and the font parameters
+     * @param layer is the current rendering layer. There are many layers on top of each other to which can be rendered. Not needed for now.
+     * @param bitmapWidth Not needed for now.
+     * @param bitmapHeight Not needed for now.
+     * @param heightInPixel the height of the text in screen coordinates
+     * @param screenPosition the position of the lower left corner of the text in screen coordinates
+     */
+    protected renderLabel(graphicalLabel: GraphicalLabel, layer: number, bitmapWidth: number,
+                          bitmapHeight: number, heightInPixel: number, screenPosition: PointF2D): void {
+        // ToDo: implement!
+        let ctx: CanvasRenderingContext2D = (this.ctx as any).vexFlowCanvasContext;
+        ctx.font = Math.floor(graphicalLabel.Label.fontHeight * 10) + "px 'Times New Roman'";
+        console.log(graphicalLabel.Label.text, screenPosition.x, screenPosition.y);
+        ctx.fillText(graphicalLabel.Label.text, screenPosition.x, screenPosition.y);
+    }
+
+    /**
+     * Renders a rectangle with the given style to the screen.
+     * It is given in screen coordinates.
+     * @param rectangle the rect in screen coordinates
+     * @param layer is the current rendering layer. There are many layers on top of each other to which can be rendered. Not needed for now.
+     * @param styleId the style id
+     */
+    protected renderRectangle(rectangle: RectangleF2D, layer: number, styleId: number): void {
+        // ToDo: implement!
+    }
+
+    /**
+     * Converts a point from unit to pixel space.
+     * @param point
+     * @returns {PointF2D}
+     */
+    protected applyScreenTransformation(point: PointF2D): PointF2D {
+        // ToDo: implement!
+        return new PointF2D(point.x * 10.0, point.y * 10.0);
+    }
+
+    /**
+     * Converts a rectangle from unit to pixel space.
+     * @param rectangle
+     * @returns {RectangleF2D}
+     */
+    protected applyScreenTransformationForRect(rectangle: RectangleF2D): RectangleF2D {
+        // FIXME Check if correct
+        return new RectangleF2D(rectangle.x * 10, rectangle.y * 10, rectangle.width * 10, rectangle.height * 10);
+    }
+}

+ 68 - 0
src/MusicalScore/Graphical/VexFlow/VexFlowMusicSystem.ts

@@ -0,0 +1,68 @@
+import {MusicSystem} from "../MusicSystem";
+import {GraphicalMusicPage} from "../GraphicalMusicPage";
+import {SystemLinesEnum} from "../SystemLinesEnum";
+import {PointF2D} from "../../../Common/DataObjects/PointF2D";
+import {SystemLinePosition} from "../SystemLinePosition";
+import {StaffMeasure} from "../StaffMeasure";
+import {SystemLine} from "../SystemLine";
+import {VexFlowMeasure} from "./VexFlowMeasure";
+import {VexFlowConverter} from "./VexFlowConverter";
+
+//import Vex = require("vexflow");
+
+export class VexFlowMusicSystem extends MusicSystem {
+    constructor(parent: GraphicalMusicPage, id: number) {
+        super(parent, id);
+
+    }
+
+    /**
+     * This method creates all the graphical lines and dots needed to render a system line (e.g. bold-thin-dots..).
+     * @param xPosition
+     * @param lineWidth
+     * @param lineType
+     * @param linePosition indicates if the line belongs to start or end of measure
+     * @param musicSystem
+     * @param topMeasure
+     * @param bottomMeasure
+     */
+    protected createSystemLine(xPosition: number, lineWidth: number, lineType: SystemLinesEnum, linePosition: SystemLinePosition,
+                               musicSystem: MusicSystem, topMeasure: StaffMeasure, bottomMeasure: StaffMeasure = undefined): SystemLine {
+        // ToDo: create line in Vexflow
+        if (bottomMeasure) {
+            (bottomMeasure as VexFlowMeasure).lineTo(topMeasure as VexFlowMeasure, VexFlowConverter.line(lineType));
+        }
+        return new SystemLine(lineType, linePosition, this, topMeasure, bottomMeasure);
+    }
+
+    /**
+     * Calculates the summed x-width of a possibly given Instrument Brace and/or Group Bracket(s).
+     * @returns {number} the x-width
+     */
+    protected calcInstrumentsBracketsWidth(): number {
+        return 0;
+    }
+
+    /**
+     * creates an instrument brace for the given dimension.
+     * The height and positioning can be inferred from the given points.
+     * @param rightUpper the upper right corner point of the bracket to create
+     * @param rightLower the lower right corner point of the bracket to create
+     */
+    protected createInstrumentBracket(rightUpper: PointF2D, rightLower: PointF2D): void {
+        return;
+    }
+
+    /**
+     * creates an instrument group bracket for the given dimension.
+     * There can be cascaded bracket (e.g. a group of 2 in a group of 4) -
+     * The recursion depth informs about the current depth level (needed for positioning)
+     * @param rightUpper rightUpper the upper right corner point of the bracket to create
+     * @param rightLower rightLower the lower right corner point of the bracket to create
+     * @param staffHeight
+     * @param recursionDepth
+     */
+    protected createGroupBracket(rightUpper: PointF2D, rightLower: PointF2D, staffHeight: number, recursionDepth: number): void {
+        return;
+    }
+}

+ 15 - 0
src/MusicalScore/Graphical/VexFlow/VexFlowStaffEntry.ts

@@ -0,0 +1,15 @@
+import {GraphicalStaffEntry} from "../GraphicalStaffEntry";
+import {VexFlowMeasure} from "./VexFlowMeasure";
+import {SourceStaffEntry} from "../../VoiceData/SourceStaffEntry";
+import {GraphicalNote} from "../GraphicalNote";
+
+export class VexFlowStaffEntry extends GraphicalStaffEntry {
+    constructor(measure: VexFlowMeasure, sourceStaffEntry: SourceStaffEntry, staffEntryParent: VexFlowStaffEntry) {
+        super(measure, sourceStaffEntry, staffEntryParent);
+    }
+
+    // The Graphical Notes belonging to this StaffEntry, sorted by voiceID
+    public graphicalNotes: { [voiceID: number]: GraphicalNote[]; } = {};
+    // The corresponding VexFlow.StaveNotes
+    public vfNotes: { [voiceID: number]: Vex.Flow.StaveNote; } = {};
+}

+ 10 - 0
src/MusicalScore/Graphical/VexFlow/VexFlowStaffLine.ts

@@ -0,0 +1,10 @@
+import {StaffLine} from "../StaffLine";
+import {MusicSystem} from "../MusicSystem";
+import {Staff} from "../../VoiceData/Staff";
+
+export class VexFlowStaffLine extends StaffLine {
+    constructor(parentSystem: MusicSystem, parentStaff: Staff) {
+        super(parentSystem, parentStaff);
+
+    }
+}

+ 20 - 0
src/MusicalScore/Graphical/VexFlow/VexFlowTextMeasurer.ts

@@ -0,0 +1,20 @@
+import {ITextMeasurer} from "../../Interfaces/ITextMeasurer";
+import {Fonts} from "../../../Common/Enums/Fonts";
+import {FontStyles} from "../../../Common/Enums/FontStyles";
+/**
+ * Created by Matthias on 21.06.2016.
+ */
+
+export class VexFlowTextMeasurer implements ITextMeasurer {
+    constructor() {
+        let canvas: HTMLCanvasElement = document.createElement("canvas");
+        this.context = canvas.getContext("2d");
+        this.context.font = "20px 'Times New Roman'";
+    }
+    private context: CanvasRenderingContext2D;
+
+    public computeTextWidthToHeightRatio(text: string, font: Fonts, style: FontStyles): number {
+        let size: any = this.context.measureText(text);
+        return size.width / 20;
+    }
+}

+ 241 - 0
src/MusicalScore/Instrument.ts

@@ -0,0 +1,241 @@
+import { InstrumentalGroup } from "./InstrumentalGroup";
+import { Label } from "./Label";
+import { MusicSheet } from "./MusicSheet";
+import { Voice } from "./VoiceData/Voice";
+import { Staff } from "./VoiceData/Staff";
+import { SubInstrument } from "./SubInstrument";
+import { MidiInstrument } from "./VoiceData/Instructions/ClefInstruction";
+
+export class Instrument extends InstrumentalGroup {
+    constructor(id: number, idString: string, musicSheet: MusicSheet, parent: InstrumentalGroup) {
+        super(undefined, musicSheet, parent);
+        this.id = id;
+        this.idString = idString;
+        this.nameLabel = new Label(idString);
+    }
+
+    public transpose: number = 0;
+    public highlight: boolean;
+
+    private voices: Voice[] = [];
+    private staves: Staff[] = [];
+    private nameLabel: Label;
+    private idString: string;
+    private id: number;
+    private hasLyrics: boolean = false;
+    private hasChordSymbols: boolean = false;
+    private playbackTranspose: number;
+
+    private lyricVersesNumbers: number[] = [];
+    private subInstruments: SubInstrument[] = [];
+
+    public get Voices(): Voice[] {
+        return this.voices;
+    }
+    public get Staves(): Staff[] {
+        return this.staves;
+    }
+    public get NameLabel(): Label {
+        return this.nameLabel;
+    }
+    public get HasLyrics(): boolean {
+        return this.hasLyrics;
+    }
+    public set HasLyrics(value: boolean) {
+        this.hasLyrics = value;
+    }
+    public get HasChordSymbols(): boolean {
+        return this.hasChordSymbols;
+    }
+    public set HasChordSymbols(value: boolean) {
+        this.hasChordSymbols = value;
+    }
+    public get LyricVersesNumbers(): number[] {
+        return this.lyricVersesNumbers;
+    }
+    public set LyricVersesNumbers(value: number[]) {
+        this.lyricVersesNumbers = value;
+    }
+    public get Name(): string {
+        return this.nameLabel.text;
+    }
+    public set Name(value: string) {
+        this.nameLabel.text = value;
+    }
+    public get IdString(): string {
+        return this.idString;
+    }
+    public get Id(): number {
+        return this.id;
+    }
+    public get MidiInstrumentId(): MidiInstrument {
+        return this.subInstruments[0].midiInstrumentID;
+    }
+    public set MidiInstrumentId(value: MidiInstrument) {
+        this.subInstruments[0].midiInstrumentID = value;
+    }
+    public get Volume(): number {
+        return this.subInstruments[0].volume;
+    }
+    public set Volume(value: number) {
+        for (let idx: number = 0, len: number = this.subInstruments.length; idx < len; ++idx) {
+            let subInstrument: SubInstrument = this.subInstruments[idx];
+            subInstrument.volume = value;
+        }
+    }
+    public get PlaybackTranspose(): number {
+        return this.playbackTranspose;
+    }
+    public set PlaybackTranspose(value: number) {
+        this.playbackTranspose = value;
+    }
+
+    public get SubInstruments(): SubInstrument[] {
+        return this.subInstruments;
+    }
+    public getSubInstrument(subInstrumentIdString: string): SubInstrument {
+        for (let idx: number = 0, len: number = this.subInstruments.length; idx < len; ++idx) {
+            let subInstrument: SubInstrument = this.subInstruments[idx];
+            if (subInstrument.idString === subInstrumentIdString) {
+                return subInstrument;
+            }
+        }
+        return undefined;
+    }
+    public get Visible(): boolean {
+        if (this.voices.length > 0) {
+            return this.Voices[0].Visible;
+        } else {
+            return false;
+        }
+    }
+    public set Visible(value: boolean) {
+        for (let idx: number = 0, len: number = this.Voices.length; idx < len; ++idx) {
+            let v: Voice = this.Voices[idx];
+            v.Visible = value;
+        }
+    }
+    public get Audible(): boolean {
+        let result: boolean = false;
+        for (let idx: number = 0, len: number = this.Voices.length; idx < len; ++idx) {
+            let v: Voice = this.Voices[idx];
+            result = result || v.Audible;
+        }
+        return result;
+    }
+    public set Audible(value: boolean) {
+        for (let idx: number = 0, len: number = this.Voices.length; idx < len; ++idx) {
+            let v: Voice = this.Voices[idx];
+            v.Audible = value;
+        }
+        for (let idx: number = 0, len: number = this.staves.length; idx < len; ++idx) {
+            let staff: Staff = this.staves[idx];
+            staff.audible = value;
+        }
+    }
+    public get Following(): boolean {
+        let result: boolean = false;
+        for (let idx: number = 0, len: number = this.Voices.length; idx < len; ++idx) {
+            let v: Voice = this.Voices[idx];
+            result = result || v.Following;
+        }
+        return result;
+    }
+    public set Following(value: boolean) {
+        for (let idx: number = 0, len: number = this.Voices.length; idx < len; ++idx) {
+            let v: Voice = this.Voices[idx];
+            v.Following = value;
+        }
+        for (let idx: number = 0, len: number = this.staves.length; idx < len; ++idx) {
+            let staff: Staff = this.staves[idx];
+            staff.following = value;
+        }
+    }
+    public SetVoiceAudible(voiceId: number, audible: boolean): void {
+        for (let idx: number = 0, len: number = this.Voices.length; idx < len; ++idx) {
+            let v: Voice = this.Voices[idx];
+            if (v.VoiceId === voiceId) {
+                v.Audible = audible;
+                break;
+            }
+        }
+    }
+    public SetVoiceFollowing(voiceId: number, following: boolean): void {
+        for (let idx: number = 0, len: number = this.Voices.length; idx < len; ++idx) {
+            let v: Voice = this.Voices[idx];
+            if (v.VoiceId === voiceId) {
+                v.Following = following;
+                break;
+            }
+        }
+    }
+    public SetStaffAudible(staffId: number, audible: boolean): void {
+        let staff: Staff = this.staves[staffId - 1];
+        staff.audible = audible;
+        if (audible) {
+            for (let idx: number = 0, len: number = staff.Voices.length; idx < len; ++idx) {
+                let v: Voice = staff.Voices[idx];
+                v.Audible = true;
+            }
+        } else {
+            for (let idx: number = 0, len: number = staff.Voices.length; idx < len; ++idx) {
+                let voice: Voice = staff.Voices[idx];
+                let isAudibleInOtherStaves: boolean = false;
+                for (let idx2: number = 0, len2: number = this.Staves.length; idx2 < len2; ++idx2) {
+                    let st: Staff = this.Staves[idx2];
+                    if (st.Id === staffId || !st.audible) { continue; }
+                    for (let idx3: number = 0, len3: number = st.Voices.length; idx3 < len3; ++idx3) {
+                        let v: Voice = st.Voices[idx3];
+                        if (v === voice) {
+                            isAudibleInOtherStaves = true;
+                        }
+                    }
+                }
+                if (!isAudibleInOtherStaves) {
+                    voice.Audible = false;
+                }
+            }
+        }
+    }
+    public SetStaffFollow(staffId: number, follow: boolean): void {
+        let staff: Staff = this.staves[staffId - 1];
+        staff.following = follow;
+        if (follow) {
+            for (let idx: number = 0, len: number = staff.Voices.length; idx < len; ++idx) {
+                let v: Voice = staff.Voices[idx];
+                v.Following = true;
+            }
+        } else {
+            for (let idx: number = 0, len: number = staff.Voices.length; idx < len; ++idx) {
+                let voice: Voice = staff.Voices[idx];
+                let isFollowingInOtherStaves: boolean = false;
+                for (let idx2: number = 0, len2: number = this.Staves.length; idx2 < len2; ++idx2) {
+                    let st: Staff = this.Staves[idx2];
+                    if (st.Id === staffId || !st.following) { continue; }
+                    for (let idx3: number = 0, len3: number = st.Voices.length; idx3 < len3; ++idx3) {
+                        let v: Voice = st.Voices[idx3];
+                        if (v === voice) {
+                            isFollowingInOtherStaves = true;
+                        }
+                    }
+                }
+                if (!isFollowingInOtherStaves) {
+                    voice.Following = false;
+                }
+            }
+        }
+    }
+    public areAllVoiceVisible(): boolean {
+        for (let voice of this.Voices) {
+            if (!voice.Visible) {
+                return false;
+            }
+        }
+        return true;
+    }
+    public createStaves(numberOfStaves: number): void {
+        for (let i: number = 0; i < numberOfStaves; i++) {
+            this.staves.push(new Staff(this, i + 1));
+        }
+    }
+}

+ 28 - 0
src/MusicalScore/InstrumentalGroup.ts

@@ -0,0 +1,28 @@
+import { MusicSheet } from "./MusicSheet";
+
+export class InstrumentalGroup {
+    constructor(name: string, musicSheet: MusicSheet, parent: InstrumentalGroup) {
+        this.name = name;
+        this.musicSheet = musicSheet;
+        this.parent = parent;
+    }
+    private name: string;
+    private musicSheet: MusicSheet;
+    private parent: InstrumentalGroup;
+    private instrumentalGroups: InstrumentalGroup[] = [];
+    public get InstrumentalGroups(): InstrumentalGroup[] {
+        return this.instrumentalGroups;
+    }
+    public get Parent(): InstrumentalGroup {
+        return this.parent;
+    }
+    public get Name(): string {
+        return this.name;
+    }
+    public set Name(value: string) {
+        this.name = value;
+    }
+    public get GetMusicSheet(): MusicSheet {
+        return this.musicSheet;
+    }
+}

+ 33 - 0
src/MusicalScore/Interfaces/IGraphicalSymbolFactory.ts

@@ -0,0 +1,33 @@
+import {GraphicalMusicPage} from "../Graphical/GraphicalMusicPage";
+import {MusicSystem} from "../Graphical/MusicSystem";
+import {Staff} from "../VoiceData/Staff";
+import {StaffLine} from "../Graphical/StaffLine";
+import {SourceMeasure} from "../VoiceData/SourceMeasure";
+import {StaffMeasure} from "../Graphical/StaffMeasure";
+import {SourceStaffEntry} from "../VoiceData/SourceStaffEntry";
+import {GraphicalStaffEntry} from "../Graphical/GraphicalStaffEntry";
+import {Note} from "../VoiceData/Note";
+import {ClefInstruction} from "../VoiceData/Instructions/ClefInstruction";
+import {OctaveEnum} from "../VoiceData/Expressions/ContinuousExpressions/octaveShift";
+import {GraphicalNote} from "../Graphical/GraphicalNote";
+import {Pitch} from "../../Common/DataObjects/pitch";
+import {TechnicalInstruction} from "../VoiceData/Instructions/TechnicalInstruction";
+export interface IGraphicalSymbolFactory {
+    createMusicSystem(page: GraphicalMusicPage, systemIndex: number): MusicSystem;
+    createStaffLine(parentSystem: MusicSystem, parentStaff: Staff): StaffLine;
+    createStaffMeasure(sourceMeasure: SourceMeasure, staff: Staff): StaffMeasure;
+    createExtraStaffMeasure(staffLine: StaffLine): StaffMeasure;
+    createStaffEntry(sourceStaffEntry: SourceStaffEntry, measure: StaffMeasure): GraphicalStaffEntry;
+    createGraceStaffEntry(staffEntryParent: GraphicalStaffEntry, measure: StaffMeasure): GraphicalStaffEntry;
+    createNote(note: Note, numberOfDots: number, graphicalStaffEntry: GraphicalStaffEntry, activeClef: ClefInstruction,
+        octaveShift: OctaveEnum): GraphicalNote;
+    createGraceNote(note: Note, numberOfDots: number, graphicalStaffEntry: GraphicalStaffEntry, activeClef: ClefInstruction,
+        octaveShift: OctaveEnum): GraphicalNote;
+    addGraphicalAccidental(graphicalNote: GraphicalNote, pitch: Pitch, grace: boolean, graceScalingFactor: number): void;
+    addFermataAtTiedEndNote(tiedNote: Note, graphicalStaffEntry: GraphicalStaffEntry): void;
+    createGraphicalTechnicalInstruction(technicalInstruction: TechnicalInstruction,
+        graphicalStaffEntry: GraphicalStaffEntry): void;
+    createInStaffClef(graphicalStaffEntry: GraphicalStaffEntry, clefInstruction: ClefInstruction): void;
+    createChordSymbol(sourceStaffEntry: SourceStaffEntry, graphicalStaffEntry: GraphicalStaffEntry,
+        transposeHalftones: number): void;
+}

+ 8 - 0
src/MusicalScore/Interfaces/IQualityFeedbackTone.ts

@@ -0,0 +1,8 @@
+import { Note } from "../VoiceData/Note";
+
+export interface IQualityFeedbackTone {
+    ParentNote: Note;
+    TimingScore: number;
+    PitchScore: number;
+    getOverallQualityFeedbackScore(): number;
+}

+ 6 - 0
src/MusicalScore/Interfaces/ITextMeasurer.ts

@@ -0,0 +1,6 @@
+import {Fonts} from "../../Common/Enums/Fonts";
+import {FontStyles} from "../../Common/Enums/FontStyles";
+
+export interface ITextMeasurer {
+    computeTextWidthToHeightRatio(text: string, font: Fonts, style: FontStyles): number;
+}

+ 13 - 0
src/MusicalScore/Interfaces/ITextTranslation.ts

@@ -0,0 +1,13 @@
+export class ITextTranslation {
+    public static defaultTextTranslation: ITextTranslation;
+
+    public static translateText(tag: string, text: string): string {
+        if (this.defaultTextTranslation === undefined) {
+            return text;
+        }
+
+        //return this.DefaultTextTranslation.translate(tag, text);
+    }
+
+    //declare public translate(tag: string, text: string): string;
+}

+ 8 - 0
src/MusicalScore/Interfaces/ITransposeCalculator.ts

@@ -0,0 +1,8 @@
+import {Pitch} from "../../Common/DataObjects/pitch";
+import {KeyInstruction} from "../VoiceData/Instructions/KeyInstruction";
+
+export interface ITransposeCalculator {
+    transposePitch(pitch: Pitch, currentKeyInstruction: KeyInstruction, halftones: number): Pitch;
+    transposeKey(keyInstruction: KeyInstruction, transpose: number): void;
+    getTransposedKeyString(keyInstruction: KeyInstruction, halftone: number): string;
+}

+ 21 - 0
src/MusicalScore/Label.ts

@@ -0,0 +1,21 @@
+import {TextAlignment} from "../Common/Enums/TextAlignment";
+import {OSMDColor} from "../Common/DataObjects/osmdColor";
+import {Fonts} from "../Common/Enums/Fonts";
+import {FontStyles} from "../Common/Enums/FontStyles";
+
+export class Label {
+    constructor(text: string = "", alignment: TextAlignment = TextAlignment.LeftBottom, font: Fonts = Fonts.TimesNewRoman) {
+        this.text = text;
+        this.textAlignment = alignment;
+        this.font = font;
+    }
+    public text: string;
+    public color: OSMDColor;
+    public font: Fonts;
+    public fontStyle: FontStyles;
+    public fontHeight: number;
+    public textAlignment: TextAlignment;
+    public ToString(): string {
+        return this.text;
+    }
+}

+ 120 - 0
src/MusicalScore/MusicParts/MusicPartManager.ts

@@ -0,0 +1,120 @@
+import { MusicSheet } from "../MusicSheet";
+import { PartListEntry } from "../MusicSource/PartListEntry";
+import { Repetition } from "../MusicSource/Repetition";
+import { Fraction } from "../../Common/DataObjects/fraction";
+import { MusicPartManagerIterator } from "./MusicPartManagerIterator";
+
+export class MusicPartManager /*implements ISelectionListener*/ {
+    constructor(musicSheet: MusicSheet) {
+        this.musicSheet = musicSheet;
+    }
+    private parts: PartListEntry[];
+    private timestamps: TimestampTransform[];
+    private musicSheet: MusicSheet;
+    private sheetStart: Fraction;
+    private sheetEnd: Fraction;
+    public reInit(): void {
+        this.init();
+    }
+    public init(): void {
+        this.parts = this.musicSheet.Repetitions.slice();
+        this.sheetStart = this.musicSheet.SelectionStart = new Fraction(0, 1);
+        this.sheetEnd = this.musicSheet.SelectionEnd = this.musicSheet.SheetEndTimestamp;
+        this.calcMapping();
+    }
+    public getCurrentRepetitionTimestampTransform(curEnrolledTimestamp: Fraction): TimestampTransform {
+        let curTransform: TimestampTransform = undefined;
+        for (let i: number = this.timestamps.length - 1; i >= 0; i--) {
+            curTransform = this.timestamps[i];
+            if (curEnrolledTimestamp >= curTransform.$from) {
+                return curTransform;
+            }
+        }
+        return this.timestamps[0];
+    }
+    public absoluteEnrolledToSheetTimestamp(timestamp: Fraction): Fraction {
+        if (this.timestamps.length === 0) {
+            return timestamp;
+        }
+        let transform: TimestampTransform = this.getCurrentRepetitionTimestampTransform(timestamp);
+        return Fraction.plus(timestamp, Fraction.minus(transform.to, transform.$from)); // FIXME
+    }
+    public get Parts(): PartListEntry[] {
+        return this.parts;
+    }
+    public get MusicSheet(): MusicSheet {
+        return this.musicSheet;
+    }
+    public getIterator(start?: Fraction): MusicPartManagerIterator {
+        if (start === undefined) {
+          return new MusicPartManagerIterator(this, this.musicSheet.SelectionStart, this.musicSheet.SelectionEnd);
+        }
+        return new MusicPartManagerIterator(this, start, undefined);
+    }
+    public setSelectionStart(beginning: Fraction): void {
+        this.musicSheet.SelectionStart = beginning;
+        this.musicSheet.SelectionEnd = undefined;
+    }
+    public setSelectionRange(start: Fraction, end: Fraction): void {
+        this.musicSheet.SelectionStart = start === undefined ? this.sheetStart : start;
+        this.musicSheet.SelectionEnd = end === undefined ? this.sheetEnd : end;
+    }
+    private calcMapping(): void {
+        let timestamps: TimestampTransform[] = [];
+        let iterator: MusicPartManagerIterator = this.getIterator();
+        let currentRepetition: Repetition = iterator.CurrentRepetition;
+        let curTimestampTransform: TimestampTransform = new TimestampTransform(
+            iterator.CurrentEnrolledTimestamp.clone(),
+            iterator.CurrentSourceTimestamp.clone(),
+            undefined,
+            0
+        );
+        timestamps.push(curTimestampTransform);
+        while (!iterator.EndReached) {
+            if (iterator.JumpOccurred || currentRepetition !== iterator.CurrentRepetition) {
+                currentRepetition = iterator.CurrentRepetition;
+                if (iterator.backJumpOccurred) {
+                    let jumpRep: Repetition = iterator.JumpResponsibleRepetition;
+                    curTimestampTransform.nextBackJump = iterator.CurrentEnrolledTimestamp;
+                    curTimestampTransform.curRepetition = jumpRep;
+                    curTimestampTransform.curRepetitionIteration = iterator.CurrentJumpResponsibleRepetitionIterationBeforeJump;
+                    for (let i: number = this.timestamps.length - 2; i >= 0; i--) {
+                        if (jumpRep.AbsoluteTimestamp > timestamps[i].to || timestamps[i].curRepetition !== undefined) {
+                            break;
+                        }
+                        timestamps[i].nextBackJump = curTimestampTransform.nextBackJump;
+                        timestamps[i].curRepetition = jumpRep;
+                        timestamps[i].curRepetitionIteration = curTimestampTransform.curRepetitionIteration;
+                    }
+                }
+                curTimestampTransform = new TimestampTransform(
+                    iterator.CurrentEnrolledTimestamp.clone(),
+                    iterator.CurrentSourceTimestamp.clone(),
+                    undefined,
+                    0
+                );
+                timestamps.push(curTimestampTransform);
+            }
+            iterator.moveToNext();
+        }
+        this.timestamps = timestamps;
+    }
+}
+
+
+class TimestampTransform {
+    constructor(sourceTimestamp: Fraction, enrolledTimestamp: Fraction, repetition: Repetition, curRepetitionIteration: number) {
+        this.$from = sourceTimestamp;
+        this.to = enrolledTimestamp;
+        this.curRepetition = repetition;
+        this.curRepetitionIteration = curRepetitionIteration;
+        this.nextBackJump = undefined;
+        this.nextForwardJump = undefined;
+    }
+    public $from: Fraction;
+    public to: Fraction;
+    public nextBackJump: Fraction;
+    public nextForwardJump: Fraction;
+    public curRepetition: Repetition;
+    public curRepetitionIteration: number;
+}

+ 536 - 0
src/MusicalScore/MusicParts/MusicPartManagerIterator.ts

@@ -0,0 +1,536 @@
+import {MusicPartManager} from "./MusicPartManager";
+import {Fraction} from "../../Common/DataObjects/fraction";
+import {Repetition} from "../MusicSource/Repetition";
+import {DynamicsContainer} from "../VoiceData/HelperObjects/DynamicsContainer";
+import {MappingSourceMusicPart} from "../MusicSource/MappingSourceMusicPart";
+import {SourceMeasure} from "../VoiceData/SourceMeasure";
+import {VoiceEntry} from "../VoiceData/VoiceEntry";
+import {Instrument} from "../Instrument";
+import {VerticalSourceStaffEntryContainer} from "../VoiceData/VerticalSourceStaffEntryContainer";
+import {RhythmInstruction} from "../VoiceData/Instructions/RhythmInstruction";
+import {AbstractNotationInstruction} from "../VoiceData/Instructions/AbstractNotationInstruction";
+import {RepetitionInstruction} from "../VoiceData/Instructions/RepetitionInstruction";
+import {ContinuousDynamicExpression} from "../VoiceData/Expressions/ContinuousExpressions/continuousDynamicExpression";
+import {InstantaniousDynamicExpression} from "../VoiceData/Expressions/instantaniousDynamicExpression";
+import {MultiTempoExpression} from "../VoiceData/Expressions/multiTempoExpression";
+import {AbstractExpression} from "../VoiceData/Expressions/abstractExpression";
+
+export class MusicPartManagerIterator {
+    constructor(manager: MusicPartManager, startTimestamp?: Fraction, endTimestamp?: Fraction) {
+        try {
+            this.frontReached = true;
+            this.manager = manager;
+            this.currentVoiceEntries = undefined;
+            this.frontReached = false;
+            for (let rep of manager.MusicSheet.Repetitions) {
+                this.setRepetitionIterationCount(rep, 1);
+            }
+            this.activeDynamicExpressions = new Array(manager.MusicSheet.getCompleteNumberOfStaves());
+            this.currentMeasure = this.manager.MusicSheet.SourceMeasures[0];
+            if (startTimestamp === undefined) { return; }
+            do {
+                this.moveToNext();
+            } while ((this.currentVoiceEntries === undefined || this.currentTimeStamp.lt(startTimestamp)) && !this.endReached);
+            for (let staffIndex: number = 0; staffIndex < this.activeDynamicExpressions.length; staffIndex++) {
+                if (this.activeDynamicExpressions[staffIndex] !== undefined) {
+                    if (this.activeDynamicExpressions[staffIndex] instanceof ContinuousDynamicExpression) {
+                        let continuousDynamic: ContinuousDynamicExpression =
+                            <ContinuousDynamicExpression>this.activeDynamicExpressions[staffIndex];
+                        this.currentDynamicChangingExpressions.push(new DynamicsContainer(continuousDynamic, staffIndex));
+                    } else {
+                        let instantaniousDynamic: InstantaniousDynamicExpression =
+                            <InstantaniousDynamicExpression>this.activeDynamicExpressions[staffIndex];
+                        this.currentDynamicChangingExpressions.push(new DynamicsContainer(instantaniousDynamic, staffIndex));
+                    }
+                }
+            }
+            this.currentTempoChangingExpression = this.activeTempoExpression;
+        } catch (err) {
+            console.log("MusicPartManagerIterator: Exception." + err); // FIXME
+        }
+
+    }
+    public backJumpOccurred: boolean;
+    public forwardJumpOccurred: boolean;
+    private manager: MusicPartManager;
+    private currentMappingPart: MappingSourceMusicPart;
+    private currentMeasure: SourceMeasure;
+    private currentMeasureIndex: number = 0;
+    private currentPartIndex: number = 0;
+    private currentVoiceEntryIndex: number = -1;
+    private currentDynamicEntryIndex: number = 0;
+    private currentTempoEntryIndex: number = 0;
+    private currentVoiceEntries: VoiceEntry[];
+    private currentDynamicChangingExpressions: DynamicsContainer[] = [];
+    private currentTempoChangingExpression: MultiTempoExpression;
+    // FIXME: replace these two with a real Dictionary!
+    private repetitionIterationCountDictKeys: Repetition[];
+    private repetitionIterationCountDictValues: number[];
+    private currentRepetition: Repetition = undefined;
+    private endReached: boolean = false;
+    private frontReached: boolean = false;
+    private currentTimeStamp: Fraction = new Fraction(0, 1);
+    private currentEnrolledMeasureTimestamp: Fraction = new Fraction(0, 1);
+    private currentVerticalContainerInMeasureTimestamp: Fraction = new Fraction(0, 1);
+    private jumpResponsibleRepetition: Repetition = undefined;
+    private activeDynamicExpressions: AbstractExpression[] = [];
+    private activeTempoExpression: MultiTempoExpression;
+
+    public get EndReached(): boolean {
+        return this.endReached;
+    }
+    public get FrontReached(): boolean {
+        return this.frontReached;
+    }
+    public get CurrentMeasure(): SourceMeasure {
+        return this.currentMeasure;
+    }
+    public get CurrentRepetition(): Repetition {
+        return this.currentRepetition;
+    }
+    public get CurrentRepetitionIteration(): number {
+        if (this.CurrentRepetition !== undefined) {
+            return this.getRepetitionIterationCount(this.CurrentRepetition);
+        }
+        return 0;
+    }
+    public get CurrentJumpResponsibleRepetitionIterationBeforeJump(): number {
+        if (this.jumpResponsibleRepetition !== undefined) {
+            return this.getRepetitionIterationCount(this.jumpResponsibleRepetition) - 1;
+        }
+        return 0;
+    }
+    public get CurrentVoiceEntries(): VoiceEntry[] {
+        return this.currentVoiceEntries;
+    }
+    public get CurrentMeasureIndex(): number {
+        return this.currentMeasureIndex;
+    }
+    public get CurrentEnrolledTimestamp(): Fraction {
+        return Fraction.plus(this.currentEnrolledMeasureTimestamp, this.currentVerticalContainerInMeasureTimestamp);
+    }
+    public get CurrentSourceTimestamp(): Fraction {
+        return this.currentTimeStamp;
+    }
+    public get JumpOccurred(): boolean {
+        return this.backJumpOccurred || this.forwardJumpOccurred;
+    }
+    public get ActiveTempoExpression(): MultiTempoExpression {
+        return this.activeTempoExpression;
+    }
+    public get ActiveDynamicExpressions(): AbstractExpression[] {
+        return this.activeDynamicExpressions;
+    }
+    public get CurrentTempoChangingExpression(): MultiTempoExpression {
+        return this.currentTempoChangingExpression;
+    }
+    public get JumpResponsibleRepetition(): Repetition {
+        return this.jumpResponsibleRepetition;
+    }
+    public clone(): MusicPartManagerIterator {
+        let ret: MusicPartManagerIterator = new MusicPartManagerIterator(this.manager);
+        ret.currentVoiceEntryIndex = this.currentVoiceEntryIndex;
+        ret.currentMappingPart = this.currentMappingPart;
+        ret.currentPartIndex = this.currentPartIndex;
+        ret.currentVoiceEntries = this.currentVoiceEntries;
+        ret.endReached = this.endReached;
+        ret.frontReached = this.frontReached;
+        return ret;
+    }
+
+    public CurrentVisibleVoiceEntries(instrument?: Instrument): VoiceEntry[] {
+        let voiceEntries: VoiceEntry[] = [];
+        if (this.currentVoiceEntries === undefined) {
+            return voiceEntries;
+        }
+        if (instrument !== undefined) {
+            for (let entry of this.currentVoiceEntries) {
+                if (entry.ParentVoice.Parent.IdString === instrument.IdString) {
+                    this.getVisibleEntries(entry, voiceEntries);
+                    return voiceEntries;
+                }
+            }
+        } else {
+            for (let entry of this.currentVoiceEntries) {
+                this.getVisibleEntries(entry, voiceEntries);
+            }
+        }
+        return voiceEntries;
+    }
+
+    public CurrentAudibleVoiceEntries(instrument?: Instrument): VoiceEntry[] {
+        let voiceEntries: VoiceEntry[] = [];
+        if (this.currentVoiceEntries === undefined) {
+            return voiceEntries;
+        }
+        if (instrument !== undefined) {
+            for (let entry of this.currentVoiceEntries) {
+                if (entry.ParentVoice.Parent.IdString === instrument.IdString) {
+                    this.getAudibleEntries(entry, voiceEntries);
+                    return voiceEntries;
+                }
+            }
+        } else {
+            for (let entry of this.currentVoiceEntries) {
+                this.getAudibleEntries(entry, voiceEntries);
+            }
+        }
+        return voiceEntries;
+    }
+
+    public getCurrentDynamicChangingExpressions(): DynamicsContainer[] {
+        return this.currentDynamicChangingExpressions;
+    }
+
+    public CurrentScoreFollowingVoiceEntries(instrument?: Instrument): VoiceEntry[] {
+        let voiceEntries: VoiceEntry[] = [];
+        if (this.currentVoiceEntries === undefined) {
+            return voiceEntries;
+        }
+        if (instrument !== undefined) {
+            for (let entry of this.currentVoiceEntries) {
+                if (entry.ParentVoice.Parent.IdString === instrument.IdString) {
+                    this.getScoreFollowingEntries(entry, voiceEntries);
+                    return voiceEntries;
+                }
+            }
+        } else {
+            for (let entry of this.currentVoiceEntries) {
+                this.getScoreFollowingEntries(entry, voiceEntries);
+            }
+        }
+        return voiceEntries;
+    }
+
+    //public currentPlaybackSettings(): PlaybackSettings {
+    //    return this.manager.MusicSheet.SheetPlaybackSetting;
+    //}
+    public moveToNext(): void {
+        this.forwardJumpOccurred = this.backJumpOccurred = false;
+        if (this.endReached) { return; }
+        if (this.currentVoiceEntries !== undefined) {
+            this.currentVoiceEntries = [];
+        }
+        this.recursiveMove();
+        if (this.currentMeasure === undefined) {
+            this.currentTimeStamp = new Fraction(99999, 1);
+        }
+    }
+    public moveToNextVisibleVoiceEntry(notesOnly: boolean): void {
+        while (!this.endReached) {
+            this.moveToNext();
+            if (this.checkEntries(notesOnly)) { return; }
+        }
+    }
+    private resetRepetitionIterationCount(repetition: Repetition): number {
+        this.setRepetitionIterationCount(repetition, 1);
+        return 1;
+    }
+    private incrementRepetitionIterationCount(repetition: Repetition): number {
+        if (this.repetitionIterationCountDictKeys.indexOf(repetition) === -1) {
+            return this.setRepetitionIterationCount(repetition, 1);
+        } else {
+            return this.setRepetitionIterationCount(repetition, this.getRepetitionIterationCount(repetition) + 1);
+        }
+    }
+    private setRepetitionIterationCount(repetition: Repetition, iterationCount: number): number {
+        let i: number = this.repetitionIterationCountDictKeys.indexOf(repetition);
+        if (i === -1) {
+            this.repetitionIterationCountDictKeys.push(repetition);
+            this.repetitionIterationCountDictValues.push(iterationCount);
+        } else {
+            this.repetitionIterationCountDictValues[i] = iterationCount;
+        }
+        return iterationCount;
+    }
+    private getRepetitionIterationCount(rep: Repetition): number {
+        let i: number = this.repetitionIterationCountDictKeys.indexOf(rep);
+        if (i !== -1) {
+            return this.repetitionIterationCountDictValues[i];
+        }
+    }
+/*    private moveTempoIndexToTimestamp(measureNumber: number): void {
+        for (let index: number = 0; index < this.manager.MusicSheet.TimestampSortedTempoExpressionsList.length; index++) {
+            if (this.manager.MusicSheet.TimestampSortedTempoExpressionsList[index].SourceMeasureParent.MeasureNumber >= measureNumber) {
+                this.currentTempoEntryIndex = Math.Max(-1, index - 1);
+                return
+            }
+        }
+    }
+    private getNextTempoEntryTimestamp(): Fraction {
+        if (this.currentTempoEntryIndex >= this.manager.MusicSheet.TimestampSortedTempoExpressionsList.length - 1) {
+            return new Fraction(99999, 1);
+        }
+        return this.manager.MusicSheet.TimestampSortedTempoExpressionsList[this.currentTempoEntryIndex + 1].SourceMeasureParent.AbsoluteTimestamp +
+        this.manager.MusicSheet.TimestampSortedTempoExpressionsList[this.currentTempoEntryIndex + 1].Timestamp;
+    }
+    private moveToNextDynamic(): void {
+        this.currentDynamicEntryIndex++;
+        this.currentDynamicChangingExpressions.Clear();
+        let curDynamicEntry: DynamicsContainer = this.manager.MusicSheet.TimestampSortedDynamicExpressionsList[this.currentDynamicEntryIndex];
+        this.currentDynamicChangingExpressions.push(curDynamicEntry);
+        let tsNow: Fraction = curDynamicEntry.parMultiExpression().AbsoluteTimestamp;
+        for (let i: number = this.currentDynamicEntryIndex + 1; i < this.manager.MusicSheet.TimestampSortedDynamicExpressionsList.length; i++) {
+            curDynamicEntry = this.manager.MusicSheet.TimestampSortedDynamicExpressionsList[i];
+            if ((curDynamicEntry.parMultiExpression().AbsoluteTimestamp !== tsNow)) { break; }
+            this.currentDynamicEntryIndex = i;
+            this.currentDynamicChangingExpressions.push(curDynamicEntry);
+        }
+    }
+    private moveDynamicIndexToTimestamp(absoluteTimestamp: Fraction): void {
+        let dynamics: DynamicsContainer[] = this.manager.MusicSheet.TimestampSortedDynamicExpressionsList;
+        for (let index: number = 0; index < dynamics.length; index++) {
+            if (dynamics[index].parMultiExpression().AbsoluteTimestamp >= absoluteTimestamp) {
+                this.currentDynamicEntryIndex = Math.Max(0, index - 1);
+                return
+            }
+        }
+    }
+    private getNextDynamicsEntryTimestamp(): Fraction {
+        if (this.currentDynamicEntryIndex >= this.manager.MusicSheet.TimestampSortedDynamicExpressionsList.length - 1) {
+            return new Fraction(99999, 1);
+        }
+        return this.manager.MusicSheet.TimestampSortedDynamicExpressionsList[this.currentDynamicEntryIndex + 1].parMultiExpression().AbsoluteTimestamp;
+    }
+    */
+    private handleRepetitionsAtMeasureBegin(): void {
+        for (let idx: number = 0, len: number = this.currentMeasure.FirstRepetitionInstructions.length; idx < len; ++idx) {
+            let repetitionInstruction: RepetitionInstruction = this.currentMeasure.FirstRepetitionInstructions[idx];
+            if (repetitionInstruction.parentRepetition === undefined) { continue; }
+            let currentRepetition: Repetition = repetitionInstruction.parentRepetition;
+            this.currentRepetition = currentRepetition;
+            if (currentRepetition.StartIndex === this.currentMeasureIndex) {
+                if (
+                  this.JumpResponsibleRepetition !== undefined &&
+                  currentRepetition !== this.JumpResponsibleRepetition &&
+                  currentRepetition.StartIndex >= this.JumpResponsibleRepetition.StartIndex &&
+                  currentRepetition.EndIndex <= this.JumpResponsibleRepetition.EndIndex
+                ) {
+                    this.resetRepetitionIterationCount(currentRepetition);
+                }
+            }
+        }
+    }
+
+    private handleRepetitionsAtMeasureEnd(): void {
+        for (let idx: number = 0, len: number = this.currentMeasure.LastRepetitionInstructions.length; idx < len; ++idx) {
+            let repetitionInstruction: RepetitionInstruction = this.currentMeasure.LastRepetitionInstructions[idx];
+            let currentRepetition: Repetition = repetitionInstruction.parentRepetition;
+            if (currentRepetition === undefined) { continue; }
+            if (currentRepetition.BackwardJumpInstructions.indexOf(repetitionInstruction) > -1) {
+                if (this.getRepetitionIterationCount(currentRepetition) < currentRepetition.UserNumberOfRepetitions) {
+                    this.doBackJump(currentRepetition);
+                    this.backJumpOccurred = true;
+                    return;
+                }
+            }
+            if (repetitionInstruction === currentRepetition.forwardJumpInstruction) {
+                if (
+                  this.JumpResponsibleRepetition !== undefined
+                  && currentRepetition !== this.JumpResponsibleRepetition
+                  && currentRepetition.StartIndex >= this.JumpResponsibleRepetition.StartIndex
+                  && currentRepetition.EndIndex <= this.JumpResponsibleRepetition.EndIndex
+                ) {
+                    this.resetRepetitionIterationCount(currentRepetition);
+                }
+
+                let forwardJumpTargetMeasureIndex: number = currentRepetition.getForwardJumpTargetForIteration(
+                  this.getRepetitionIterationCount(currentRepetition)
+                );
+                if (forwardJumpTargetMeasureIndex >= 0) {
+                    this.currentMeasureIndex = forwardJumpTargetMeasureIndex;
+                    this.currentMeasure = this.manager.MusicSheet.SourceMeasures[this.currentMeasureIndex];
+                    this.currentVoiceEntryIndex = -1;
+                    this.jumpResponsibleRepetition = currentRepetition;
+                    this.forwardJumpOccurred = true;
+                    return;
+                }
+                if (forwardJumpTargetMeasureIndex === -2) {
+                    this.endReached = true;
+                }
+            }
+        }
+        this.currentMeasureIndex++;
+        if (this.JumpResponsibleRepetition !== undefined && this.currentMeasureIndex > this.JumpResponsibleRepetition.EndIndex) {
+            this.jumpResponsibleRepetition = undefined;
+        }
+    }
+    private doBackJump(currentRepetition: Repetition): void {
+        this.currentMeasureIndex = currentRepetition.getBackwardJumpTarget();
+        this.currentMeasure = this.manager.MusicSheet.SourceMeasures[this.currentMeasureIndex];
+        this.currentVoiceEntryIndex = -1;
+        this.incrementRepetitionIterationCount(currentRepetition);
+        this.jumpResponsibleRepetition = currentRepetition;
+    }
+    private activateCurrentRhythmInstructions(): void {
+        if (
+          this.currentMeasure !== undefined &&
+          this.currentMeasure.FirstInstructionsStaffEntries.length > 0 &&
+          this.currentMeasure.FirstInstructionsStaffEntries[0] !== undefined
+        ) {
+            let instructions: AbstractNotationInstruction[] = this.currentMeasure.FirstInstructionsStaffEntries[0].Instructions;
+            for (let idx: number = 0, len: number = instructions.length; idx < len; ++idx) {
+                let abstractNotationInstruction: AbstractNotationInstruction = instructions[idx];
+                if (abstractNotationInstruction instanceof RhythmInstruction) {
+                    this.manager.MusicSheet.SheetPlaybackSetting.rhythm = (<RhythmInstruction>abstractNotationInstruction).Rhythm;
+                }
+            }
+        }
+    }
+    private activateCurrentDynamicOrTempoInstructions(): void {
+        let timeSortedDynamics: DynamicsContainer[] = this.manager.MusicSheet.TimestampSortedDynamicExpressionsList;
+        while (
+          this.currentDynamicEntryIndex > 0 && (
+            this.currentDynamicEntryIndex >= timeSortedDynamics.length ||
+            timeSortedDynamics[this.currentDynamicEntryIndex].parMultiExpression().AbsoluteTimestamp >= this.CurrentSourceTimestamp
+          )
+        ) {
+            this.currentDynamicEntryIndex--;
+        }
+        while (
+          this.currentDynamicEntryIndex < timeSortedDynamics.length &&
+          timeSortedDynamics[this.currentDynamicEntryIndex].parMultiExpression().AbsoluteTimestamp < this.CurrentSourceTimestamp
+        ) {
+            this.currentDynamicEntryIndex++;
+        }
+        while (
+          this.currentDynamicEntryIndex < timeSortedDynamics.length
+          && timeSortedDynamics[this.currentDynamicEntryIndex].parMultiExpression().AbsoluteTimestamp === this.CurrentSourceTimestamp
+        ) {
+            let dynamicsContainer: DynamicsContainer = timeSortedDynamics[this.currentDynamicEntryIndex];
+            let staffIndex: number = dynamicsContainer.staffNumber;
+            if (this.CurrentSourceTimestamp === dynamicsContainer.parMultiExpression().AbsoluteTimestamp) {
+                if (dynamicsContainer.continuousDynamicExpression !== undefined) {
+                    this.activeDynamicExpressions[staffIndex] = dynamicsContainer.continuousDynamicExpression;
+                } else if (dynamicsContainer.instantaneousDynamicExpression !== undefined) {
+                    this.activeDynamicExpressions[staffIndex] = dynamicsContainer.instantaneousDynamicExpression;
+                }
+            }
+            this.currentDynamicEntryIndex++;
+        }
+        this.currentDynamicChangingExpressions = [];
+        for (let staffIndex: number = 0; staffIndex < this.activeDynamicExpressions.length; staffIndex++) {
+            if (this.activeDynamicExpressions[staffIndex] !== undefined) {
+                let startTime: Fraction;
+                let endTime: Fraction;
+                if (this.activeDynamicExpressions[staffIndex] instanceof ContinuousDynamicExpression) {
+                    let continuousDynamic: ContinuousDynamicExpression = <ContinuousDynamicExpression>this.activeDynamicExpressions[staffIndex];
+                    startTime = continuousDynamic.StartMultiExpression.AbsoluteTimestamp;
+                    endTime = continuousDynamic.EndMultiExpression.AbsoluteTimestamp;
+                    if (this.CurrentSourceTimestamp >= startTime && this.CurrentSourceTimestamp <= endTime) {
+                        this.currentDynamicChangingExpressions.push(new DynamicsContainer(continuousDynamic, staffIndex));
+                    }
+                } else {
+                    let instantaniousDynamic: InstantaniousDynamicExpression = <InstantaniousDynamicExpression>this.activeDynamicExpressions[staffIndex];
+                    if (this.CurrentSourceTimestamp === instantaniousDynamic.ParentMultiExpression.AbsoluteTimestamp) {
+                        this.currentDynamicChangingExpressions.push(new DynamicsContainer(instantaniousDynamic, staffIndex));
+                    }
+                }
+            }
+        }
+        let timeSortedTempoExpressions: MultiTempoExpression[] = this.manager.MusicSheet.TimestampSortedTempoExpressionsList;
+
+        while (this.currentTempoEntryIndex > 0 && (
+          this.currentTempoEntryIndex >= timeSortedTempoExpressions.length
+          || timeSortedTempoExpressions[this.currentTempoEntryIndex].AbsoluteTimestamp >= this.CurrentSourceTimestamp
+        )) {
+            this.currentTempoEntryIndex--;
+        }
+
+        while (
+          this.currentTempoEntryIndex < timeSortedTempoExpressions.length &&
+          timeSortedTempoExpressions[this.currentTempoEntryIndex].AbsoluteTimestamp < this.CurrentSourceTimestamp
+        ) {
+            this.currentTempoEntryIndex++;
+        }
+
+        while (
+          this.currentTempoEntryIndex < timeSortedTempoExpressions.length
+          && timeSortedTempoExpressions[this.currentTempoEntryIndex].AbsoluteTimestamp === this.CurrentSourceTimestamp
+        ) {
+            this.activeTempoExpression = timeSortedTempoExpressions[this.currentTempoEntryIndex];
+            this.currentTempoEntryIndex++;
+        }
+        this.currentTempoChangingExpression = undefined;
+        if (this.activeTempoExpression !== undefined) {
+            let endTime: Fraction = this.activeTempoExpression.AbsoluteTimestamp;
+            if (this.activeTempoExpression.ContinuousTempo !== undefined) {
+                endTime = this.activeTempoExpression.ContinuousTempo.AbsoluteEndTimestamp;
+            }
+            if (   this.CurrentSourceTimestamp >= this.activeTempoExpression.AbsoluteTimestamp
+                || this.CurrentSourceTimestamp <= endTime
+            ) {
+                this.currentTempoChangingExpression = this.activeTempoExpression;
+            }
+        }
+    }
+    private recursiveMove(): void {
+        this.currentVoiceEntryIndex++;
+        if (this.currentVoiceEntryIndex === 0) {
+            this.handleRepetitionsAtMeasureBegin();
+            this.activateCurrentRhythmInstructions();
+        }
+        if (this.currentVoiceEntryIndex >= 0 && this.currentVoiceEntryIndex < this.currentMeasure.VerticalSourceStaffEntryContainers.length) {
+            let currentContainer: VerticalSourceStaffEntryContainer = this.currentMeasure.VerticalSourceStaffEntryContainers[this.currentVoiceEntryIndex];
+            this.currentVoiceEntries = this.getVoiceEntries(currentContainer);
+            this.currentVerticalContainerInMeasureTimestamp = currentContainer.Timestamp;
+            this.currentTimeStamp = Fraction.plus(this.currentMeasure.AbsoluteTimestamp, this.currentVerticalContainerInMeasureTimestamp);
+            if (this.currentTimeStamp >= this.manager.MusicSheet.SelectionEnd) {
+                this.endReached = true;
+            }
+            this.activateCurrentDynamicOrTempoInstructions();
+            return;
+        }
+        this.currentEnrolledMeasureTimestamp.Add(this.currentMeasure.Duration);
+        this.handleRepetitionsAtMeasureEnd();
+        if (this.currentMeasureIndex >= 0 && this.currentMeasureIndex < this.manager.MusicSheet.SourceMeasures.length) {
+            this.currentMeasure = this.manager.MusicSheet.SourceMeasures[this.currentMeasureIndex];
+            this.currentTimeStamp = Fraction.plus(this.currentMeasure.AbsoluteTimestamp, this.currentVerticalContainerInMeasureTimestamp);
+            this.currentVoiceEntryIndex = -1;
+            this.recursiveMove();
+            return;
+        }
+        this.currentVerticalContainerInMeasureTimestamp = new Fraction();
+        this.currentMeasure = undefined;
+        this.currentVoiceEntries = undefined;
+        this.endReached = true;
+    }
+    private checkEntries(notesOnly: boolean): boolean {
+        let tlist: VoiceEntry[] = this.CurrentVisibleVoiceEntries();
+        if (tlist.length > 0) {
+            if (!notesOnly) { return true; }
+            for (let idx: number = 0, len: number = tlist.length; idx < len; ++idx) {
+                let entry: VoiceEntry = tlist[idx];
+                if (entry.Notes[0].Pitch !== undefined) { return true; }
+            }
+        }
+        return false;
+    }
+    private getVisibleEntries(entry: VoiceEntry, visibleEntries: VoiceEntry[]): void {
+        if (entry.ParentVoice.Visible) {
+            visibleEntries.push(entry);
+        }
+    }
+    private getAudibleEntries(entry: VoiceEntry, audibleEntries: VoiceEntry[]): void {
+        if (entry.ParentVoice.Audible) {
+            audibleEntries.push(entry);
+        }
+    }
+    private getScoreFollowingEntries(entry: VoiceEntry, followingEntries: VoiceEntry[]): void {
+        if (entry.ParentVoice.Following && entry.ParentVoice.Parent.Following) {
+            followingEntries.push(entry);
+        }
+    }
+    private getVoiceEntries(container: VerticalSourceStaffEntryContainer): VoiceEntry[] {
+        let entries: VoiceEntry[] = [];
+        for (let sourceStaffEntry of container.StaffEntries) {
+            if (sourceStaffEntry === undefined) { continue; }
+            for (let voiceEntry of sourceStaffEntry.VoiceEntries) {
+                entries.push(voiceEntry);
+            }
+        }
+        return entries;
+    }
+
+
+}

+ 496 - 0
src/MusicalScore/MusicSheet.ts

@@ -0,0 +1,496 @@
+import {Fraction} from "../Common/DataObjects/fraction";
+import {MusicPartManager} from "./MusicParts/MusicPartManager";
+import {SourceMeasure} from "./VoiceData/SourceMeasure";
+import {Repetition} from "./MusicSource/Repetition";
+import {DynamicsContainer} from "./VoiceData/HelperObjects/DynamicsContainer";
+import {InstrumentalGroup} from "./InstrumentalGroup";
+import {Instrument} from "./Instrument";
+import {Label} from "./Label";
+import {Staff} from "./VoiceData/Staff";
+import {MusicPartManagerIterator} from "./MusicParts/MusicPartManagerIterator";
+import {VerticalSourceStaffEntryContainer} from "./VoiceData/VerticalSourceStaffEntryContainer";
+import {Voice} from "./VoiceData/Voice";
+import {MusicSheetErrors} from "../Common/DataObjects/MusicSheetErrors";
+import {MultiTempoExpression} from "./VoiceData/Expressions/multiTempoExpression";
+import {EngravingRules} from "./Graphical/EngravingRules";
+import {NoteState} from "./Graphical/DrawingEnums";
+import {Note} from "./VoiceData/Note";
+import {VoiceEntry} from "./VoiceData/VoiceEntry";
+
+// FIXME
+//type MusicSheetParameters = any;
+//type MultiTempoExpression = any;
+//type PlaybackSettings = any;
+//type MusicSheetParameterObject = any;
+//type EngravingRules = any;
+//type MusicSheetErrors = any;
+//type IPhonicScoreInterface = any;
+//type MusicSheetParameterChangedDelegate = any;
+//type IInstrument = any;
+//type ISettableInstrument = any;
+//type IRepetition = any;
+
+// FIXME Andrea: Commented out some things, have a look at (*)
+
+export class PlaybackSettings {
+    public rhythm: Fraction;
+}
+
+export class MusicSheet /*implements ISettableMusicSheet, IComparable<MusicSheet>*/ {
+    constructor() {
+        this.rules = EngravingRules.Rules;
+        // (*) this.playbackSettings = new PlaybackSettings(new Fraction(4, 4, false), 100);
+        this.userStartTempoInBPM = 100;
+        this.pageWidth = 120;
+        this.MusicPartManager = new MusicPartManager(this);
+    }
+    public static defaultTitle: string = "[kein Titel]";
+
+    public userStartTempoInBPM: number;
+    public pageWidth: number;
+    public rules: EngravingRules;
+
+    private idString: string = "kjgdfuilhsdaöoihfsvjh";
+    private sourceMeasures: SourceMeasure[] = [];
+    private repetitions: Repetition[] = [];
+    private dynListStaves: DynamicsContainer[][] = [];
+    private timestampSortedDynamicExpressionsList: DynamicsContainer[] = [];
+    private timestampSortedTempoExpressionsList: MultiTempoExpression[] = [];
+    private instrumentalGroups: InstrumentalGroup[] = [];
+    private instruments: Instrument[] = [];
+    private playbackSettings: PlaybackSettings;
+    private path: string;
+    private title: Label;
+    private subtitle: Label;
+    private composer: Label;
+    private lyricist: Label;
+    // private languages: Language[] = [];
+    // private activeLanguage: Language;
+    private musicPartManager: MusicPartManager = undefined;
+    private musicSheetErrors: MusicSheetErrors = new MusicSheetErrors();
+    private staves: Staff[] = [];
+    private selectionStart: Fraction;
+    private selectionEnd: Fraction;
+    private transpose: number = 0;
+    private defaultStartTempoInBpm: number = 0;
+    private drawErroneousMeasures: boolean = false;
+    private hasBeenOpenedForTheFirstTime: boolean = false;
+    private currentEnrolledPosition: Fraction = new Fraction(0, 1);
+    // (*) private musicSheetParameterObject: MusicSheetParameterObject = undefined;
+    private engravingRules: EngravingRules;
+    // (*) private musicSheetParameterChangedDelegate: MusicSheetParameterChangedDelegate;
+
+    public static getIndexFromStaff(staff: Staff): number {
+        return staff.idInMusicSheet;
+    }
+    public get SourceMeasures(): SourceMeasure[] {
+        return this.sourceMeasures;
+    }
+    public set SourceMeasures(value: SourceMeasure[]) {
+        this.sourceMeasures = value;
+    }
+    public get Repetitions(): Repetition[] {
+        return this.repetitions;
+    }
+    public set Repetitions(value: Repetition[]) {
+        this.repetitions = value;
+    }
+    public get DynListStaves(): DynamicsContainer[][] {
+        return this.dynListStaves;
+    }
+    public get TimestampSortedTempoExpressionsList(): MultiTempoExpression[] {
+        return this.timestampSortedTempoExpressionsList;
+    }
+    public get TimestampSortedDynamicExpressionsList(): DynamicsContainer[] {
+        return this.timestampSortedDynamicExpressionsList;
+    }
+    public get InstrumentalGroups(): InstrumentalGroup[] {
+        return this.instrumentalGroups;
+    }
+    public get Instruments(): Instrument[] {
+        return this.instruments;
+    }
+     public get SheetPlaybackSetting(): PlaybackSettings {
+        return this.playbackSettings;
+    }
+     public set SheetPlaybackSetting(value: PlaybackSettings) {
+        this.playbackSettings = value;
+    }
+    public get DrawErroneousMeasures(): boolean {
+        return this.drawErroneousMeasures;
+    }
+    public set DrawErroneousMeasures(value: boolean) {
+        this.drawErroneousMeasures = value;
+    }
+    public get HasBeenOpenedForTheFirstTime(): boolean {
+        return this.hasBeenOpenedForTheFirstTime;
+    }
+    public set HasBeenOpenedForTheFirstTime(value: boolean) {
+        this.hasBeenOpenedForTheFirstTime = value;
+    }
+    public InitializeStartTempoInBPM(startTempo: number): void {
+        // (*) this.playbackSettings.BeatsPerMinute = startTempo;
+        this.userStartTempoInBPM = startTempo;
+    }
+    public get DefaultStartTempoInBpm(): number {
+        return this.defaultStartTempoInBpm;
+    }
+    public set DefaultStartTempoInBpm(value: number) {
+        this.defaultStartTempoInBpm = value;
+        this.InitializeStartTempoInBPM(value);
+    }
+    public get Path(): string {
+        return this.path;
+    }
+    public set Path(value: string) {
+        this.path = value;
+    }
+    public get Staves(): Staff[] {
+        return this.staves;
+    }
+    public get TitleString(): string {
+        if (this.title !== undefined) {
+            return this.title.text;
+        } else {
+            return "";
+        }
+    }
+    public get SubtitleString(): string {
+        if (this.subtitle !== undefined) {
+            return this.subtitle.text;
+        } else {
+            return "";
+        }
+    }
+    public get ComposerString(): string {
+        if (this.composer !== undefined) {
+            return this.composer.text;
+        } else {
+            return "";
+        }
+    }
+    public get LyricistString(): string {
+        if (this.lyricist !== undefined) {
+            return this.lyricist.text;
+        } else {
+            return "";
+        }
+    }
+    public get Title(): Label {
+        return this.title;
+    }
+    public set Title(value: Label) {
+        this.title = value;
+    }
+    public get Subtitle(): Label {
+        return this.subtitle;
+    }
+    public set Subtitle(value: Label) {
+        this.subtitle = value;
+    }
+    public get Composer(): Label {
+        return this.composer;
+    }
+    public set Composer(value: Label) {
+        this.composer = value;
+    }
+    public get Lyricist(): Label {
+        return this.lyricist;
+    }
+    public set Lyricist(value: Label) {
+        this.lyricist = value;
+    }
+    public get Rules(): EngravingRules {
+       return this.engravingRules;
+    }
+    public set Rules(value: EngravingRules) {
+       this.engravingRules = value;
+    }
+    public get SheetErrors(): MusicSheetErrors {
+        return this.musicSheetErrors;
+    }
+    public get SelectionStart(): Fraction {
+        return this.selectionStart;
+    }
+    public set SelectionStart(value: Fraction) {
+        this.selectionStart = value.clone();
+        this.currentEnrolledPosition = value.clone();
+    }
+    public get SelectionEnd(): Fraction {
+        return this.selectionEnd;
+    }
+    public set SelectionEnd(value: Fraction) {
+        this.selectionEnd = value;
+    }
+    // (*) public get MusicSheetParameterObject(): MusicSheetParameterObject {
+    //    return this.musicSheetParameterObject;
+    //}
+    // (*) public set MusicSheetParameterObject(value: MusicSheetParameterObject) {
+    //    this.musicSheetParameterObject = value;
+    //    this.Title = new Label(this.musicSheetParameterObject.Title);
+    //    this.Composer = new Label(this.musicSheetParameterObject.Composer);
+    //}
+    public addMeasure(measure: SourceMeasure): void {
+        this.sourceMeasures.push(measure);
+        measure.measureListIndex = this.sourceMeasures.length - 1;
+    }
+    public checkForInstrumentWithNoVoice(): void {
+        for (let idx: number = 0, len: number = this.instruments.length; idx < len; ++idx) {
+            let instrument: Instrument = this.instruments[idx];
+            if (instrument.Voices.length === 0) {
+                let voice: Voice = new Voice(instrument, 1);
+                instrument.Voices.push(voice);
+            }
+        }
+    }
+    public getStaffFromIndex(staffIndexInMusicSheet: number): Staff {
+        return this.staves[staffIndexInMusicSheet];
+    }
+    public fillStaffList(): void {
+        let i: number = 0;
+        for (let idx: number = 0, len: number = this.instruments.length; idx < len; ++idx) {
+            let instrument: Instrument = this.instruments[idx];
+            for (let idx2: number = 0, len2: number = instrument.Staves.length; idx2 < len2; ++idx2) {
+                let staff: Staff = instrument.Staves[idx2];
+                staff.idInMusicSheet = i;
+                this.staves.push(staff);
+                i++;
+            }
+        }
+    }
+    public get MusicPartManager(): MusicPartManager {
+        return this.musicPartManager;
+    }
+    public set MusicPartManager(value: MusicPartManager) {
+        this.musicPartManager = value;
+    }
+    public getCompleteNumberOfStaves(): number {
+        let num: number = 0;
+        for (let idx: number = 0, len: number = this.instruments.length; idx < len; ++idx) {
+            let instrument: Instrument = this.instruments[idx];
+            num += instrument.Staves.length;
+        }
+        return num;
+    }
+    public getListOfMeasuresFromIndeces(start: number, end: number): SourceMeasure[] {
+        let measures: SourceMeasure[] = [];
+        for (let i: number = start; i <= end; i++) {
+            measures.push(this.sourceMeasures[i]);
+        }
+        return measures;
+    }
+    public getNextSourceMeasure(measure: SourceMeasure): SourceMeasure {
+        let index: number = this.sourceMeasures.indexOf(measure);
+        if (index === this.sourceMeasures.length - 1) {
+            return measure;
+        }
+        return this.sourceMeasures[index + 1];
+    }
+    public getFirstSourceMeasure(): SourceMeasure {
+        return this.sourceMeasures[0];
+    }
+    public getLastSourceMeasure(): SourceMeasure {
+        return this.sourceMeasures[this.sourceMeasures.length - 1];
+    }
+    public resetAllNoteStates(): void {
+       let iterator: MusicPartManagerIterator = this.MusicPartManager.getIterator();
+       while (!iterator.EndReached && iterator.CurrentVoiceEntries !== undefined) {
+           for (let idx: number = 0, len: number = iterator.CurrentVoiceEntries.length; idx < len; ++idx) {
+               let voiceEntry: VoiceEntry = iterator.CurrentVoiceEntries[idx];
+               for (let idx2: number = 0, len2: number = voiceEntry.Notes.length; idx2 < len2; ++idx2) {
+                   let note: Note = voiceEntry.Notes[idx2];
+                   note.state = NoteState.Normal;
+               }
+           }
+           iterator.moveToNext();
+       }
+    }
+    public getMusicSheetInstrumentIndex(instrument: Instrument): number {
+        return this.Instruments.indexOf(instrument);
+    }
+    public getGlobalStaffIndexOfFirstStaff(instrument: Instrument): number {
+        let instrumentIndex: number = this.getMusicSheetInstrumentIndex(instrument);
+        let staffLineIndex: number = 0;
+        for (let i: number = 0; i < instrumentIndex; i++) {
+            staffLineIndex += this.Instruments[i].Staves.length;
+        }
+        return staffLineIndex;
+    }
+    public setRepetitionNewUserNumberOfRepetitions(index: number, value: number): void {
+        let repIndex: number = 0;
+        for (let i: number = 0; i < this.repetitions.length; i++) {
+            if (this.repetitions[i] instanceof Repetition) { // FIXME
+                if (index === repIndex) {
+                    this.repetitions[i].UserNumberOfRepetitions = value;
+                    break;
+                } else {
+                    repIndex++;
+                }
+            }
+        }
+    }
+    public getRepetitionByIndex(index: number): Repetition {
+        let repIndex: number = 0;
+        for (let i: number = 0; i < this.repetitions.length; i++) {
+            if (this.repetitions[i] instanceof Repetition) {
+                if (index === repIndex) {
+                    return <Repetition>this.repetitions[i];
+                }
+                repIndex++;
+            }
+        }
+        return undefined;
+    }
+    public CompareTo(other: MusicSheet): number {
+        return this.Title.text.localeCompare(other.Title.text);
+    }
+    // (*)
+    //public get IInstruments(): IInstrument[] {
+    //    return this.instruments.slice()
+    //}
+    //public get IInitializableInstruments(): ISettableInstrument[] {
+    //    return this.instruments.slice();
+    //}
+    //public get IRepetitions(): IRepetition[] {
+    //    try {
+    //        let repetitions: IRepetition[] = [];
+    //        for (let idx: number = 0, len: number = this.repetitions.length; idx < len; ++idx) {
+    //            let partListEntry: PartListEntry = this.repetitions[idx];
+    //            if (partListEntry instanceof Repetition) {
+    //                repetitions.push(<Repetition>partListEntry);
+    //            }
+    //        }
+    //        return repetitions;
+    //    } catch (ex) {
+    //        console.log(/*Logger.DefaultLogger.LogError(LogLevel.NORMAL, FIXME */ "MusicSheet.IRepetitions get: ", ex);
+    //        return undefined;
+    //    }
+    //
+    //}
+    //public GetExpressionsStartTempoInBPM(): number {
+    //    if (this.TimestampSortedTempoExpressionsList.length > 0) {
+    //        let me: MultiTempoExpression = this.TimestampSortedTempoExpressionsList[0];
+    //        if (me.InstantaniousTempo !== undefined) {
+    //            return me.InstantaniousTempo.TempoInBpm;
+    //        } else if (me.ContinuousTempo !== undefined) {
+    //            return me.ContinuousTempo.StartTempo;
+    //        }
+    //    }
+    //    return this.UserStartTempoInBPM;
+    //}
+    public get Errors(): { [n: number]: string[]; } {
+        return this.musicSheetErrors.measureErrors;
+    }
+    public get FirstMeasureNumber(): number {
+        try {
+            return this.getFirstSourceMeasure().MeasureNumber;
+        } catch (ex) {
+            console.log(/* FIXME LogLevel.NORMAL, */ "MusicSheet.FirstMeasureNumber: ", ex);
+            return 0;
+        }
+
+    }
+    public get LastMeasureNumber(): number {
+        try {
+            return this.getLastSourceMeasure().MeasureNumber;
+        } catch (ex) {
+            console.log(/* FIXME LogLevel.NORMAL, */ "MusicSheet.LastMeasureNumber: ", ex);
+            return 0;
+        }
+
+    }
+    public get CurrentEnrolledPosition(): Fraction {
+        return this.currentEnrolledPosition.clone();
+    }
+    public set CurrentEnrolledPosition(value: Fraction) {
+        this.currentEnrolledPosition = value.clone();
+    }
+    public get Transpose(): number {
+        return this.transpose;
+    }
+    public set Transpose(value: number) {
+        this.transpose = value;
+    }
+    // (*)
+    //public SetMusicSheetParameter(parameter: MusicSheetParameters, value: Object): void {
+    //    if (this.PhonicScoreInterface !== undefined) {
+    //        this.PhonicScoreInterface.RequestMusicSheetParameter(parameter, value);
+    //    } else {
+    //        let oldValue: Object = 0;
+    //        if (parameter === undefined) { // FIXME MusicSheetParameters.MusicSheetTranspose) {
+    //            oldValue = this.Transpose;
+    //            this.Transpose = <number>value;
+    //        }
+    //        if (parameter === undefined) { // FIXME MusicSheetParameters.StartTempoInBPM) {
+    //            oldValue = this.UserStartTempoInBPM;
+    //            this.UserStartTempoInBPM = <number>value;
+    //        }
+    //        if (parameter === undefined) { // FIXME MusicSheetParameters.HighlightErrors) {
+    //            oldValue = value;
+    //        }
+    //        if (this.MusicSheetParameterChanged !== undefined) {
+    //            this.musicSheetParameterChangedDelegate(undefined, parameter, value, oldValue);
+    //        }
+    //    }
+    //}
+    //public get MusicSheetParameterChanged(): MusicSheetParameterChangedDelegate {
+    //    return this.musicSheetParameterChangedDelegate;
+    //}
+    //public set MusicSheetParameterChanged(value: MusicSheetParameterChangedDelegate) {
+    //    this.musicSheetParameterChangedDelegate = value;
+    //}
+    public get FullNameString(): string {
+       return this.ComposerString + " " + this.TitleString;
+    }
+    public get IdString(): string {
+       return this.idString;
+    }
+    public set IdString(value: string) {
+       this.idString = value;
+    }
+    // public Dispose(): void {
+    //    this.MusicSheetParameterChanged = undefined;
+    //    for (let idx: number = 0, len: number = this.instruments.length; idx < len; ++idx) {
+    //        let instrument: Instrument = this.instruments[idx];
+    //        instrument.dispose(); // FIXME
+    //    }
+    // }
+    public getEnrolledSelectionStartTimeStampWorkaround(): Fraction {
+        let iter: MusicPartManagerIterator = this.MusicPartManager.getIterator(this.SelectionStart);
+        return Fraction.createFromFraction(iter.CurrentEnrolledTimestamp);
+    }
+    public get SheetEndTimestamp(): Fraction {
+        let lastMeasure: SourceMeasure = this.getLastSourceMeasure();
+        return Fraction.plus(lastMeasure.AbsoluteTimestamp, lastMeasure.Duration);
+    }
+    public getSourceMeasureFromTimeStamp(timeStamp: Fraction): SourceMeasure {
+        for (let idx: number = 0, len: number = this.sourceMeasures.length; idx < len; ++idx) {
+            let sm: SourceMeasure = this.sourceMeasures[idx];
+            for (let idx2: number = 0, len2: number = sm.VerticalSourceStaffEntryContainers.length; idx2 < len2; ++idx2) {
+                let vssec: VerticalSourceStaffEntryContainer = sm.VerticalSourceStaffEntryContainers[idx2];
+                if (Fraction.Equal(timeStamp, vssec.getAbsoluteTimestamp())) {
+                    return sm;
+                }
+            }
+        }
+        return this.findSourceMeasureFromTimeStamp(timeStamp);
+    }
+    public findSourceMeasureFromTimeStamp(timestamp: Fraction): SourceMeasure {
+        for (let sm of this.sourceMeasures) {
+            if (sm.AbsoluteTimestamp.lte(timestamp) && timestamp.lt(Fraction.plus(sm.AbsoluteTimestamp, sm.Duration))) {
+                return sm;
+            }
+        }
+    }
+
+    public getVisibleInstruments(): Instrument[] {
+        let visInstruments: Instrument[] = [];
+        for (let idx: number = 0, len: number = this.Instruments.length; idx < len; ++idx) {
+            let instrument: Instrument = this.Instruments[idx];
+            if (instrument.Voices.length > 0 && instrument.Voices[0].Visible) {
+                visInstruments.push(instrument);
+            }
+        }
+        return visInstruments;
+    }
+}

+ 56 - 0
src/MusicalScore/MusicSource/MappingSourceMusicPart.ts

@@ -0,0 +1,56 @@
+import {SourceMusicPart} from "./SourceMusicPart";
+import {Fraction} from "../../Common/DataObjects/fraction";
+import {Repetition} from "./Repetition";
+import {PartListEntry} from "./PartListEntry";
+
+export class MappingSourceMusicPart /* implements IComparable, IComparable<MappingSourceMusicPart>*/ {
+    constructor(
+        sourceMusicPart: SourceMusicPart, startTimestamp: Fraction, parentPartListEntry?: Repetition,
+        repetitionRun: number = -1, isEnding: boolean = false
+    ) {
+        this.sourceMusicPart = sourceMusicPart;
+        this.parentPartListEntry = parentPartListEntry;
+        this.startTimestamp = startTimestamp.clone();
+        this.repetitionRun = repetitionRun;
+        this.parentRepetition = parentPartListEntry;
+        this.isEnding = isEnding;
+    }
+
+    private sourceMusicPart: SourceMusicPart;
+    private parentRepetition: Repetition;
+    private parentPartListEntry: PartListEntry;
+    private startTimestamp: Fraction;
+    private repetitionRun: number = -1;
+    private isEnding: boolean;
+
+    public get IsRepetition(): boolean {
+        return this.parentRepetition !== undefined;
+    }
+    public get IsEnding(): boolean {
+        return this.isEnding;
+    }
+    public get IsLastRepetitionRun(): boolean {
+        return this.IsRepetition && (this.repetitionRun + 1 === this.parentRepetition.UserNumberOfRepetitions);
+    }
+    public get RepetitionRun(): number {
+        return this.repetitionRun;
+    }
+    public get ParentPartListEntry(): PartListEntry {
+        return this.parentPartListEntry;
+    }
+    public get SourceMusicPart(): SourceMusicPart {
+        return this.sourceMusicPart;
+    }
+    public get StartTimestamp(): Fraction {
+        return this.startTimestamp;
+    }
+    public CompareTo(comp: MappingSourceMusicPart): number {
+        //let comp: MappingSourceMusicPart = <MappingSourceMusicPart>(obj, MappingSourceMusicPart);
+        if (comp !== undefined) {
+            return this.startTimestamp.CompareTo(comp.startTimestamp);
+        } else { return 1; }
+    }
+    //public CompareTo(other: MappingSourceMusicPart): number {
+    //    return this.CompareTo(<Object>other);
+    //}
+}

+ 30 - 0
src/MusicalScore/MusicSource/PartListEntry.ts

@@ -0,0 +1,30 @@
+import {MusicSheet} from "../MusicSheet";
+import {Fraction} from "../../Common/DataObjects/fraction";
+import {SourceMeasure} from "../VoiceData/SourceMeasure";
+
+export abstract class PartListEntry {
+    constructor(musicSheet: MusicSheet) {
+        this.musicSheet = musicSheet;
+    }
+
+    public absoluteTimestamp: Fraction;
+    public startIndex: number;
+    public endIndex: number;
+
+    protected enrolledTimestamps: Fraction[] = [];
+    protected visible: boolean = true;
+    protected musicSheet: MusicSheet;
+
+    public get Visible(): boolean {
+        return this.visible;
+    }
+    public set Visible(value: boolean) {
+        this.visible = value;
+    }
+    public getFirstSourceMeasure(): SourceMeasure {
+        return this.musicSheet.SourceMeasures[this.startIndex];
+    }
+    public getLastSourceMeasure(): SourceMeasure {
+        return this.musicSheet.SourceMeasures[this.endIndex];
+    }
+}

+ 168 - 0
src/MusicalScore/MusicSource/Repetition.ts

@@ -0,0 +1,168 @@
+import {SourceMusicPart} from "./SourceMusicPart";
+import {SourceMeasure} from "../VoiceData/SourceMeasure";
+import {Fraction} from "../../Common/DataObjects/fraction";
+import {MusicSheet} from "../MusicSheet";
+import {RepetitionInstruction} from "../VoiceData/Instructions/RepetitionInstruction";
+import {PartListEntry} from "./PartListEntry";
+
+export class Repetition extends PartListEntry /*implements IRepetition*/ {
+    constructor(musicSheet: MusicSheet, virtualOverallRepetition: boolean) {
+        super(musicSheet);
+        this.musicSheet2 = musicSheet;
+        this.virtualOverallRepetition = virtualOverallRepetition;
+    }
+
+    public startMarker: RepetitionInstruction;
+    public endMarker: RepetitionInstruction;
+    public forwardJumpInstruction: RepetitionInstruction;
+
+    private backwardJumpInstructions: RepetitionInstruction[] = [];
+    private endingParts: RepetitionEndingPart[] = [];
+    private endingIndexDict: { [_: number]: RepetitionEndingPart; } = {};
+    private userNumberOfRepetitions: number = 0;
+    private visibles: boolean[] = [];
+    private fromWords: boolean = false;
+    private musicSheet2: MusicSheet;
+    private repetitonIterationOrder: number[] = [];
+    private numberOfEndings: number = 1;
+    private virtualOverallRepetition: boolean;
+
+    public get BackwardJumpInstructions(): RepetitionInstruction[] {
+        return this.backwardJumpInstructions;
+    }
+    public get EndingIndexDict(): { [_: number]: RepetitionEndingPart; } {
+        return this.endingIndexDict;
+    }
+    public get EndingParts(): RepetitionEndingPart[] {
+        return this.endingParts;
+    }
+    public get Visibles(): boolean[] {
+        return this.visibles;
+    }
+    public set Visibles(value: boolean[]) {
+        this.visibles = value;
+    }
+    public get DefaultNumberOfRepetitions(): number {
+        let defaultNumber: number = 2;
+        if (this.virtualOverallRepetition) { defaultNumber = 1; }
+        return Math.max(defaultNumber, Object.keys(this.endingIndexDict).length, this.checkRepetitionForMultipleLyricVerses());
+    }
+    public get UserNumberOfRepetitions(): number {
+        return this.userNumberOfRepetitions;
+    }
+    public set UserNumberOfRepetitions(value: number) {
+        this.userNumberOfRepetitions = value;
+        this.repetitonIterationOrder = [];
+        let endingsDiff: number = this.userNumberOfRepetitions - this.NumberOfEndings;
+        for (let i: number = 1; i <= this.userNumberOfRepetitions; i++) {
+            if (i <= endingsDiff) {
+                this.repetitonIterationOrder.push(1);
+            } else {
+                this.repetitonIterationOrder.push(i - endingsDiff);
+            }
+        }
+    }
+    public getForwardJumpTargetForIteration(iteration: number): number {
+        let endingIndex: number = this.repetitonIterationOrder[iteration - 1];
+        if (this.endingIndexDict[endingIndex] !== undefined) {
+            return this.endingIndexDict[endingIndex].part.StartIndex;
+        }
+        return -1;
+    }
+    public getBackwardJumpTarget(): number {
+        return this.startMarker.measureIndex;
+    }
+    public SetEndingStartIndex(endingNumbers: number[], startIndex: number): void {
+        let part: RepetitionEndingPart = new RepetitionEndingPart(new SourceMusicPart(this.musicSheet2, startIndex, startIndex));
+        this.endingParts.push(part);
+        for (let endingNumber of endingNumbers) {
+            try {
+                this.endingIndexDict[endingNumber] = part;
+                part.endingIndices.push(endingNumber);
+                if (this.numberOfEndings < endingNumber) {
+                    this.numberOfEndings = endingNumber;
+                }
+            } catch (err) {
+                console.log("Repetition: Exception."); // FIXME
+            }
+
+        }
+    }
+    //public SetEndingStartIndex(endingNumber: number, startIndex: number): void {
+    //    let part: RepetitionEndingPart = new RepetitionEndingPart(new SourceMusicPart(this.musicSheet2, startIndex, startIndex));
+    //    this.endingParts.push(part);
+    //    this.endingIndexDict[endingNumber] = part;
+    //    part.endingIndices.push(endingNumber);
+    //    if (this.numberOfEndings < endingNumber) {
+    //        this.numberOfEndings = endingNumber;
+    //    }
+    //}
+    public setEndingEndIndex(endingNumber: number, endIndex: number): void {
+        if (this.endingIndexDict[endingNumber] !== undefined) {
+            this.endingIndexDict[endingNumber].part.setEndIndex(endIndex);
+        }
+    }
+    public get NumberOfEndings(): number {
+        return this.numberOfEndings;
+    }
+    public get FromWords(): boolean {
+        return this.fromWords;
+    }
+    public set FromWords(value: boolean) {
+        this.fromWords = value;
+    }
+    public get AbsoluteTimestamp(): Fraction {
+        return Fraction.createFromFraction(this.musicSheet2.SourceMeasures[this.startMarker.measureIndex].AbsoluteTimestamp);
+    }
+    public get StartIndex(): number {
+        return this.startMarker.measureIndex;
+    }
+    public get EndIndex(): number {
+        if (this.BackwardJumpInstructions.length === 0) {
+            return this.StartIndex;
+        }
+        let result: number = this.backwardJumpInstructions[this.backwardJumpInstructions.length - 1].measureIndex;
+        if (this.endingIndexDict[this.NumberOfEndings] !== undefined) {
+            result = Math.max(this.endingIndexDict[this.NumberOfEndings].part.EndIndex, result);
+        }
+        return result;
+    }
+    private checkRepetitionForMultipleLyricVerses(): number {
+        let lyricVerses: number = 0;
+        let start: number = this.StartIndex;
+        let end: number = this.EndIndex;
+        for (let measureIndex: number = start; measureIndex <= end; measureIndex++) {
+            let sourceMeasure: SourceMeasure = this.musicSheet2.SourceMeasures[measureIndex];
+            for (let i: number = 0; i < sourceMeasure.CompleteNumberOfStaves; i++) {
+                for (let sourceStaffEntry of sourceMeasure.VerticalSourceStaffEntryContainers[i].StaffEntries) {
+                    if (sourceStaffEntry !== undefined) {
+                        let verses: number = 0;
+                        for (let voiceEntry of sourceStaffEntry.VoiceEntries) {
+                            verses += Object.keys(voiceEntry.LyricsEntries).length;
+                        }
+                        lyricVerses = Math.max(lyricVerses, verses);
+                    }
+                }
+            }
+        }
+        return lyricVerses;
+    }
+    public get FirstSourceMeasureNumber(): number {
+        return this.getFirstSourceMeasure().MeasureNumber;
+    }
+    public get LastSourceMeasureNumber(): number {
+        return this.getLastSourceMeasure().MeasureNumber;
+    }
+
+}
+
+export class RepetitionEndingPart {
+    constructor(endingPart: SourceMusicPart) {
+        this.part = endingPart;
+    }
+    public part: SourceMusicPart;
+    public endingIndices: number[] = [];
+    public ToString(): string {
+      return this.endingIndices.join(", ");
+    }
+}

+ 43 - 0
src/MusicalScore/MusicSource/SourceMusicPart.ts

@@ -0,0 +1,43 @@
+import {PartListEntry} from "./PartListEntry";
+import {Repetition} from "./Repetition";
+import {Fraction} from "../../Common/DataObjects/fraction";
+import {MusicSheet} from "../MusicSheet";
+
+export class SourceMusicPart extends PartListEntry {
+    constructor(musicSheet: MusicSheet, startIndex?: number, endIndex?: number) {
+        super(musicSheet);
+        this.musicSheet = musicSheet;
+        this.startIndex = startIndex;
+        this.endIndex = endIndex;
+    }
+
+    //protected musicSheet: MusicSheet;
+    protected parentRepetition: Repetition;
+    //private startIndex: number;
+    //private endIndex: number;
+
+    public get MeasuresCount(): number {
+        return this.endIndex - this.startIndex + 1;
+    }
+    public get StartIndex(): number {
+        return this.startIndex;
+    }
+    public get EndIndex(): number {
+        return this.endIndex;
+    }
+    public get ParentRepetition(): Repetition {
+        return this.parentRepetition;
+    }
+    public set ParentRepetition(value: Repetition) {
+        this.parentRepetition = value;
+    }
+    public get AbsoluteTimestamp(): Fraction {
+        return Fraction.createFromFraction(this.musicSheet.SourceMeasures[this.startIndex].AbsoluteTimestamp);
+    }
+    public setStartIndex(startIndex: number): void {
+        this.startIndex = startIndex;
+    }
+    public setEndIndex(index: number): void {
+        this.endIndex = index;
+    }
+}

+ 1015 - 0
src/MusicalScore/ScoreIO/InstrumentReader.ts

@@ -0,0 +1,1015 @@
+import {Instrument} from "../Instrument";
+import {MusicSheet} from "../MusicSheet";
+import {VoiceGenerator} from "./VoiceGenerator";
+import {Staff} from "../VoiceData/Staff";
+import {SourceMeasure} from "../VoiceData/SourceMeasure";
+import {SourceStaffEntry} from "../VoiceData/SourceStaffEntry";
+import {ClefInstruction} from "../VoiceData/Instructions/ClefInstruction";
+import {KeyInstruction} from "../VoiceData/Instructions/KeyInstruction";
+import {RhythmInstruction} from "../VoiceData/Instructions/RhythmInstruction";
+import {AbstractNotationInstruction} from "../VoiceData/Instructions/AbstractNotationInstruction";
+import {Fraction} from "../../Common/DataObjects/fraction";
+import {IXmlElement} from "../../Common/FileIO/Xml";
+import {ITextTranslation} from "../Interfaces/ITextTranslation";
+import {MusicSheetReadingException} from "../Exceptions";
+import {ClefEnum} from "../VoiceData/Instructions/ClefInstruction";
+import {RhythmSymbolEnum} from "../VoiceData/Instructions/RhythmInstruction";
+import {KeyEnum} from "../VoiceData/Instructions/KeyInstruction";
+import {IXmlAttribute} from "../../Common/FileIO/Xml";
+import {ChordSymbolContainer} from "../VoiceData/ChordSymbolContainer";
+import {Logging} from "../../Common/logging";
+import {MidiInstrument} from "../VoiceData/Instructions/ClefInstruction";
+//import Dictionary from "typescript-collections/dist/lib/Dictionary";
+
+// FIXME: The following classes are missing
+//type repetitionInstructionReader = any;
+//type ChordSymbolContainer = any;
+//type SlurReader = any;
+//type RepetitionInstructionReader = any;
+//type ExpressionReader = any;
+//declare class MusicSymbolModuleFactory {
+//  public static createSlurReader(x: any): any;
+//  public static createExpressionGenerator(musicSheet: MusicSheet, instrument: Instrument, n: number);
+//}
+//
+//class MetronomeReader {
+//  public static addMetronomeSettings(xmlNode: IXmlElement, musicSheet: MusicSheet): void { }
+//  public static readMetronomeInstructions(xmlNode: IXmlElement, musicSheet: MusicSheet, currentXmlMeasureIndex: number): void { }
+//  public static readTempoInstruction(soundNode: IXmlElement, musicSheet: MusicSheet, currentXmlMeasureIndex: number): void { }
+//}
+//
+//class ChordSymbolReader {
+//  public static readChordSymbol(xmlNode:IXmlElement, musicSheet:MusicSheet, activeKey:any): void {
+//  }
+//}
+
+type repetitionInstructionReader = any;
+
+export class InstrumentReader {
+    constructor(repetitionInstructionReader: repetitionInstructionReader, xmlMeasureList: IXmlElement[], instrument: Instrument) {
+        // (*) this.repetitionInstructionReader = repetitionInstructionReader;
+        this.xmlMeasureList = xmlMeasureList;
+        this.musicSheet = instrument.GetMusicSheet;
+        this.instrument = instrument;
+        this.activeClefs = new Array(instrument.Staves.length);
+        this.activeClefsHaveBeenInitialized = new Array(instrument.Staves.length);
+        for (let i: number = 0; i < instrument.Staves.length; i++) {
+            this.activeClefsHaveBeenInitialized[i] = false;
+        }
+        // FIXME createExpressionGenerators(instrument.Staves.length);
+        // (*) this.slurReader = MusicSymbolModuleFactory.createSlurReader(this.musicSheet);
+    }
+
+    // (*) private repetitionInstructionReader: RepetitionInstructionReader;
+    private xmlMeasureList: IXmlElement[];
+    private musicSheet: MusicSheet;
+    private slurReader: any; // (*) SlurReader;
+    private instrument: Instrument;
+    private voiceGeneratorsDict: { [n: number]: VoiceGenerator; } = {};
+    private staffMainVoiceGeneratorDict: { [staffId: number]: VoiceGenerator } = {};
+    private inSourceMeasureInstrumentIndex: number;
+    private divisions: number = 0;
+    private currentMeasure: SourceMeasure;
+    private previousMeasure: SourceMeasure;
+    private currentXmlMeasureIndex: number = 0;
+    private currentStaff: Staff;
+    private currentStaffEntry: SourceStaffEntry;
+    private activeClefs: ClefInstruction[];
+    private activeKey: KeyInstruction;
+    private activeRhythm: RhythmInstruction;
+    private activeClefsHaveBeenInitialized: boolean[];
+    private activeKeyHasBeenInitialized: boolean = false;
+    private abstractInstructions: [number, AbstractNotationInstruction][] = [];
+    private openChordSymbolContainer: ChordSymbolContainer;
+    // (*) private expressionReaders: ExpressionReader[];
+    private currentVoiceGenerator: VoiceGenerator;
+    //private openSlurDict: { [n: number]: Slur; } = {};
+    private maxTieNoteFraction: Fraction;
+
+    public get ActiveKey(): KeyInstruction {
+        return this.activeKey;
+    }
+
+    public get MaxTieNoteFraction(): Fraction {
+        return this.maxTieNoteFraction;
+    }
+
+    public get ActiveRhythm(): RhythmInstruction {
+        return this.activeRhythm;
+    }
+
+    public set ActiveRhythm(value: RhythmInstruction) {
+        this.activeRhythm = value;
+    }
+
+    public readNextXmlMeasure(currentMeasure: SourceMeasure, measureStartAbsoluteTimestamp: Fraction, guitarPro: boolean): boolean {
+        if (this.currentXmlMeasureIndex >= this.xmlMeasureList.length) {
+            return false;
+        }
+        this.currentMeasure = currentMeasure;
+        this.inSourceMeasureInstrumentIndex = this.musicSheet.getGlobalStaffIndexOfFirstStaff(this.instrument);
+        // (*) if (this.repetitionInstructionReader !== undefined) {
+        //  this.repetitionInstructionReader.prepareReadingMeasure(currentMeasure, this.currentXmlMeasureIndex);
+        //}
+        let currentFraction: Fraction = new Fraction(0, 1);
+        let previousFraction: Fraction = new Fraction(0, 1);
+        let divisionsException: boolean = false;
+        this.maxTieNoteFraction = new Fraction(0, 1);
+        let lastNoteWasGrace: boolean = false;
+        try {
+            let xmlMeasureListArr: IXmlElement[] = this.xmlMeasureList[this.currentXmlMeasureIndex].elements();
+            for (let xmlNode of xmlMeasureListArr) {
+                if (xmlNode.name === "note") {
+                    if (xmlNode.hasAttributes && xmlNode.attribute("print-object") && xmlNode.attribute("print-spacing")) {
+                        continue;
+                    }
+                    let noteStaff: number = 1;
+                    if (this.instrument.Staves.length > 1) {
+                        if (xmlNode.element("staff") !== undefined) {
+                            noteStaff = parseInt(xmlNode.element("staff").value, 10);
+                            if (isNaN(noteStaff)) {
+                                Logging.debug("InstrumentReader.readNextXmlMeasure.get staff number");
+                                noteStaff = 1;
+                            }
+                        }
+                    }
+
+                    this.currentStaff = this.instrument.Staves[noteStaff - 1];
+                    let isChord: boolean = xmlNode.element("chord") !== undefined;
+                    if (xmlNode.element("voice") !== undefined) {
+                        let noteVoice: number = parseInt(xmlNode.element("voice").value, 10);
+                        this.currentVoiceGenerator = this.getOrCreateVoiceGenerator(noteVoice, noteStaff - 1);
+                    } else {
+                        if (!isChord || this.currentVoiceGenerator === undefined) {
+                            this.currentVoiceGenerator = this.getOrCreateVoiceGenerator(1, noteStaff - 1);
+                        }
+                    }
+                    let noteDivisions: number = 0;
+                    let noteDuration: Fraction = new Fraction(0, 1);
+                    let isTuplet: boolean = false;
+                    if (xmlNode.element("duration") !== undefined) {
+                        noteDivisions = parseInt(xmlNode.element("duration").value, 10);
+                        if (!isNaN(noteDivisions)) {
+                            noteDuration = new Fraction(noteDivisions, 4 * this.divisions);
+                            if (noteDivisions === 0) {
+                                noteDuration = this.getNoteDurationFromTypeNode(xmlNode);
+                            }
+                            if (xmlNode.element("time-modification") !== undefined) {
+                                noteDuration = this.getNoteDurationForTuplet(xmlNode);
+                                isTuplet = true;
+                            }
+                        } else {
+                            let errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/NoteDurationError", "Invalid Note Duration.");
+                            this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
+                            Logging.debug("InstrumentReader.readNextXmlMeasure", errorMsg);
+                            continue;
+                        }
+                    }
+
+                    let restNote: boolean = xmlNode.element("rest") !== undefined;
+                    //Logging.log("New note found!", noteDivisions, noteDuration.toString(), restNote);
+                    let isGraceNote: boolean = xmlNode.element("grace") !== undefined || noteDivisions === 0 || isChord && lastNoteWasGrace;
+                    let musicTimestamp: Fraction = currentFraction.clone();
+                    if (isChord) {
+                        musicTimestamp = previousFraction.clone();
+                    }
+                    this.currentStaffEntry = this.currentMeasure.findOrCreateStaffEntry(
+                        musicTimestamp,
+                        this.inSourceMeasureInstrumentIndex + noteStaff - 1,
+                        this.currentStaff
+                    ).staffEntry;
+                    //Logging.log("currentStaffEntry", this.currentStaffEntry, this.currentMeasure.VerticalSourceStaffEntryContainers.length);
+
+                    if (!this.currentVoiceGenerator.hasVoiceEntry() || (!isChord && !isGraceNote && !lastNoteWasGrace) || (!lastNoteWasGrace && isGraceNote)) {
+                        this.currentVoiceGenerator.createVoiceEntry(musicTimestamp, this.currentStaffEntry, !restNote);
+                    }
+                    if (!isGraceNote && !isChord) {
+                        previousFraction = currentFraction.clone();
+                        currentFraction.Add(noteDuration);
+                    }
+                    if (
+                        isChord &&
+                        this.currentStaffEntry !== undefined &&
+                        this.currentStaffEntry.ParentStaff !== this.currentStaff
+                    ) {
+                        this.currentStaffEntry = this.currentVoiceGenerator.checkForStaffEntryLink(
+                            this.inSourceMeasureInstrumentIndex + noteStaff - 1, this.currentStaff, this.currentStaffEntry, this.currentMeasure
+                        );
+                    }
+                    let beginOfMeasure: boolean = (
+                        this.currentStaffEntry !== undefined &&
+                        this.currentStaffEntry.Timestamp !== undefined &&
+                        this.currentStaffEntry.Timestamp.Equals(new Fraction(0, 1)) && !this.currentStaffEntry.hasNotes()
+                    );
+                    this.saveAbstractInstructionList(this.instrument.Staves.length, beginOfMeasure);
+                    if (this.openChordSymbolContainer !== undefined) {
+                        this.currentStaffEntry.ChordContainer = this.openChordSymbolContainer;
+                        this.openChordSymbolContainer = undefined;
+                    }
+                    if (this.activeRhythm !== undefined) {
+                        // (*) this.musicSheet.SheetPlaybackSetting.Rhythm = this.activeRhythm.Rhythm;
+                    }
+                    if (isTuplet) {
+                        this.currentVoiceGenerator.read(
+                            xmlNode, noteDuration.Numerator,
+                            noteDuration.Denominator, restNote, isGraceNote,
+                            this.currentStaffEntry, this.currentMeasure,
+                            measureStartAbsoluteTimestamp,
+                            this.maxTieNoteFraction, isChord, guitarPro
+                        );
+                    } else {
+                        this.currentVoiceGenerator.read(
+                            xmlNode, noteDivisions, 4 * this.divisions,
+                            restNote, isGraceNote, this.currentStaffEntry,
+                            this.currentMeasure, measureStartAbsoluteTimestamp,
+                            this.maxTieNoteFraction, isChord, guitarPro
+                        );
+                    }
+                    let notationsNode: IXmlElement = xmlNode.element("notations");
+                    if (notationsNode !== undefined && notationsNode.element("dynamics") !== undefined) {
+                        // (*) let expressionReader: ExpressionReader = this.expressionReaders[this.readExpressionStaffNumber(xmlNode) - 1];
+                        //if (expressionReader !== undefined) {
+                        //  expressionReader.readExpressionParameters(
+                        //    xmlNode, this.instrument, this.divisions, currentFraction, previousFraction, this.currentMeasure.MeasureNumber, false
+                        //  );
+                        //  expressionReader.read(
+                        //    xmlNode, this.currentMeasure, previousFraction
+                        //  );
+                        //}
+                    }
+                    lastNoteWasGrace = isGraceNote;
+                } else if (xmlNode.name === "attributes") {
+                    let divisionsNode: IXmlElement = xmlNode.element("divisions");
+                    if (divisionsNode !== undefined) {
+                        this.divisions = parseInt(divisionsNode.value, 10);
+                        if (isNaN(this.divisions)) {
+                            let errorMsg: string = ITextTranslation.translateText(  "ReaderErrorMessages/DivisionError",
+                                                                                    "Invalid divisions value at Instrument: ");
+                            Logging.debug("InstrumentReader.readNextXmlMeasure", errorMsg);
+                            this.divisions = this.readDivisionsFromNotes();
+                            if (this.divisions > 0) {
+                                this.musicSheet.SheetErrors.push(errorMsg + this.instrument.Name);
+                            } else {
+                                divisionsException = true;
+                                throw new MusicSheetReadingException(errorMsg + this.instrument.Name);
+                            }
+                        }
+
+                    }
+                    if (
+                        xmlNode.element("divisions") === undefined &&
+                        this.divisions === 0 &&
+                        this.currentXmlMeasureIndex === 0
+                    ) {
+                        let errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/DivisionError", "Invalid divisions value at Instrument: ");
+                        this.divisions = this.readDivisionsFromNotes();
+                        if (this.divisions > 0) {
+                            this.musicSheet.SheetErrors.push(errorMsg + this.instrument.Name);
+                        } else {
+                            divisionsException = true;
+                            throw new MusicSheetReadingException(errorMsg + this.instrument.Name);
+                        }
+                    }
+                    this.addAbstractInstruction(xmlNode, guitarPro);
+                    if (currentFraction.Equals(new Fraction(0, 1)) &&
+                        this.isAttributesNodeAtBeginOfMeasure(this.xmlMeasureList[this.currentXmlMeasureIndex], xmlNode)) {
+                        this.saveAbstractInstructionList(this.instrument.Staves.length, true);
+                    }
+                    if (this.isAttributesNodeAtEndOfMeasure(this.xmlMeasureList[this.currentXmlMeasureIndex], xmlNode)) {
+                        this.saveClefInstructionAtEndOfMeasure();
+                    }
+                } else if (xmlNode.name === "forward") {
+                    let forFraction: number = parseInt(xmlNode.element("duration").value, 10);
+                    currentFraction.Add(new Fraction(forFraction, 4 * this.divisions));
+                } else if (xmlNode.name === "backup") {
+                    let backFraction: number = parseInt(xmlNode.element("duration").value, 10);
+                    currentFraction.Sub(new Fraction(backFraction, 4 * this.divisions));
+                    if (currentFraction.Numerator < 0) {
+                        currentFraction = new Fraction(0, 1);
+                    }
+                    previousFraction.Sub(new Fraction(backFraction, 4 * this.divisions));
+                    if (previousFraction.Numerator < 0) {
+                        previousFraction = new Fraction(0, 1);
+                    }
+                } else if (xmlNode.name === "direction") {
+                    // unused let directionTypeNode: IXmlElement = xmlNode.element("direction-type");
+                    // (*) MetronomeReader.readMetronomeInstructions(xmlNode, this.musicSheet, this.currentXmlMeasureIndex);
+                    let relativePositionInMeasure: number = Math.min(1, currentFraction.RealValue);
+                    if (this.activeRhythm !== undefined && this.activeRhythm.Rhythm !== undefined) {
+                        relativePositionInMeasure /= this.activeRhythm.Rhythm.RealValue;
+                    }
+                    // unused: let handeled: boolean = false;
+                    // (*) if (this.repetitionInstructionReader !== undefined) {
+                    //  handeled = this.repetitionInstructionReader.handleRepetitionInstructionsFromWordsOrSymbols(directionTypeNode,
+                    //                                                                                              relativePositionInMeasure);
+                    //}
+                    //if (!handeled) {
+                    //  let expressionReader: ExpressionReader = this.expressionReaders[0];
+                    //  let staffIndex: number = this.readExpressionStaffNumber(xmlNode) - 1;
+                    //  if (staffIndex < this.expressionReaders.length) {
+                    //    expressionReader = this.expressionReaders[staffIndex];
+                    //  }
+                    //  if (expressionReader !== undefined) {
+                    //    if (directionTypeNode.element("octave-shift") !== undefined) {
+                    //      expressionReader.readExpressionParameters(
+                    //        xmlNode, this.instrument, this.divisions, currentFraction, previousFraction, this.currentMeasure.MeasureNumber, true
+                    //      );
+                    //      expressionReader.addOctaveShift(xmlNode, this.currentMeasure, previousFraction.clone());
+                    //    }
+                    //    expressionReader.readExpressionParameters(
+                    //      xmlNode, this.instrument, this.divisions, currentFraction, previousFraction, this.currentMeasure.MeasureNumber, false
+                    //    );
+                    //    expressionReader.read(xmlNode, this.currentMeasure, currentFraction);
+                    //  }
+                    //}
+                } else if (xmlNode.name === "barline") {
+                    // (*)
+                    //if (this.repetitionInstructionReader !== undefined) {
+                    //  let measureEndsSystem: boolean = false;
+                    //  this.repetitionInstructionReader.handleLineRepetitionInstructions(xmlNode, measureEndsSystem);
+                    //  if (measureEndsSystem) {
+                    //    this.currentMeasure.BreakSystemAfter = true;
+                    //    this.currentMeasure.endsPiece = true;
+                    //  }
+                    //}
+                } else if (xmlNode.name === "sound") {
+                    // (*) MetronomeReader.readTempoInstruction(xmlNode, this.musicSheet, this.currentXmlMeasureIndex);
+                } else if (xmlNode.name === "harmony") {
+                    // (*) this.openChordSymbolContainer = ChordSymbolReader.readChordSymbol(xmlNode, this.musicSheet, this.activeKey);
+                }
+            }
+            for (let j in this.voiceGeneratorsDict) {
+                if (this.voiceGeneratorsDict.hasOwnProperty(j)) {
+                    let voiceGenerator: VoiceGenerator = this.voiceGeneratorsDict[j];
+                    voiceGenerator.checkForOpenBeam();
+                    voiceGenerator.checkForOpenGraceNotes();
+                }
+            }
+            if (this.currentXmlMeasureIndex === this.xmlMeasureList.length - 1) {
+                for (let i: number = 0; i < this.instrument.Staves.length; i++) {
+                    if (!this.activeClefsHaveBeenInitialized[i]) {
+                        this.createDefaultClefInstruction(this.musicSheet.getGlobalStaffIndexOfFirstStaff(this.instrument) + i);
+                    }
+                }
+                if (!this.activeKeyHasBeenInitialized) {
+                    this.createDefaultKeyInstruction();
+                }
+                // (*)
+                //for (let i: number = 0; i < this.expressionReaders.length; i++) {
+                //  let reader: ExpressionReader = this.expressionReaders[i];
+                //  if (reader !== undefined) {
+                //    reader.checkForOpenExpressions(this.currentMeasure, currentFraction);
+                //  }
+                //}
+            }
+        } catch (e) {
+            if (divisionsException) {
+                throw new MusicSheetReadingException(e.Message);
+            }
+            let errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/MeasureError", "Error while reading Measure.");
+            this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
+            Logging.debug("InstrumentReader.readNextXmlMeasure", errorMsg, e);
+        }
+
+        this.previousMeasure = this.currentMeasure;
+        this.currentXmlMeasureIndex += 1;
+        return true;
+    }
+
+    public doCalculationsAfterDurationHasBeenSet(): void {
+        for (let j in this.voiceGeneratorsDict) {
+            if (this.voiceGeneratorsDict.hasOwnProperty(j)) {
+                this.voiceGeneratorsDict[j].checkOpenTies();
+            }
+        }
+    }
+
+    private getOrCreateVoiceGenerator(voiceId: number, staffId: number): VoiceGenerator {
+        let staff: Staff = this.instrument.Staves[staffId];
+        let voiceGenerator: VoiceGenerator = this.voiceGeneratorsDict[voiceId];
+        if (voiceGenerator !== undefined) {
+            if (staff.Voices.indexOf(voiceGenerator.GetVoice) === -1) {
+                staff.Voices.push(voiceGenerator.GetVoice);
+            }
+        } else {
+            let mainVoiceGenerator: VoiceGenerator = this.staffMainVoiceGeneratorDict[staffId];
+            if (mainVoiceGenerator !== undefined) {
+                voiceGenerator = new VoiceGenerator(this.instrument, voiceId, this.slurReader, mainVoiceGenerator.GetVoice);
+                staff.Voices.push(voiceGenerator.GetVoice);
+                this.voiceGeneratorsDict[voiceId] = voiceGenerator;
+            } else {
+                voiceGenerator = new VoiceGenerator(this.instrument, voiceId, this.slurReader);
+                staff.Voices.push(voiceGenerator.GetVoice);
+                this.voiceGeneratorsDict[voiceId] = voiceGenerator;
+                this.staffMainVoiceGeneratorDict[staffId] = voiceGenerator;
+            }
+        }
+        return voiceGenerator;
+    }
+
+
+    //private createExpressionGenerators(numberOfStaves: number): void {
+    //  // (*)
+    //  //this.expressionReaders = new Array(numberOfStaves);
+    //  //for (let i: number = 0; i < numberOfStaves; i++) {
+    //  //  this.expressionReaders[i] = MusicSymbolModuleFactory.createExpressionGenerator(this.musicSheet, this.instrument, i + 1);
+    //  //}
+    //}
+
+
+    private createDefaultClefInstruction(staffIndex: number): void {
+        let first: SourceMeasure;
+        if (this.musicSheet.SourceMeasures.length > 0) {
+            first = this.musicSheet.SourceMeasures[0];
+        } else {
+            first = this.currentMeasure;
+        }
+        let clefInstruction: ClefInstruction = new ClefInstruction(ClefEnum.G, 0, 2);
+        let firstStaffEntry: SourceStaffEntry;
+        if (first.FirstInstructionsStaffEntries[staffIndex] === undefined) {
+            firstStaffEntry = new SourceStaffEntry(undefined, undefined);
+            first.FirstInstructionsStaffEntries[staffIndex] = firstStaffEntry;
+        } else {
+            firstStaffEntry = first.FirstInstructionsStaffEntries[staffIndex];
+            firstStaffEntry.removeFirstInstructionOfTypeClefInstruction();
+        }
+        clefInstruction.Parent = firstStaffEntry;
+        firstStaffEntry.Instructions.splice(0, 0, clefInstruction);
+    }
+
+    private createDefaultKeyInstruction(): void {
+        let first: SourceMeasure;
+        if (this.musicSheet.SourceMeasures.length > 0) {
+            first = this.musicSheet.SourceMeasures[0];
+        } else {
+            first = this.currentMeasure;
+        }
+        let keyInstruction: KeyInstruction = new KeyInstruction(undefined, 0, KeyEnum.major);
+        for (let j: number = this.inSourceMeasureInstrumentIndex; j < this.inSourceMeasureInstrumentIndex + this.instrument.Staves.length; j++) {
+            if (first.FirstInstructionsStaffEntries[j] === undefined) {
+                let firstStaffEntry: SourceStaffEntry = new SourceStaffEntry(undefined, undefined);
+                first.FirstInstructionsStaffEntries[j] = firstStaffEntry;
+                keyInstruction.Parent = firstStaffEntry;
+                firstStaffEntry.Instructions.push(keyInstruction);
+            } else {
+                let firstStaffEntry: SourceStaffEntry = first.FirstInstructionsStaffEntries[j];
+                keyInstruction.Parent = firstStaffEntry;
+                firstStaffEntry.removeFirstInstructionOfTypeKeyInstruction();
+                if (firstStaffEntry.Instructions[0] instanceof ClefInstruction) {
+                    firstStaffEntry.Instructions.splice(1, 0, keyInstruction);
+                } else {
+                    firstStaffEntry.Instructions.splice(0, 0, keyInstruction);
+                }
+            }
+        }
+    }
+
+    private isAttributesNodeAtBeginOfMeasure(parentNode: IXmlElement, attributesNode: IXmlElement): boolean {
+        let children: IXmlElement[] = parentNode.elements();
+        let attributesNodeIndex: number = children.indexOf(attributesNode); // FIXME | 0
+        if (attributesNodeIndex > 0 && children[attributesNodeIndex - 1].name === "backup") {
+            return true;
+        }
+        let firstNoteNodeIndex: number = -1;
+        for (let i: number = 0; i < children.length; i++) {
+            if (children[i].name === "note") {
+                firstNoteNodeIndex = i;
+                break;
+            }
+        }
+        return (attributesNodeIndex < firstNoteNodeIndex && firstNoteNodeIndex > 0) || (firstNoteNodeIndex < 0);
+    }
+
+    private isAttributesNodeAtEndOfMeasure(parentNode: IXmlElement, attributesNode: IXmlElement): boolean {
+        let childs: IXmlElement[] = parentNode.elements().slice();
+        let attributesNodeIndex: number = 0;
+        for (let i: number = 0; i < childs.length; i++) {
+            if (childs[i] === attributesNode) {
+                attributesNodeIndex = i;
+                break;
+            }
+        }
+        let nextNoteNodeIndex: number = 0;
+        for (let i: number = attributesNodeIndex; i < childs.length; i++) {
+            if (childs[i].name === "note") {
+                nextNoteNodeIndex = i;
+                break;
+            }
+        }
+        return attributesNodeIndex > nextNoteNodeIndex;
+    }
+
+    private getNoteDurationFromTypeNode(xmlNode: IXmlElement): Fraction {
+        if (xmlNode.element("type") !== undefined) {
+            let typeNode: IXmlElement = xmlNode.element("type");
+            if (typeNode !== undefined) {
+                let type: string = typeNode.value;
+                return this.currentVoiceGenerator.getNoteDurationFromType(type);
+            }
+        }
+        return new Fraction(0, 4 * this.divisions);
+    }
+
+    private addAbstractInstruction(node: IXmlElement, guitarPro: boolean): void {
+        if (node.element("divisions") !== undefined) {
+            if (node.elements().length === 1) {
+                return;
+            }
+        }
+        let transposeNode: IXmlElement = node.element("transpose");
+        if (transposeNode !== undefined) {
+            let chromaticNode: IXmlElement = transposeNode.element("chromatic");
+            if (chromaticNode !== undefined) {
+                this.instrument.PlaybackTranspose = parseInt(chromaticNode.value, 10);
+            }
+        }
+        let clefList: IXmlElement[] = node.elements("clef");
+        let errorMsg: string;
+        if (clefList.length > 0) {
+            for (let idx: number = 0, len: number = clefList.length; idx < len; ++idx) {
+                let nodeList: IXmlElement = clefList[idx];
+                let clefEnum: ClefEnum = ClefEnum.G;
+                let line: number = 2;
+                let staffNumber: number = 1;
+                let clefOctaveOffset: number = 0;
+                let lineNode: IXmlElement = nodeList.element("line");
+                if (lineNode !== undefined) {
+                    try {
+                        line = parseInt(lineNode.value, 10);
+                    } catch (ex) {
+                        errorMsg = ITextTranslation.translateText(
+                            "ReaderErrorMessages/ClefLineError",
+                            "Invalid clef line given -> using default clef line."
+                        );
+                        this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
+                        line = 2;
+                        Logging.debug("InstrumentReader.addAbstractInstruction", errorMsg, ex);
+                    }
+
+                }
+                let signNode: IXmlElement = nodeList.element("sign");
+                if (signNode !== undefined) {
+                    try {
+                        clefEnum = ClefEnum[signNode.value];
+                        if (!ClefInstruction.isSupportedClef(clefEnum)) {
+                            if (clefEnum === ClefEnum.TAB && guitarPro) {
+                                clefOctaveOffset = -1;
+                            }
+                            errorMsg = ITextTranslation.translateText(
+                                "ReaderErrorMessages/ClefError",
+                                "Unsupported clef found -> using default clef."
+                            );
+                            this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
+                            clefEnum = ClefEnum.G;
+                            line = 2;
+                        }
+                    } catch (e) {
+                        errorMsg = ITextTranslation.translateText(
+                            "ReaderErrorMessages/ClefError",
+                            "Invalid clef found -> using default clef."
+                        );
+                        this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
+                        clefEnum = ClefEnum.G;
+                        line = 2;
+                        Logging.debug("InstrumentReader.addAbstractInstruction", errorMsg, e);
+                    }
+
+                }
+                let clefOctaveNode: IXmlElement = nodeList.element("clef-octave-change");
+                if (clefOctaveNode !== undefined) {
+                    try {
+                        clefOctaveOffset = parseInt(clefOctaveNode.value, 10);
+                    } catch (e) {
+                        errorMsg = ITextTranslation.translateText(
+                            "ReaderErrorMessages/ClefOctaveError",
+                            "Invalid clef octave found -> using default clef octave."
+                        );
+                        this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
+                        clefOctaveOffset = 0;
+                    }
+
+                }
+                if (nodeList.hasAttributes && nodeList.attributes()[0].name === "number") {
+                    try {
+                        staffNumber = parseInt(nodeList.attributes()[0].value, 10);
+                    } catch (err) {
+                        errorMsg = ITextTranslation.translateText(
+                            "ReaderErrorMessages/ClefError",
+                            "Invalid clef found -> using default clef."
+                        );
+                        this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
+                        staffNumber = 1;
+                    }
+                }
+
+                let clefInstruction: ClefInstruction = new ClefInstruction(clefEnum, clefOctaveOffset, line);
+                this.abstractInstructions.push([staffNumber, clefInstruction]);
+            }
+        }
+        if (node.element("key") !== undefined && this.instrument.MidiInstrumentId !== MidiInstrument.Percussion) {
+            let key: number = 0;
+            let keyNode: IXmlElement = node.element("key").element("fifths");
+            if (keyNode !== undefined) {
+                try {
+                    key = parseInt(keyNode.value, 10);
+                } catch (ex) {
+                    errorMsg = ITextTranslation.translateText(
+                        "ReaderErrorMessages/KeyError",
+                        "Invalid key found -> set to default."
+                    );
+                    this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
+                    key = 0;
+                    Logging.debug("InstrumentReader.addAbstractInstruction", errorMsg, ex);
+                }
+
+            }
+            let keyEnum: KeyEnum = KeyEnum.none;
+            let modeNode: IXmlElement = node.element("key");
+            if (modeNode !== undefined) {
+                modeNode = modeNode.element("mode");
+            }
+            if (modeNode !== undefined) {
+                try {
+                    keyEnum = KeyEnum[modeNode.value];
+                } catch (ex) {
+                    errorMsg = ITextTranslation.translateText(
+                        "ReaderErrorMessages/KeyError",
+                        "Invalid key found -> set to default."
+                    );
+                    this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
+                    keyEnum = KeyEnum.major;
+                    Logging.debug("InstrumentReader.addAbstractInstruction", errorMsg, ex);
+                }
+
+            }
+            let keyInstruction: KeyInstruction = new KeyInstruction(undefined, key, keyEnum);
+            this.abstractInstructions.push([1, keyInstruction]);
+        }
+        if (node.element("time") !== undefined) {
+            let symbolEnum: RhythmSymbolEnum = RhythmSymbolEnum.NONE;
+            let timeNode: IXmlElement = node.element("time");
+            if (timeNode !== undefined && timeNode.hasAttributes) {
+                let firstAttr: IXmlAttribute = timeNode.firstAttribute;
+                if (firstAttr.name === "symbol") {
+                    if (firstAttr.value === "common") {
+                        symbolEnum = RhythmSymbolEnum.COMMON;
+                    } else if (firstAttr.value === "cut") {
+                        symbolEnum = RhythmSymbolEnum.CUT;
+                    }
+                }
+            }
+            let num: number = 0;
+            let denom: number = 0;
+            let senzaMisura: boolean = (timeNode !== undefined && timeNode.element("senza-misura") !== undefined);
+            let timeList: IXmlElement[] = node.elements("time");
+            let beatsList: IXmlElement[] = [];
+            let typeList: IXmlElement[] = [];
+            for (let idx: number = 0, len: number = timeList.length; idx < len; ++idx) {
+                let xmlNode: IXmlElement = timeList[idx];
+                beatsList.push.apply(beatsList, xmlNode.elements("beats"));
+                typeList.push.apply(typeList, xmlNode.elements("beat-type"));
+            }
+            if (!senzaMisura) {
+                try {
+                    if (beatsList !== undefined && beatsList.length > 0 && typeList !== undefined && beatsList.length === typeList.length) {
+                        let length: number = beatsList.length;
+                        let fractions: Fraction[] = new Array(length);
+                        let maxDenom: number = 0;
+                        for (let i: number = 0; i < length; i++) {
+                            let s: string = beatsList[i].value;
+                            let n: number = 0;
+                            let d: number = 0;
+                            if (s.indexOf("+") !== -1) {
+                                let numbers: string[] = s.split("+");
+                                for (let idx: number = 0, len: number = numbers.length; idx < len; ++idx) {
+                                    n += parseInt(numbers[idx], 10);
+                                }
+                            } else {
+                                n = parseInt(s, 10);
+                            }
+                            d = parseInt(typeList[i].value, 10);
+                            maxDenom = Math.max(maxDenom, d);
+                            fractions[i] = new Fraction(n, d, false);
+                        }
+                        for (let i: number = 0; i < length; i++) {
+                            if (fractions[i].Denominator === maxDenom) {
+                                num += fractions[i].Numerator;
+                            } else {
+                                num += (maxDenom / fractions[i].Denominator) * fractions[i].Numerator;
+                            }
+                        }
+                        denom = maxDenom;
+                    } else {
+                        num = parseInt(node.element("time").element("beats").value, 10);
+                        denom = parseInt(node.element("time").element("beat-type").value, 10);
+                    }
+                } catch (ex) {
+                    errorMsg = ITextTranslation.translateText("ReaderErrorMessages/RhythmError", "Invalid rhythm found -> set to default.");
+                    this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
+                    num = 4;
+                    denom = 4;
+                    Logging.debug("InstrumentReader.addAbstractInstruction", errorMsg, ex);
+                }
+
+                if ((num === 4 && denom === 4) || (num === 2 && denom === 2)) {
+                    symbolEnum = RhythmSymbolEnum.NONE;
+                }
+                this.abstractInstructions.push([1, new RhythmInstruction(
+                    new Fraction(num, denom, false), num, denom, symbolEnum
+                )]);
+            } else {
+                this.abstractInstructions.push([1, new RhythmInstruction(new Fraction(4, 4, false), 4, 4, RhythmSymbolEnum.NONE)]);
+            }
+        }
+    }
+
+    private saveAbstractInstructionList(numberOfStaves: number, beginOfMeasure: boolean): void {
+        for (let i: number = this.abstractInstructions.length - 1; i >= 0; i--) {
+            let pair: [number, AbstractNotationInstruction] = this.abstractInstructions[i];
+            let key: number = pair[0];
+            let value: AbstractNotationInstruction = pair[1];
+            if (value instanceof ClefInstruction) {
+                let clefInstruction: ClefInstruction = <ClefInstruction>value;
+                if (this.currentXmlMeasureIndex === 0 || (key <= this.activeClefs.length && clefInstruction !== this.activeClefs[key - 1])) {
+                    if (!beginOfMeasure && this.currentStaffEntry !== undefined && !this.currentStaffEntry.hasNotes() && key - 1
+                        === this.instrument.Staves.indexOf(this.currentStaffEntry.ParentStaff)) {
+                        let newClefInstruction: ClefInstruction = clefInstruction;
+                        newClefInstruction.Parent = this.currentStaffEntry;
+                        this.currentStaffEntry.removeFirstInstructionOfTypeClefInstruction();
+                        this.currentStaffEntry.Instructions.push(newClefInstruction);
+                        this.activeClefs[key - 1] = clefInstruction;
+                        this.abstractInstructions.splice(i, 1);
+                    } else if (beginOfMeasure) {
+                        let firstStaffEntry: SourceStaffEntry;
+                        if (this.currentMeasure !== undefined) {
+                            let newClefInstruction: ClefInstruction = clefInstruction;
+                            let sseIndex: number = this.inSourceMeasureInstrumentIndex + key - 1;
+                            let firstSse: SourceStaffEntry = this.currentMeasure.FirstInstructionsStaffEntries[sseIndex];
+                            if (this.currentXmlMeasureIndex === 0) {
+                                if (firstSse === undefined) {
+                                    firstStaffEntry = new SourceStaffEntry(undefined, undefined);
+                                    this.currentMeasure.FirstInstructionsStaffEntries[sseIndex] = firstStaffEntry;
+                                    newClefInstruction.Parent = firstStaffEntry;
+                                    firstStaffEntry.Instructions.push(newClefInstruction);
+                                    this.activeClefsHaveBeenInitialized[key - 1] = true;
+                                } else if (this.currentMeasure.FirstInstructionsStaffEntries[sseIndex]
+                                    !==
+                                    undefined && !(firstSse.Instructions[0] instanceof ClefInstruction)) {
+                                    firstStaffEntry = firstSse;
+                                    newClefInstruction.Parent = firstStaffEntry;
+                                    firstStaffEntry.removeFirstInstructionOfTypeClefInstruction();
+                                    firstStaffEntry.Instructions.splice(0, 0, newClefInstruction);
+                                    this.activeClefsHaveBeenInitialized[key - 1] = true;
+                                } else {
+                                    let lastStaffEntry: SourceStaffEntry = new SourceStaffEntry(undefined, undefined);
+                                    this.currentMeasure.LastInstructionsStaffEntries[sseIndex] = lastStaffEntry;
+                                    newClefInstruction.Parent = lastStaffEntry;
+                                    lastStaffEntry.Instructions.push(newClefInstruction);
+                                }
+                            } else if (!this.activeClefsHaveBeenInitialized[key - 1]) {
+                                let first: SourceMeasure = this.musicSheet.SourceMeasures[0];
+                                if (first.FirstInstructionsStaffEntries[sseIndex] === undefined) {
+                                    firstStaffEntry = new SourceStaffEntry(undefined, undefined);
+                                } else {
+                                    firstStaffEntry = first.FirstInstructionsStaffEntries[sseIndex];
+                                    firstStaffEntry.removeFirstInstructionOfTypeClefInstruction();
+                                }
+                                newClefInstruction.Parent = firstStaffEntry;
+                                firstStaffEntry.Instructions.splice(0, 0, newClefInstruction);
+                                this.activeClefsHaveBeenInitialized[key - 1] = true;
+                            } else {
+                                let lastStaffEntry: SourceStaffEntry = new SourceStaffEntry(undefined, undefined);
+                                this.previousMeasure.LastInstructionsStaffEntries[sseIndex] = lastStaffEntry;
+                                newClefInstruction.Parent = lastStaffEntry;
+                                lastStaffEntry.Instructions.push(newClefInstruction);
+                            }
+                            this.activeClefs[key - 1] = clefInstruction;
+                            this.abstractInstructions.splice(i, 1);
+                        }
+                    }
+                } else if (key <= this.activeClefs.length && clefInstruction === this.activeClefs[key - 1]) {
+                    this.abstractInstructions.splice(i, 1);
+                }
+            }
+            if (value instanceof KeyInstruction) {
+                let keyInstruction: KeyInstruction = <KeyInstruction>value;
+                if (this.activeKey === undefined || this.activeKey.Key !== keyInstruction.Key) {
+                    this.activeKey = keyInstruction;
+                    this.abstractInstructions.splice(i, 1);
+                    let sourceMeasure: SourceMeasure;
+                    if (!this.activeKeyHasBeenInitialized) {
+                        this.activeKeyHasBeenInitialized = true;
+                        if (this.currentXmlMeasureIndex > 0) {
+                            sourceMeasure = this.musicSheet.SourceMeasures[0];
+                        } else {
+                            sourceMeasure = this.currentMeasure;
+                        }
+                    } else {
+                        sourceMeasure = this.currentMeasure;
+                    }
+                    if (sourceMeasure !== undefined) {
+                        for (let j: number = this.inSourceMeasureInstrumentIndex; j < this.inSourceMeasureInstrumentIndex + numberOfStaves; j++) {
+                            let newKeyInstruction: KeyInstruction = keyInstruction;
+                            if (sourceMeasure.FirstInstructionsStaffEntries[j] === undefined) {
+                                let firstStaffEntry: SourceStaffEntry = new SourceStaffEntry(undefined, undefined);
+                                sourceMeasure.FirstInstructionsStaffEntries[j] = firstStaffEntry;
+                                newKeyInstruction.Parent = firstStaffEntry;
+                                firstStaffEntry.Instructions.push(newKeyInstruction);
+                            } else {
+                                let firstStaffEntry: SourceStaffEntry = sourceMeasure.FirstInstructionsStaffEntries[j];
+                                newKeyInstruction.Parent = firstStaffEntry;
+                                firstStaffEntry.removeFirstInstructionOfTypeKeyInstruction();
+                                if (firstStaffEntry.Instructions.length === 0) {
+                                    firstStaffEntry.Instructions.push(newKeyInstruction);
+                                } else {
+                                    if (firstStaffEntry.Instructions[0] instanceof ClefInstruction) {
+                                        firstStaffEntry.Instructions.splice(1, 0, newKeyInstruction);
+                                    } else {
+                                        firstStaffEntry.Instructions.splice(0, 0, newKeyInstruction);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                } else {
+                    this.abstractInstructions.splice(i, 1);
+                }
+            }
+            if (value instanceof RhythmInstruction) {
+                let rhythmInstruction: RhythmInstruction = <RhythmInstruction>value;
+                if (this.activeRhythm === undefined || this.activeRhythm !== rhythmInstruction) {
+                    this.activeRhythm = rhythmInstruction;
+                    this.abstractInstructions.splice(i, 1);
+                    if (this.currentMeasure !== undefined) {
+                        for (let j: number = this.inSourceMeasureInstrumentIndex; j < this.inSourceMeasureInstrumentIndex + numberOfStaves; j++) {
+                            let newRhythmInstruction: RhythmInstruction = rhythmInstruction;
+                            let firstStaffEntry: SourceStaffEntry;
+                            if (this.currentMeasure.FirstInstructionsStaffEntries[j] === undefined) {
+                                firstStaffEntry = new SourceStaffEntry(undefined, undefined);
+                                this.currentMeasure.FirstInstructionsStaffEntries[j] = firstStaffEntry;
+                            } else {
+                                firstStaffEntry = this.currentMeasure.FirstInstructionsStaffEntries[j];
+                                firstStaffEntry.removeFirstInstructionOfTypeRhythmInstruction();
+                            }
+                            newRhythmInstruction.Parent = firstStaffEntry;
+                            firstStaffEntry.Instructions.push(newRhythmInstruction);
+                        }
+                    }
+                } else {
+                    this.abstractInstructions.splice(i, 1);
+                }
+            }
+        }
+    }
+
+    private saveClefInstructionAtEndOfMeasure(): void {
+        for (let i: number = this.abstractInstructions.length - 1; i >= 0; i--) {
+            let key: number = this.abstractInstructions[i][0];
+            let value: AbstractNotationInstruction = this.abstractInstructions[i][1];
+            if (value instanceof ClefInstruction) {
+                let clefInstruction: ClefInstruction = <ClefInstruction>value;
+                if (
+                    (this.activeClefs[key - 1] === undefined) ||
+                    (clefInstruction.ClefType !== this.activeClefs[key - 1].ClefType || (
+                        clefInstruction.ClefType === this.activeClefs[key - 1].ClefType &&
+                        clefInstruction.Line !== this.activeClefs[key - 1].Line
+                    ))) {
+                    let lastStaffEntry: SourceStaffEntry = new SourceStaffEntry(undefined, undefined);
+                    this.currentMeasure.LastInstructionsStaffEntries[this.inSourceMeasureInstrumentIndex + key - 1] = lastStaffEntry;
+                    let newClefInstruction: ClefInstruction = clefInstruction;
+                    newClefInstruction.Parent = lastStaffEntry;
+                    lastStaffEntry.Instructions.push(newClefInstruction);
+                    this.activeClefs[key - 1] = clefInstruction;
+                    this.abstractInstructions.splice(i, 1);
+                }
+            }
+        }
+    }
+
+    private getNoteDurationForTuplet(xmlNode: IXmlElement): Fraction {
+        let duration: Fraction = new Fraction(0, 1);
+        let typeDuration: Fraction = this.getNoteDurationFromTypeNode(xmlNode);
+        if (xmlNode.element("time-modification") !== undefined) {
+            let time: IXmlElement = xmlNode.element("time-modification");
+            if (time !== undefined) {
+                if (time.element("actual-notes") !== undefined && time.element("normal-notes") !== undefined) {
+                    let actualNotes: IXmlElement = time.element("actual-notes");
+                    let normalNotes: IXmlElement = time.element("normal-notes");
+                    if (actualNotes !== undefined && normalNotes !== undefined) {
+                        let actual: number = parseInt(actualNotes.value, 10);
+                        let normal: number = parseInt(normalNotes.value, 10);
+                        duration = new Fraction(normal * typeDuration.Numerator, actual * typeDuration.Denominator);
+                    }
+                }
+            }
+        }
+        return duration;
+    }
+
+    //private readExpressionStaffNumber(xmlNode: IXmlElement): number {
+    //  let directionStaffNumber: number = 1;
+    //  if (xmlNode.element("staff") !== undefined) {
+    //    let staffNode: IXmlElement = xmlNode.element("staff");
+    //    if (staffNode !== undefined) {
+    //      try {
+    //        directionStaffNumber = parseInt(staffNode.value, 10);
+    //      } catch (ex) {
+    //        let errorMsg: string = ITextTranslation.translateText(
+    //          "ReaderErrorMessages/ExpressionStaffError", "Invalid Expression staff number -> set to default."
+    //        );
+    //        this.musicSheet.SheetErrors.pushTemp(errorMsg);
+    //        directionStaffNumber = 1;
+    //        logging.debug("InstrumentReader.readExpressionStaffNumber", errorMsg, ex);
+    //      }
+    //
+    //    }
+    //  }
+    //  return directionStaffNumber;
+    //}
+    private readDivisionsFromNotes(): number {
+        let divisionsFromNote: number = 0;
+        let xmlMeasureIndex: number = this.currentXmlMeasureIndex;
+        let read: boolean = false;
+        while (!read) {
+            let xmlMeasureListArr: IXmlElement[] = this.xmlMeasureList[xmlMeasureIndex].elements();
+            for (let idx: number = 0, len: number = xmlMeasureListArr.length; idx < len; ++idx) {
+                let xmlNode: IXmlElement = xmlMeasureListArr[idx];
+                if (xmlNode.name === "note" && xmlNode.element("time-modification") === undefined) {
+                    if (xmlNode.element("duration") !== undefined && xmlNode.element("type") !== undefined) {
+                        let durationNode: IXmlElement = xmlNode.element("duration");
+                        let typeNode: IXmlElement = xmlNode.element("type");
+                        if (durationNode !== undefined && typeNode !== undefined) {
+                            let type: string = typeNode.value;
+                            let noteDuration: number = 0;
+                            try {
+                                noteDuration = parseInt(durationNode.value, 10);
+                            } catch (ex) {
+                                Logging.debug("InstrumentReader.readDivisionsFromNotes", ex);
+                                continue;
+                            }
+
+                            switch (type) {
+                                case "1024th":
+                                    divisionsFromNote = (noteDuration / 4) * 1024;
+                                    break;
+                                case "512th":
+                                    divisionsFromNote = (noteDuration / 4) * 512;
+                                    break;
+                                case "256th":
+                                    divisionsFromNote = (noteDuration / 4) * 256;
+                                    break;
+                                case "128th":
+                                    divisionsFromNote = (noteDuration / 4) * 128;
+                                    break;
+                                case "64th":
+                                    divisionsFromNote = (noteDuration / 4) * 64;
+                                    break;
+                                case "32nd":
+                                    divisionsFromNote = (noteDuration / 4) * 32;
+                                    break;
+                                case "16th":
+                                    divisionsFromNote = (noteDuration / 4) * 16;
+                                    break;
+                                case "eighth":
+                                    divisionsFromNote = (noteDuration / 4) * 8;
+                                    break;
+                                case "quarter":
+                                    divisionsFromNote = (noteDuration / 4) * 4;
+                                    break;
+                                case "half":
+                                    divisionsFromNote = (noteDuration / 4) * 2;
+                                    break;
+                                case "whole":
+                                    divisionsFromNote = (noteDuration / 4);
+                                    break;
+                                case "breve":
+                                    divisionsFromNote = (noteDuration / 4) / 2;
+                                    break;
+                                case "long":
+                                    divisionsFromNote = (noteDuration / 4) / 4;
+                                    break;
+                                case "maxima":
+                                    divisionsFromNote = (noteDuration / 4) / 8;
+                                    break;
+                                default:
+                                    break;
+                            }
+                        }
+                    }
+                }
+                if (divisionsFromNote > 0) {
+                    read = true;
+                    break;
+                }
+            }
+            if (divisionsFromNote === 0) {
+                xmlMeasureIndex++;
+                if (xmlMeasureIndex === this.xmlMeasureList.length) {
+                    let errorMsg: string = ITextTranslation.translateText("ReaderErrorMEssages/DivisionsError", "Invalid divisions value at Instrument: ");
+                    throw new MusicSheetReadingException(errorMsg + this.instrument.Name);
+                }
+            }
+        }
+        return divisionsFromNote;
+    }
+}

+ 833 - 0
src/MusicalScore/ScoreIO/MusicSheetReader.ts

@@ -0,0 +1,833 @@
+import {MusicSheet} from "../MusicSheet";
+import {SourceMeasure} from "../VoiceData/SourceMeasure";
+import {Fraction} from "../../Common/DataObjects/fraction";
+import {InstrumentReader} from "./InstrumentReader";
+import {IXmlElement} from "../../Common/FileIO/Xml";
+import {Instrument} from "../Instrument";
+import {ITextTranslation} from "../Interfaces/ITextTranslation";
+import {MusicSheetReadingException} from "../Exceptions";
+import {Logging} from "../../Common/logging";
+import {IXmlAttribute} from "../../Common/FileIO/Xml";
+import {RhythmInstruction} from "../VoiceData/Instructions/RhythmInstruction";
+import {RhythmSymbolEnum} from "../VoiceData/Instructions/RhythmInstruction";
+import {SourceStaffEntry} from "../VoiceData/SourceStaffEntry";
+import {VoiceEntry} from "../VoiceData/VoiceEntry";
+import {InstrumentalGroup} from "../InstrumentalGroup";
+import {SubInstrument} from "../SubInstrument";
+import {MidiInstrument} from "../VoiceData/Instructions/ClefInstruction";
+import {AbstractNotationInstruction} from "../VoiceData/Instructions/AbstractNotationInstruction";
+import {Label} from "../Label";
+
+type RepetitionInstructionReader = any;
+type RepetitionCalculator = any;
+
+export class MusicSheetReader /*implements IMusicSheetReader*/ {
+
+    //constructor(afterSheetReadingModules: IAfterSheetReadingModule[]) {
+    //  if (afterSheetReadingModules === undefined) {
+    //    this.afterSheetReadingModules = [];
+    //  } else {
+    //    this.afterSheetReadingModules = afterSheetReadingModules;
+    //  }
+    //  this.repetitionInstructionReader = MusicSymbolModuleFactory.createRepetitionInstructionReader();
+    //  this.repetitionCalculator = MusicSymbolModuleFactory.createRepetitionCalculator();
+    //}
+
+    private repetitionInstructionReader: RepetitionInstructionReader;
+    private repetitionCalculator: RepetitionCalculator;
+    // private afterSheetReadingModules: IAfterSheetReadingModule[];
+    private musicSheet: MusicSheet;
+    private completeNumberOfStaves: number = 0;
+    private currentMeasure: SourceMeasure;
+    private previousMeasure: SourceMeasure;
+    private currentFraction: Fraction;
+
+    public get CompleteNumberOfStaves(): number {
+        return this.completeNumberOfStaves;
+    }
+
+    private static doCalculationsAfterDurationHasBeenSet(instrumentReaders: InstrumentReader[]): void {
+        for (let instrumentReader of instrumentReaders) {
+            instrumentReader.doCalculationsAfterDurationHasBeenSet();
+        }
+    }
+
+    public createMusicSheet(root: IXmlElement, path: string): MusicSheet {
+        try {
+            return this._createMusicSheet(root, path);
+        } catch (e) {
+            Logging.log("MusicSheetReader.CreateMusicSheet", e);
+        }
+    }
+
+    private _removeFromArray(list: any[], elem: any): void {
+        let i: number = list.indexOf(elem);
+        if (i !== -1) {
+            list.splice(i, 1);
+        }
+    }
+
+    // Trim from a string also newlines
+    private trimString(str: string): string {
+        return str.replace(/^\s+|\s+$/g, "");
+    }
+
+    private _lastElement<T>(list: T[]): T {
+        return list[list.length - 1];
+    }
+
+    //public SetPhonicScoreInterface(phonicScoreInterface: IPhonicScoreInterface): void {
+    //  this.phonicScoreInterface = phonicScoreInterface;
+    //}
+    //public ReadMusicSheetParameters(sheetObject: MusicSheetParameterObject, root: IXmlElement, path: string): MusicSheetParameterObject {
+    //  this.musicSheet = new MusicSheet();
+    //  if (root !== undefined) {
+    //    this.pushSheetLabels(root, path);
+    //    if (this.musicSheet.Title !== undefined) {
+    //      sheetObject.Title = this.musicSheet.Title.text;
+    //    }
+    //    if (this.musicSheet.Composer !== undefined) {
+    //      sheetObject.Composer = this.musicSheet.Composer.text;
+    //    }
+    //    if (this.musicSheet.Lyricist !== undefined) {
+    //      sheetObject.Lyricist = this.musicSheet.Lyricist.text;
+    //    }
+    //    let partlistNode: IXmlElement = root.element("part-list");
+    //    let partList: IXmlElement[] = partlistNode.elements();
+    //    this.createInstrumentGroups(partList);
+    //    for (let idx: number = 0, len: number = this.musicSheet.Instruments.length; idx < len; ++idx) {
+    //      let instr: Instrument = this.musicSheet.Instruments[idx];
+    //      sheetObject.InstrumentList.push(__init(new MusicSheetParameterObject.LibrarySheetInstrument(), { name: instr.name }));
+    //    }
+    //  }
+    //  return sheetObject;
+    //}
+
+    private _createMusicSheet(root: IXmlElement, path: string): MusicSheet {
+        let instrumentReaders: InstrumentReader[] = [];
+        let sourceMeasureCounter: number = 0;
+        this.musicSheet = new MusicSheet();
+        this.musicSheet.Path = path;
+        if (root === undefined) {
+            throw new MusicSheetReadingException("Undefined root element");
+        }
+        this.pushSheetLabels(root, path);
+        let partlistNode: IXmlElement = root.element("part-list");
+        if (partlistNode === undefined) {
+            throw new MusicSheetReadingException("Undefined partListNode");
+        }
+
+        let partInst: IXmlElement[] = root.elements("part");
+        console.log(partInst.length + " parts");
+        let partList: IXmlElement[] = partlistNode.elements();
+        //Logging.debug("Starting initializeReading");
+        this.initializeReading(partList, partInst, instrumentReaders);
+        //Logging.debug("Done initializeReading");
+        let couldReadMeasure: boolean = true;
+        this.currentFraction = new Fraction(0, 1);
+        let guitarPro: boolean = false;
+        let encoding: IXmlElement = root.element("identification");
+        if (encoding !== undefined) {
+            encoding = encoding.element("encoding");
+        }
+        if (encoding !== undefined) {
+            encoding = encoding.element("software");
+        }
+        if (encoding !== undefined && encoding.value === "Guitar Pro 5") {
+            guitarPro = true;
+        }
+
+        while (couldReadMeasure) {
+            if (this.currentMeasure !== undefined && this.currentMeasure.endsPiece) {
+                sourceMeasureCounter = 0;
+            }
+            this.currentMeasure = new SourceMeasure(this.completeNumberOfStaves);
+            for (let instrumentReader of instrumentReaders) {
+                try {
+                    couldReadMeasure = couldReadMeasure && instrumentReader.readNextXmlMeasure(this.currentMeasure, this.currentFraction, guitarPro);
+                } catch (e) {
+                    let errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/InstrumentError", "Error while reading instruments.");
+                    throw new MusicSheetReadingException(errorMsg, e);
+                }
+
+            }
+            if (couldReadMeasure) {
+                //Logging.debug("couldReadMeasure: 1");
+                this.musicSheet.addMeasure(this.currentMeasure);
+                //Logging.debug("couldReadMeasure: 2");
+                this.checkIfRhythmInstructionsAreSetAndEqual(instrumentReaders);
+                //Logging.debug("couldReadMeasure: 3");
+                this.checkSourceMeasureForundefinedEntries();
+                //Logging.debug("couldReadMeasure: 4");
+                this.setSourceMeasureDuration(instrumentReaders, sourceMeasureCounter);
+                //Logging.debug("couldReadMeasure: 5");
+                MusicSheetReader.doCalculationsAfterDurationHasBeenSet(instrumentReaders);
+                //Logging.debug("couldReadMeasure: 6");
+                this.currentMeasure.AbsoluteTimestamp = this.currentFraction.clone();
+                this.musicSheet.SheetErrors.finalizeMeasure(this.currentMeasure.MeasureNumber);
+                this.currentFraction.Add(this.currentMeasure.Duration);
+                this.previousMeasure = this.currentMeasure;
+                //Logging.debug("couldReadMeasure: 7");
+            }
+        }
+
+        if (this.repetitionInstructionReader !== undefined) {
+            this.repetitionInstructionReader.removeRedundantInstructions();
+            if (this.repetitionCalculator !== undefined) {
+                this.repetitionCalculator.calculateRepetitions(this.musicSheet, this.repetitionInstructionReader.RepetitionInstructions);
+            }
+        }
+        this.musicSheet.checkForInstrumentWithNoVoice();
+        this.musicSheet.fillStaffList();
+        //this.musicSheet.DefaultStartTempoInBpm = this.musicSheet.SheetPlaybackSetting.BeatsPerMinute;
+        //for (let idx: number = 0, len: number = this.afterSheetReadingModules.length; idx < len; ++idx) {
+        //  let afterSheetReadingModule: IAfterSheetReadingModule = this.afterSheetReadingModules[idx];
+        //  afterSheetReadingModule.calculate(this.musicSheet);
+        //}
+
+        return this.musicSheet;
+    }
+
+    private initializeReading(partList: IXmlElement[], partInst: IXmlElement[], instrumentReaders: InstrumentReader[]): void {
+        let instrumentDict: { [_: string]: Instrument; } = this.createInstrumentGroups(partList);
+        this.completeNumberOfStaves = this.getCompleteNumberOfStavesFromXml(partInst);
+        if (partInst.length !== 0) {
+            // (*) this.repetitionInstructionReader.MusicSheet = this.musicSheet;
+            this.currentFraction = new Fraction(0, 1);
+            this.currentMeasure = undefined;
+            this.previousMeasure = undefined;
+        }
+        let counter: number = 0;
+        for (let node of partInst) {
+            let idNode: IXmlAttribute = node.attribute("id");
+            if (idNode) {
+                let currentInstrument: Instrument = instrumentDict[idNode.value];
+                let xmlMeasureList: IXmlElement[] = node.elements("measure");
+                let instrumentNumberOfStaves: number = 1;
+                try {
+                    instrumentNumberOfStaves = this.getInstrumentNumberOfStavesFromXml(node);
+                } catch (err) {
+                    let errorMsg: string = ITextTranslation.translateText(
+                        "ReaderErrorMessages/InstrumentStavesNumberError",
+                        "Invalid number of staves at instrument: "
+                    );
+                    this.musicSheet.SheetErrors.push(errorMsg + currentInstrument.Name);
+                    continue;
+                }
+
+                currentInstrument.createStaves(instrumentNumberOfStaves);
+                instrumentReaders.push(new InstrumentReader(this.repetitionInstructionReader, xmlMeasureList, currentInstrument));
+                if (this.repetitionInstructionReader !== undefined) {
+                    this.repetitionInstructionReader.XmlMeasureList[counter] = xmlMeasureList;
+                }
+                counter++;
+            }
+        }
+    }
+
+    private checkIfRhythmInstructionsAreSetAndEqual(instrumentReaders: InstrumentReader[]): void {
+        let rhythmInstructions: RhythmInstruction[] = [];
+        for (let i: number = 0; i < this.completeNumberOfStaves; i++) {
+            if (this.currentMeasure.FirstInstructionsStaffEntries[i] !== undefined) {
+                let last: AbstractNotationInstruction = this.currentMeasure.FirstInstructionsStaffEntries[i].Instructions[
+                this.currentMeasure.FirstInstructionsStaffEntries[i].Instructions.length - 1
+                    ];
+                if (last instanceof RhythmInstruction) {
+                    rhythmInstructions.push(<RhythmInstruction>last);
+                }
+            }
+        }
+        let maxRhythmValue: number = 0.0;
+        let index: number = -1;
+        for (let idx: number = 0, len: number = rhythmInstructions.length; idx < len; ++idx) {
+            let rhythmInstruction: RhythmInstruction = rhythmInstructions[idx];
+            if (rhythmInstruction.Rhythm.RealValue > maxRhythmValue) {
+                if (this.areRhythmInstructionsMixed(rhythmInstructions) && rhythmInstruction.SymbolEnum !== RhythmSymbolEnum.NONE) {
+                    continue;
+                }
+                maxRhythmValue = rhythmInstruction.Rhythm.RealValue;
+                index = rhythmInstructions.indexOf(rhythmInstruction);
+            }
+        }
+        if (rhythmInstructions.length > 0 && rhythmInstructions.length < this.completeNumberOfStaves) {
+            let rhythmInstruction: RhythmInstruction = rhythmInstructions[index].clone();
+            for (let i: number = 0; i < this.completeNumberOfStaves; i++) {
+                if (
+                    this.currentMeasure.FirstInstructionsStaffEntries[i] !== undefined &&
+                    !(this._lastElement(this.currentMeasure.FirstInstructionsStaffEntries[i].Instructions) instanceof RhythmInstruction)
+                ) {
+                    this.currentMeasure.FirstInstructionsStaffEntries[i].removeAllInstructionsOfTypeRhythmInstruction();
+                    this.currentMeasure.FirstInstructionsStaffEntries[i].Instructions.push(rhythmInstruction.clone());
+                }
+                if (this.currentMeasure.FirstInstructionsStaffEntries[i] === undefined) {
+                    this.currentMeasure.FirstInstructionsStaffEntries[i] = new SourceStaffEntry(undefined, undefined);
+                    this.currentMeasure.FirstInstructionsStaffEntries[i].Instructions.push(rhythmInstruction.clone());
+                }
+            }
+            for (let idx: number = 0, len: number = instrumentReaders.length; idx < len; ++idx) {
+                let instrumentReader: InstrumentReader = instrumentReaders[idx];
+                instrumentReader.ActiveRhythm = rhythmInstruction;
+            }
+        }
+        if (rhythmInstructions.length === 0 && this.currentMeasure === this.musicSheet.SourceMeasures[0]) {
+            let rhythmInstruction: RhythmInstruction = new RhythmInstruction(new Fraction(4, 4, false), 4, 4, RhythmSymbolEnum.NONE);
+            for (let i: number = 0; i < this.completeNumberOfStaves; i++) {
+                if (this.currentMeasure.FirstInstructionsStaffEntries[i] === undefined) {
+                    this.currentMeasure.FirstInstructionsStaffEntries[i] = new SourceStaffEntry(undefined, undefined);
+                } else {
+                    this.currentMeasure.FirstInstructionsStaffEntries[i].removeAllInstructionsOfTypeRhythmInstruction();
+                }
+                this.currentMeasure.FirstInstructionsStaffEntries[i].Instructions.push(rhythmInstruction);
+            }
+            for (let idx: number = 0, len: number = instrumentReaders.length; idx < len; ++idx) {
+                let instrumentReader: InstrumentReader = instrumentReaders[idx];
+                instrumentReader.ActiveRhythm = rhythmInstruction;
+            }
+        }
+        for (let idx: number = 0, len: number = rhythmInstructions.length; idx < len; ++idx) {
+            let rhythmInstruction: RhythmInstruction = rhythmInstructions[idx];
+            if (rhythmInstruction.Rhythm.RealValue < maxRhythmValue) {
+                if (this._lastElement(
+                        this.currentMeasure.FirstInstructionsStaffEntries[rhythmInstructions.indexOf(rhythmInstruction)].Instructions
+                    ) instanceof RhythmInstruction) {
+                    // TODO Test correctness
+                    let instrs: AbstractNotationInstruction[] =
+                        this.currentMeasure.FirstInstructionsStaffEntries[rhythmInstructions.indexOf(rhythmInstruction)].Instructions;
+                    instrs[instrs.length - 1] = rhythmInstructions[index].clone();
+                }
+            }
+            if (
+                Math.abs(rhythmInstruction.Rhythm.RealValue - maxRhythmValue) < 0.000001 &&
+                rhythmInstruction.SymbolEnum !== RhythmSymbolEnum.NONE &&
+                this.areRhythmInstructionsMixed(rhythmInstructions)
+            ) {
+                rhythmInstruction.SymbolEnum = RhythmSymbolEnum.NONE;
+            }
+        }
+    }
+
+    private areRhythmInstructionsMixed(rhythmInstructions: RhythmInstruction[]): boolean {
+        for (let i: number = 1; i < rhythmInstructions.length; i++) {
+            if (
+                Math.abs(rhythmInstructions[i].Rhythm.RealValue - rhythmInstructions[0].Rhythm.RealValue) < 0.000001 &&
+                rhythmInstructions[i].SymbolEnum !== rhythmInstructions[0].SymbolEnum
+            ) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private setSourceMeasureDuration(instrumentReaders: InstrumentReader[], sourceMeasureCounter: number): void {
+        let activeRhythm: Fraction = new Fraction(0, 1);
+        let instrumentsMaxTieNoteFractions: Fraction[] = [];
+        for (let idx: number = 0, len: number = instrumentReaders.length; idx < len; ++idx) {
+            let instrumentReader: InstrumentReader = instrumentReaders[idx];
+            instrumentsMaxTieNoteFractions.push(instrumentReader.MaxTieNoteFraction);
+            let activeRythmMeasure: Fraction = instrumentReader.ActiveRhythm.Rhythm;
+            if (activeRhythm < activeRythmMeasure) {
+                activeRhythm = new Fraction(activeRythmMeasure.Numerator, activeRythmMeasure.Denominator, false);
+            }
+        }
+        let instrumentsDurations: Fraction[] = this.currentMeasure.calculateInstrumentsDuration(this.musicSheet, instrumentsMaxTieNoteFractions);
+        let maxInstrumentDuration: Fraction = new Fraction(0, 1);
+        for (let idx: number = 0, len: number = instrumentsDurations.length; idx < len; ++idx) {
+            let instrumentsDuration: Fraction = instrumentsDurations[idx];
+            if (maxInstrumentDuration < instrumentsDuration) {
+                maxInstrumentDuration = instrumentsDuration;
+            }
+        }
+        if (Fraction.Equal(maxInstrumentDuration, activeRhythm)) {
+            this.checkFractionsForEquivalence(maxInstrumentDuration, activeRhythm);
+        } else {
+            if (maxInstrumentDuration < activeRhythm) {
+                maxInstrumentDuration = this.currentMeasure.reverseCheck(this.musicSheet, maxInstrumentDuration);
+                this.checkFractionsForEquivalence(maxInstrumentDuration, activeRhythm);
+            }
+        }
+        this.currentMeasure.ImplicitMeasure = this.checkIfMeasureIsImplicit(maxInstrumentDuration, activeRhythm);
+        if (!this.currentMeasure.ImplicitMeasure) {
+            sourceMeasureCounter++;
+        }
+        this.currentMeasure.Duration = maxInstrumentDuration;
+        this.currentMeasure.MeasureNumber = sourceMeasureCounter;
+        for (let i: number = 0; i < instrumentsDurations.length; i++) {
+            let instrumentsDuration: Fraction = instrumentsDurations[i];
+            if (
+                (this.currentMeasure.ImplicitMeasure && instrumentsDuration !== maxInstrumentDuration) ||
+                instrumentsDuration !== activeRhythm && // FIXME
+                !this.allInstrumentsHaveSameDuration(instrumentsDurations, maxInstrumentDuration)
+            ) {
+                let firstStaffIndexOfInstrument: number = this.musicSheet.getGlobalStaffIndexOfFirstStaff(this.musicSheet.Instruments[i]);
+                for (let staffIndex: number = 0; staffIndex < this.musicSheet.Instruments[i].Staves.length; staffIndex++) {
+                    if (!this.staffMeasureIsEmpty(firstStaffIndexOfInstrument + staffIndex)) {
+                        this.currentMeasure.setErrorInStaffMeasure(firstStaffIndexOfInstrument + staffIndex, true);
+                        let errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/MissingNotesError",
+                                                                              "Given Notes don't correspond to measure duration.");
+                        this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
+                    }
+                }
+            }
+        }
+    }
+
+    private checkFractionsForEquivalence(maxInstrumentDuration: Fraction, activeRhythm: Fraction): void {
+        if (activeRhythm.Denominator > maxInstrumentDuration.Denominator) {
+            let factor: number = activeRhythm.Denominator / maxInstrumentDuration.Denominator;
+            maxInstrumentDuration.multiplyWithFactor(factor);
+        }
+    }
+
+    private checkIfMeasureIsImplicit(maxInstrumentDuration: Fraction, activeRhythm: Fraction): boolean {
+        if (this.previousMeasure === undefined && maxInstrumentDuration < activeRhythm) {
+            return true;
+        }
+        if (this.previousMeasure !== undefined) {
+            return Fraction.plus(this.previousMeasure.Duration, maxInstrumentDuration).CompareTo(activeRhythm) === 0;
+        }
+        return false;
+    }
+
+    private allInstrumentsHaveSameDuration(instrumentsDurations: Fraction[], maxInstrumentDuration: Fraction): boolean {
+        let counter: number = 0;
+        for (let idx: number = 0, len: number = instrumentsDurations.length; idx < len; ++idx) {
+            let instrumentsDuration: Fraction = instrumentsDurations[idx];
+            if (instrumentsDuration === maxInstrumentDuration) {
+                counter++;
+            }
+        }
+        return (counter === instrumentsDurations.length && maxInstrumentDuration !== new Fraction(0, 1));
+    }
+
+    private staffMeasureIsEmpty(index: number): boolean {
+        let counter: number = 0;
+        for (let i: number = 0; i < this.currentMeasure.VerticalSourceStaffEntryContainers.length; i++) {
+            if (this.currentMeasure.VerticalSourceStaffEntryContainers[i].StaffEntries[index] === undefined) {
+                counter++;
+            }
+        }
+        return (counter === this.currentMeasure.VerticalSourceStaffEntryContainers.length);
+    }
+
+    private checkSourceMeasureForundefinedEntries(): void {
+        for (let i: number = this.currentMeasure.VerticalSourceStaffEntryContainers.length - 1; i >= 0; i--) {
+            for (let j: number = this.currentMeasure.VerticalSourceStaffEntryContainers[i].StaffEntries.length - 1; j >= 0; j--) {
+                let sourceStaffEntry: SourceStaffEntry = this.currentMeasure.VerticalSourceStaffEntryContainers[i].StaffEntries[j];
+                if (sourceStaffEntry !== undefined) {
+                    for (let k: number = sourceStaffEntry.VoiceEntries.length - 1; k >= 0; k--) {
+                        let voiceEntry: VoiceEntry = sourceStaffEntry.VoiceEntries[k];
+                        if (voiceEntry.Notes.length === 0) {
+                            this._removeFromArray(voiceEntry.ParentVoice.VoiceEntries, voiceEntry);
+                            this._removeFromArray(sourceStaffEntry.VoiceEntries, voiceEntry);
+                        }
+                    }
+                }
+                if (sourceStaffEntry !== undefined && sourceStaffEntry.VoiceEntries.length === 0) {
+                    this.currentMeasure.VerticalSourceStaffEntryContainers[i].StaffEntries[j] = undefined;
+                }
+            }
+        }
+        for (let i: number = this.currentMeasure.VerticalSourceStaffEntryContainers.length - 1; i >= 0; i--) {
+            let counter: number = 0;
+            for (let idx: number = 0, len: number = this.currentMeasure.VerticalSourceStaffEntryContainers[i].StaffEntries.length; idx < len; ++idx) {
+                let sourceStaffEntry: SourceStaffEntry = this.currentMeasure.VerticalSourceStaffEntryContainers[i].StaffEntries[idx];
+                if (sourceStaffEntry === undefined) {
+                    counter++;
+                }
+            }
+            if (counter === this.currentMeasure.VerticalSourceStaffEntryContainers[i].StaffEntries.length) {
+                this._removeFromArray(this.currentMeasure.VerticalSourceStaffEntryContainers, this.currentMeasure.VerticalSourceStaffEntryContainers[i]);
+            }
+        }
+    }
+
+    private pushSheetLabels(root: IXmlElement, filePath: string): void {
+        this.readComposer(root);
+        this.readTitle(root);
+        if (this.musicSheet.Title === undefined || this.musicSheet.Composer === undefined) {
+            this.readTitleAndComposerFromCredits(root);
+        }
+        if (this.musicSheet.Title === undefined) {
+            try {
+                let barI: number = Math.max(
+                    0, filePath.lastIndexOf("/"), filePath.lastIndexOf("\\")
+                );
+                let filename: string = filePath.substr(barI);
+                let filenameSplits: string[] = filename.split(".", 1);
+                this.musicSheet.Title = new Label(filenameSplits[0]);
+            } catch (ex) {
+                Logging.log("MusicSheetReader.pushSheetLabels: ", ex);
+            }
+
+        }
+    }
+
+    // Checks whether _elem_ has an attribute with value _val_.
+    private presentAttrsWithValue(elem: IXmlElement, val: string): boolean {
+        for (let attr of elem.attributes()) {
+            if (attr.value === val) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private readComposer(root: IXmlElement): void {
+        let identificationNode: IXmlElement = root.element("identification");
+        if (identificationNode !== undefined) {
+            let creators: IXmlElement[] = identificationNode.elements("creator");
+            for (let idx: number = 0, len: number = creators.length; idx < len; ++idx) {
+                let creator: IXmlElement = creators[idx];
+                if (creator.hasAttributes) {
+                    if (this.presentAttrsWithValue(creator, "composer")) {
+                        this.musicSheet.Composer = new Label(this.trimString(creator.value));
+                        continue;
+                    }
+                    if (this.presentAttrsWithValue(creator, "lyricist") || this.presentAttrsWithValue(creator, "poet")) {
+                        this.musicSheet.Lyricist = new Label(this.trimString(creator.value));
+                    }
+                }
+            }
+        }
+    }
+
+    private readTitleAndComposerFromCredits(root: IXmlElement): void {
+        let systemYCoordinates: number = this.computeSystemYCoordinates(root);
+        if (systemYCoordinates === 0) {
+            return;
+        }
+        let largestTitleCreditSize: number = 1;
+        let finalTitle: string = undefined;
+        let largestCreditYInfo: number = 0;
+        let finalSubtitle: string = undefined;
+        let possibleTitle: string = undefined;
+        let creditElements: IXmlElement[] = root.elements("credit");
+        for (let idx: number = 0, len: number = creditElements.length; idx < len; ++idx) {
+            let credit: IXmlElement = creditElements[idx];
+            if (!credit.attribute("page")) {
+                return;
+            }
+            if (credit.attribute("page").value === "1") {
+                let creditChild: IXmlElement = undefined;
+                if (credit !== undefined) {
+                    creditChild = credit.element("credit-words");
+                    if (!creditChild.attribute("justify")) {
+                        break;
+                    }
+                    let creditJustify: string = creditChild.attribute("justify").value;
+                    let creditY: string = creditChild.attribute("default-y").value;
+                    let creditYInfo: number = parseFloat(creditY);
+                    if (creditYInfo > systemYCoordinates) {
+                        if (this.musicSheet.Title === undefined) {
+                            let creditSize: string = creditChild.attribute("font-size").value;
+                            let titleCreditSizeInt: number = parseFloat(creditSize);
+                            if (largestTitleCreditSize < titleCreditSizeInt) {
+                                largestTitleCreditSize = titleCreditSizeInt;
+                                finalTitle = creditChild.value;
+                            }
+                        }
+                        if (this.musicSheet.Subtitle === undefined) {
+                            if (creditJustify !== "right" && creditJustify !== "left") {
+                                if (largestCreditYInfo < creditYInfo) {
+                                    largestCreditYInfo = creditYInfo;
+                                    if (possibleTitle) {
+                                        finalSubtitle = possibleTitle;
+                                        possibleTitle = creditChild.value;
+                                    } else {
+                                        possibleTitle = creditChild.value;
+                                    }
+                                }
+                            }
+                        }
+                        if (!(this.musicSheet.Composer !== undefined && this.musicSheet.Lyricist !== undefined)) {
+                            switch (creditJustify) {
+                                case "right":
+                                    this.musicSheet.Composer = new Label(this.trimString(creditChild.value));
+                                    break;
+                                case "left":
+                                    this.musicSheet.Lyricist = new Label(this.trimString(creditChild.value));
+                                    break;
+                                default:
+                                    break;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        if (this.musicSheet.Title === undefined && finalTitle) {
+            this.musicSheet.Title = new Label(this.trimString(finalTitle));
+        }
+        if (this.musicSheet.Subtitle === undefined && finalSubtitle) {
+            this.musicSheet.Subtitle = new Label(this.trimString(finalSubtitle));
+        }
+    }
+
+    private computeSystemYCoordinates(root: IXmlElement): number {
+        if (root.element("defaults") === undefined) {
+            return 0;
+        }
+        let paperHeight: number = 0;
+        let topSystemDistance: number = 0;
+        let defi: string = root.element("defaults").element("page-layout").element("page-height").value;
+        paperHeight = parseFloat(defi);
+        let found: boolean = false;
+        let parts: IXmlElement[] = root.elements("part");
+        for (let idx: number = 0, len: number = parts.length; idx < len; ++idx) {
+            let measures: IXmlElement[] = parts[idx].elements("measure");
+            for (let idx2: number = 0, len2: number = measures.length; idx2 < len2; ++idx2) {
+                let measure: IXmlElement = measures[idx2];
+                if (measure.element("print") !== undefined) {
+                    let systemLayouts: IXmlElement[] = measure.element("print").elements("system-layout");
+                    for (let idx3: number = 0, len3: number = systemLayouts.length; idx3 < len3; ++idx3) {
+                        let syslab: IXmlElement = systemLayouts[idx3];
+                        if (syslab.element("top-system-distance") !== undefined) {
+                            let topSystemDistanceString: string = syslab.element("top-system-distance").value;
+                            topSystemDistance = parseFloat(topSystemDistanceString);
+                            found = true;
+                            break;
+                        }
+                    }
+                    break;
+                }
+            }
+            if (found) {
+                break;
+            }
+        }
+        if (root.element("defaults").element("system-layout") !== undefined) {
+            let syslay: IXmlElement = root.element("defaults").element("system-layout");
+            if (syslay.element("top-system-distance") !== undefined) {
+                let topSystemDistanceString: string = root.element("defaults").element("system-layout").element("top-system-distance").value;
+                topSystemDistance = parseFloat(topSystemDistanceString);
+            }
+        }
+        if (topSystemDistance === 0) {
+            return 0;
+        }
+        return paperHeight - topSystemDistance;
+    }
+
+    private readTitle(root: IXmlElement): void {
+        let titleNode: IXmlElement = root.element("work");
+        let titleNodeChild: IXmlElement = undefined;
+        if (titleNode !== undefined) {
+            titleNodeChild = titleNode.element("work-title");
+            if (titleNodeChild !== undefined && titleNodeChild.value) {
+                this.musicSheet.Title = new Label(this.trimString(titleNodeChild.value));
+            }
+        }
+        let movementNode: IXmlElement = root.element("movement-title");
+        let finalSubTitle: string = "";
+        if (movementNode !== undefined) {
+            if (this.musicSheet.Title === undefined) {
+                this.musicSheet.Title = new Label(this.trimString(movementNode.value));
+            } else {
+                finalSubTitle = this.trimString(movementNode.value);
+            }
+        }
+        if (titleNode !== undefined) {
+            let subtitleNodeChild: IXmlElement = titleNode.element("work-number");
+            if (subtitleNodeChild !== undefined) {
+                let workNumber: string = subtitleNodeChild.value;
+                if (workNumber) {
+                    if (finalSubTitle) {
+                        finalSubTitle = workNumber;
+                    } else {
+                        finalSubTitle = finalSubTitle + ", " + workNumber;
+                    }
+                }
+            }
+        }
+        if (finalSubTitle
+        ) {
+            this.musicSheet.Subtitle = new Label(finalSubTitle);
+        }
+    }
+
+    private createInstrumentGroups(entryList: IXmlElement[]): { [_: string]: Instrument; } {
+        let instrumentId: number = 0;
+        let instrumentDict: { [_: string]: Instrument; } = {};
+        let currentGroup: InstrumentalGroup;
+        try {
+            let entryArray: IXmlElement[] = entryList;
+            for (let idx: number = 0, len: number = entryArray.length; idx < len; ++idx) {
+                let node: IXmlElement = entryArray[idx];
+                if (node.name === "score-part") {
+                    let instrIdString: string = node.attribute("id").value;
+                    let instrument: Instrument = new Instrument(instrumentId, instrIdString, this.musicSheet, currentGroup);
+                    instrumentId++;
+                    let partElements: IXmlElement[] = node.elements();
+                    for (let idx2: number = 0, len2: number = partElements.length; idx2 < len2; ++idx2) {
+                        let partElement: IXmlElement = partElements[idx2];
+                        try {
+                            if (partElement.name === "part-name") {
+                                instrument.Name = partElement.value;
+                            } else if (partElement.name === "score-instrument") {
+                                let subInstrument: SubInstrument = new SubInstrument(instrument);
+                                subInstrument.idString = partElement.firstAttribute.value;
+                                instrument.SubInstruments.push(subInstrument);
+                                let subElement: IXmlElement = partElement.element("instrument-name");
+                                if (subElement !== undefined) {
+                                    subInstrument.name = subElement.value;
+                                    subInstrument.setMidiInstrument(subElement.value);
+                                }
+                            } else if (partElement.name === "midi-instrument") {
+                                let subInstrument: SubInstrument = instrument.getSubInstrument(partElement.firstAttribute.value);
+                                for (let idx3: number = 0, len3: number = instrument.SubInstruments.length; idx3 < len3; ++idx3) {
+                                    let subInstr: SubInstrument = instrument.SubInstruments[idx3];
+                                    if (subInstr.idString === partElement.value) {
+                                        subInstrument = subInstr;
+                                        break;
+                                    }
+                                }
+                                let instrumentElements: IXmlElement[] = partElement.elements();
+                                for (let idx3: number = 0, len3: number = instrumentElements.length; idx3 < len3; ++idx3) {
+                                    let instrumentElement: IXmlElement = instrumentElements[idx3];
+                                    try {
+                                        if (instrumentElement.name === "midi-channel") {
+                                            if (parseInt(instrumentElement.value, 10) === 10) {
+                                                instrument.MidiInstrumentId = MidiInstrument.Percussion;
+                                            }
+                                        } else if (instrumentElement.name === "midi-program") {
+                                            if (instrument.SubInstruments.length > 0 && instrument.MidiInstrumentId !== MidiInstrument.Percussion) {
+                                                subInstrument.midiInstrumentID = <MidiInstrument>Math.max(0, parseInt(instrumentElement.value, 10) - 1);
+                                            }
+                                        } else if (instrumentElement.name === "midi-unpitched") {
+                                            subInstrument.fixedKey = Math.max(0, parseInt(instrumentElement.value, 10));
+                                        } else if (instrumentElement.name === "volume") {
+                                            try {
+                                                let result: number = <number>parseFloat(instrumentElement.value);
+                                                subInstrument.volume = result / 127.0;
+                                            } catch (ex) {
+                                                Logging.debug("ExpressionReader.readExpressionParameters", "read volume", ex);
+                                            }
+
+                                        } else if (instrumentElement.name === "pan") {
+                                            try {
+                                                let result: number = <number>parseFloat(instrumentElement.value);
+                                                subInstrument.pan = result / 64.0;
+                                            } catch (ex) {
+                                                Logging.debug("ExpressionReader.readExpressionParameters", "read pan", ex);
+                                            }
+
+                                        }
+                                    } catch (ex) {
+                                        Logging.log("MusicSheetReader.createInstrumentGroups midi settings: ", ex);
+                                    }
+
+                                }
+                            }
+                        } catch (ex) {
+                            Logging.log("MusicSheetReader.createInstrumentGroups: ", ex);
+                        }
+
+                    }
+                    if (instrument.SubInstruments.length === 0) {
+                        let subInstrument: SubInstrument = new SubInstrument(instrument);
+                        instrument.SubInstruments.push(subInstrument);
+                    }
+                    instrumentDict[instrIdString] = instrument;
+                    if (currentGroup !== undefined) {
+                        currentGroup.InstrumentalGroups.push(instrument);
+                        this.musicSheet.Instruments.push(instrument);
+                    } else {
+                        this.musicSheet.InstrumentalGroups.push(instrument);
+                        this.musicSheet.Instruments.push(instrument);
+                    }
+                } else {
+                    if ((node.name === "part-group") && (node.attribute("type").value === "start")) {
+                        let iG: InstrumentalGroup = new InstrumentalGroup("group", this.musicSheet, currentGroup);
+                        if (currentGroup !== undefined) {
+                            currentGroup.InstrumentalGroups.push(iG);
+                        } else {
+                            this.musicSheet.InstrumentalGroups.push(iG);
+                        }
+                        currentGroup = iG;
+                    } else {
+                        if ((node.name === "part-group") && (node.attribute("type").value === "stop")) {
+                            if (currentGroup !== undefined) {
+                                if (currentGroup.InstrumentalGroups.length === 1) {
+                                    let instr: InstrumentalGroup = currentGroup.InstrumentalGroups[0];
+                                    if (currentGroup.Parent !== undefined) {
+                                        currentGroup.Parent.InstrumentalGroups.push(instr);
+                                        this._removeFromArray(currentGroup.Parent.InstrumentalGroups, currentGroup);
+                                    } else {
+                                        this.musicSheet.InstrumentalGroups.push(instr);
+                                        this._removeFromArray(this.musicSheet.InstrumentalGroups, currentGroup);
+                                    }
+                                }
+                                currentGroup = currentGroup.Parent;
+                            }
+                        }
+                    }
+                }
+            }
+        } catch (e) {
+            let errorMsg: string = ITextTranslation.translateText(
+                "ReaderErrorMessages/InstrumentError", "Error while reading Instruments"
+            );
+            throw new MusicSheetReadingException(errorMsg, e);
+        }
+
+        for (let idx: number = 0, len: number = this.musicSheet.Instruments.length; idx < len; ++idx) {
+            let instrument: Instrument = this.musicSheet.Instruments[idx];
+            if (!instrument.Name) {
+                instrument.Name = "Instr. " + instrument.IdString;
+            }
+        }
+        return instrumentDict;
+    }
+
+    private getCompleteNumberOfStavesFromXml(partInst: IXmlElement[]): number {
+        let num: number = 0;
+        for (let partNode of partInst) {
+            let xmlMeasureList: IXmlElement[] = partNode.elements("measure");
+            if (xmlMeasureList.length > 0) {
+                let xmlMeasure: IXmlElement = xmlMeasureList[0];
+                if (xmlMeasure !== undefined) {
+                    let stavesNode: IXmlElement = xmlMeasure.element("attributes");
+                    if (stavesNode !== undefined) {
+                        stavesNode = stavesNode.element("staves");
+                    }
+                    if (stavesNode === undefined) {
+                        num++;
+                    } else {
+                        num += parseInt(stavesNode.value, 10);
+                    }
+                }
+            }
+        }
+        if (isNaN(num) || num <= 0) {
+            let errorMsg: string = ITextTranslation.translateText(
+                "ReaderErrorMessages/StaffError", "Invalid number of staves."
+            );
+            throw new MusicSheetReadingException(errorMsg);
+        }
+        return num;
+    }
+
+    private getInstrumentNumberOfStavesFromXml(partNode: IXmlElement): number {
+        let num: number = 0;
+        let xmlMeasure: IXmlElement = partNode.element("measure");
+        if (xmlMeasure !== undefined) {
+            let attributes: IXmlElement = xmlMeasure.element("attributes");
+            let staves: IXmlElement = undefined;
+            if (attributes !== undefined) {
+                staves = attributes.element("staves");
+            }
+            if (attributes === undefined || staves === undefined) {
+                num = 1;
+            } else {
+                num = parseInt(staves.value, 10);
+            }
+        }
+        if (isNaN(num) || num <= 0) {
+            let errorMsg: string = ITextTranslation.translateText(
+                "ReaderErrorMessages/StaffError", "Invalid number of Staves."
+            );
+            throw new MusicSheetReadingException(errorMsg);
+        }
+        return num;
+    }
+}

+ 865 - 0
src/MusicalScore/ScoreIO/VoiceGenerator.ts

@@ -0,0 +1,865 @@
+import {Instrument} from "../Instrument";
+import {LinkedVoice} from "../VoiceData/LinkedVoice";
+import {Voice} from "../VoiceData/Voice";
+import {MusicSheet} from "../MusicSheet";
+import {VoiceEntry} from "../VoiceData/VoiceEntry";
+import {Note} from "../VoiceData/Note";
+import {SourceMeasure} from "../VoiceData/SourceMeasure";
+import {SourceStaffEntry} from "../VoiceData/SourceStaffEntry";
+import {Beam} from "../VoiceData/Beam";
+import {Tie} from "../VoiceData/Tie";
+import {Tuplet} from "../VoiceData/Tuplet";
+import {Fraction} from "../../Common/DataObjects/fraction";
+//import {MusicSymbolModuleFactory} from "./InstrumentReader";
+import {IXmlElement} from "../../Common/FileIO/Xml";
+import {ITextTranslation} from "../Interfaces/ITextTranslation";
+import {ArticulationEnum} from "../VoiceData/VoiceEntry";
+import {Slur} from "../VoiceData/Expressions/ContinuousExpressions/Slur";
+import {LyricsEntry} from "../VoiceData/Lyrics/LyricsEntry";
+import {MusicSheetReadingException} from "../Exceptions";
+import {AccidentalEnum} from "../../Common/DataObjects/pitch";
+import {NoteEnum} from "../../Common/DataObjects/pitch";
+import {Staff} from "../VoiceData/Staff";
+import {StaffEntryLink} from "../VoiceData/StaffEntryLink";
+import {VerticalSourceStaffEntryContainer} from "../VoiceData/VerticalSourceStaffEntryContainer";
+import {Logging} from "../../Common/logging";
+import {Pitch} from "../../Common/DataObjects/pitch";
+import {IXmlAttribute} from "../../Common/FileIO/Xml";
+import {CollectionUtil} from "../../Util/collectionUtil";
+import Dictionary from "typescript-collections/dist/lib/Dictionary";
+
+type SlurReader = any;
+
+export class VoiceGenerator {
+    constructor(instrument: Instrument, voiceId: number, slurReader: SlurReader, mainVoice: Voice = undefined) {
+        this.musicSheet = instrument.GetMusicSheet;
+        this.slurReader = slurReader;
+        if (mainVoice !== undefined) {
+            this.voice = new LinkedVoice(instrument, voiceId, mainVoice);
+        } else {
+            this.voice = new Voice(instrument, voiceId);
+        }
+        instrument.Voices.push(this.voice);
+        //this.lyricsReader = MusicSymbolModuleFactory.createLyricsReader(this.musicSheet);
+        //this.articulationReader = MusicSymbolModuleFactory.createArticulationReader();
+    }
+
+    private slurReader: SlurReader;
+    //private lyricsReader: LyricsReader;
+    //private articulationReader: ArticulationReader;
+    private musicSheet: MusicSheet;
+    private voice: Voice;
+    private currentVoiceEntry: VoiceEntry;
+    private currentNote: Note;
+    private currentMeasure: SourceMeasure;
+    private currentStaffEntry: SourceStaffEntry;
+    private lastBeamTag: string = "";
+    private openBeam: Beam;
+    private openGraceBeam: Beam;
+    private openTieDict: { [_: number]: Tie; } = {};
+    private currentOctaveShift: number = 0;
+    private tupletDict: { [_: number]: Tuplet; } = {};
+    private openTupletNumber: number = 0;
+
+    public get GetVoice(): Voice {
+        return this.voice;
+    }
+    public get OctaveShift(): number {
+        return this.currentOctaveShift;
+    }
+    public set OctaveShift(value: number) {
+        this.currentOctaveShift = value;
+    }
+    public createVoiceEntry(musicTimestamp: Fraction, parentStaffEntry: SourceStaffEntry, addToVoice: boolean): void {
+        this.currentVoiceEntry = new VoiceEntry(musicTimestamp.clone(), this.voice, parentStaffEntry);
+        if (addToVoice) {
+            this.voice.VoiceEntries.push(this.currentVoiceEntry);
+        }
+        if (parentStaffEntry.VoiceEntries.indexOf(this.currentVoiceEntry) === -1) {
+            parentStaffEntry.VoiceEntries.push(this.currentVoiceEntry);
+        }
+    }
+    public read(
+        noteNode: IXmlElement, noteDuration: number, divisions: number, restNote: boolean, graceNote: boolean,
+        parentStaffEntry: SourceStaffEntry, parentMeasure: SourceMeasure,
+        measureStartAbsoluteTimestamp: Fraction, maxTieNoteFraction: Fraction, chord: boolean, guitarPro: boolean
+    ): Note {
+        this.currentStaffEntry = parentStaffEntry;
+        this.currentMeasure = parentMeasure;
+        //Logging.debug("read called:", restNote);
+        try {
+            this.currentNote = restNote
+                ? this.addRestNote(noteDuration, divisions)
+                : this.addSingleNote(noteNode, noteDuration, divisions, graceNote, chord, guitarPro);
+            // (*)
+            //if (this.lyricsReader !== undefined && noteNode.element("lyric") !== undefined) {
+            //    this.lyricsReader.addLyricEntry(noteNode, this.currentVoiceEntry);
+            //    this.voice.Parent.HasLyrics = true;
+            //}
+            let notationNode: IXmlElement = noteNode.element("notations");
+            if (notationNode !== undefined) {
+                // let articNode: IXmlElement = undefined;
+                // (*)
+                //if (this.articulationReader !== undefined) {
+                //    this.readArticulations(notationNode, this.currentVoiceEntry);
+                //}
+                //let slurNodes: IXmlElement[] = undefined;
+                // (*)
+                //if (this.slurReader !== undefined && (slurNodes = notationNode.elements("slur")))
+                //    this.slurReader.addSlur(slurNodes, this.currentNote);
+                let tupletNodeList: IXmlElement[] = notationNode.elements("tuplet");
+                if (tupletNodeList) {
+                    this.openTupletNumber = this.addTuplet(noteNode, tupletNodeList);
+                }
+                if (notationNode.element("arpeggiate") !== undefined && !graceNote) {
+                    this.currentVoiceEntry.ArpeggiosNotesIndices.push(this.currentVoiceEntry.Notes.indexOf(this.currentNote));
+                }
+                let tiedNodeList: IXmlElement[] = notationNode.elements("tied");
+                if (tiedNodeList) {
+                    this.addTie(tiedNodeList, measureStartAbsoluteTimestamp, maxTieNoteFraction);
+                }
+
+                let openTieDict: { [_: number]: Tie; } = this.openTieDict;
+                for (let key in openTieDict) {
+                    if (openTieDict.hasOwnProperty(key)) {
+                        let tie: Tie = openTieDict[key];
+                        if (Fraction.plus(tie.Start.ParentStaffEntry.Timestamp, tie.Start.Length).lt(this.currentStaffEntry.Timestamp)) {
+                            delete openTieDict[key];
+                        }
+                    }
+                }
+            }
+            if (noteNode.element("time-modification") !== undefined && notationNode === undefined) {
+                this.handleTimeModificationNode(noteNode);
+            }
+        } catch (err) {
+            let errorMsg: string = ITextTranslation.translateText(
+                "ReaderErrorMessages/NoteError", "Ignored erroneous Note."
+            );
+            this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
+        }
+
+        return this.currentNote;
+    }
+    public checkForOpenGraceNotes(): void {
+        if (
+            this.currentStaffEntry !== undefined
+            && this.currentStaffEntry.VoiceEntries.length === 0
+            && this.currentVoiceEntry.graceVoiceEntriesBefore !== undefined
+            && this.currentVoiceEntry.graceVoiceEntriesBefore.length > 0
+        ) {
+            let voice: Voice = this.currentVoiceEntry.ParentVoice;
+            let horizontalIndex: number = this.currentMeasure.VerticalSourceStaffEntryContainers.indexOf(this.currentStaffEntry.VerticalContainerParent);
+            let verticalIndex: number = this.currentStaffEntry.VerticalContainerParent.StaffEntries.indexOf(this.currentStaffEntry);
+            let previousStaffEntry: SourceStaffEntry = this.currentMeasure.getPreviousSourceStaffEntryFromIndex(verticalIndex, horizontalIndex);
+            if (previousStaffEntry !== undefined) {
+                let previousVoiceEntry: VoiceEntry = undefined;
+                for (let idx: number = 0, len: number = previousStaffEntry.VoiceEntries.length; idx < len; ++idx) {
+                    let voiceEntry: VoiceEntry = previousStaffEntry.VoiceEntries[idx];
+                    if (voiceEntry.ParentVoice === voice) {
+                        previousVoiceEntry = voiceEntry;
+                        previousVoiceEntry.graceVoiceEntriesAfter = [];
+                        for (let idx2: number = 0, len2: number = this.currentVoiceEntry.graceVoiceEntriesBefore.length; idx2 < len2; ++idx2) {
+                            let graceVoiceEntry: VoiceEntry = this.currentVoiceEntry.graceVoiceEntriesBefore[idx2];
+                            previousVoiceEntry.graceVoiceEntriesAfter.push(graceVoiceEntry);
+                        }
+                        this.currentVoiceEntry.graceVoiceEntriesBefore = [];
+                        this.currentStaffEntry = undefined;
+                        break;
+                    }
+                }
+            }
+        }
+    }
+    public checkForStaffEntryLink(
+        index: number, currentStaff: Staff, currentStaffEntry: SourceStaffEntry, currentMeasure: SourceMeasure
+    ): SourceStaffEntry {
+        let staffEntryLink: StaffEntryLink = new StaffEntryLink(this.currentVoiceEntry);
+        staffEntryLink.LinkStaffEntries.push(currentStaffEntry);
+        currentStaffEntry.Link = staffEntryLink;
+        let linkMusicTimestamp: Fraction = this.currentVoiceEntry.Timestamp.clone();
+        let verticalSourceStaffEntryContainer: VerticalSourceStaffEntryContainer = currentMeasure.getVerticalContainerByTimestamp(linkMusicTimestamp);
+        currentStaffEntry = verticalSourceStaffEntryContainer.StaffEntries[index];
+        if (currentStaffEntry === undefined) {
+            currentStaffEntry = new SourceStaffEntry(verticalSourceStaffEntryContainer, currentStaff);
+            verticalSourceStaffEntryContainer.StaffEntries[index] = currentStaffEntry;
+        }
+        currentStaffEntry.VoiceEntries.push(this.currentVoiceEntry);
+        staffEntryLink.LinkStaffEntries.push(currentStaffEntry);
+        currentStaffEntry.Link = staffEntryLink;
+        return currentStaffEntry;
+    }
+    public checkForOpenBeam(): void {
+        if (this.openBeam !== undefined && this.currentNote !== undefined) {
+            this.handleOpenBeam();
+        }
+    }
+    public checkOpenTies(): void {
+        let openTieDict: {[key: number]: Tie} = this.openTieDict;
+        for (let key in openTieDict) {
+            if (openTieDict.hasOwnProperty(key)) {
+                let tie: Tie = openTieDict[key];
+                if (Fraction.plus(tie.Start.ParentStaffEntry.Timestamp, tie.Start.Length)
+                        .lt(tie.Start.ParentStaffEntry.VerticalContainerParent.ParentMeasure.Duration)) {
+                    delete openTieDict[key];
+                }
+            }
+        }
+    }
+    public hasVoiceEntry(): boolean {
+        return this.currentVoiceEntry !== undefined;
+    }
+    public getNoteDurationFromType(type: string): Fraction {
+        switch (type) {
+            case "1024th":
+                return new Fraction(1, 1024);
+            case "512th":
+                return new Fraction(1, 512);
+            case "256th":
+                return new Fraction(1, 256);
+            case "128th":
+                return new Fraction(1, 128);
+            case "64th":
+                return new Fraction(1, 64);
+            case "32th":
+            case "32nd":
+                return new Fraction(1, 32);
+            case "16th":
+                return new Fraction(1, 16);
+            case "eighth":
+                return new Fraction(1, 8);
+            case "quarter":
+                return new Fraction(1, 4);
+            case "half":
+                return new Fraction(1, 2);
+            case "whole":
+                return new Fraction(1, 1);
+            case "breve":
+                return new Fraction(2, 1);
+            case "long":
+                return new Fraction(4, 1);
+            case "maxima":
+                return new Fraction(8, 1);
+            default: {
+                let errorMsg: string = ITextTranslation.translateText(
+                    "ReaderErrorMessages/NoteDurationError", "Invalid note duration."
+                );
+                throw new MusicSheetReadingException(errorMsg);
+            }
+        }
+    }
+    // (*)
+    //private readArticulations(notationNode: IXmlElement, currentVoiceEntry: VoiceEntry): void {
+    //    let articNode: IXmlElement;
+    //    if ((articNode = notationNode.element("articulations")) !== undefined)
+    //        this.articulationReader.addArticulationExpression(articNode, currentVoiceEntry);
+    //    let fermaNode: IXmlElement = undefined;
+    //    if ((fermaNode = notationNode.element("fermata")) !== undefined)
+    //        this.articulationReader.addFermata(fermaNode, currentVoiceEntry);
+    //    let tecNode: IXmlElement = undefined;
+    //    if ((tecNode = notationNode.element("technical")) !== undefined)
+    //        this.articulationReader.addTechnicalArticulations(tecNode, currentVoiceEntry);
+    //    let ornaNode: IXmlElement = undefined;
+    //    if ((ornaNode = notationNode.element("ornaments")) !== undefined)
+    //        this.articulationReader.addOrnament(ornaNode, currentVoiceEntry);
+    //}
+    private addSingleNote(
+        node: IXmlElement, noteDuration: number, divisions: number, graceNote: boolean, chord: boolean, guitarPro: boolean
+    ): Note {
+        //Logging.debug("addSingleNote called");
+        let noteAlter: AccidentalEnum = AccidentalEnum.NONE;
+        let noteStep: NoteEnum = NoteEnum.C;
+        let noteOctave: number = 0;
+        let playbackInstrumentId: string = undefined;
+        let xmlnodeElementsArr: IXmlElement[] = node.elements();
+        for (let idx: number = 0, len: number = xmlnodeElementsArr.length; idx < len; ++idx) {
+            let noteElement: IXmlElement = xmlnodeElementsArr[idx];
+            try {
+                if (noteElement.name === "pitch") {
+                    let noteElementsArr: IXmlElement[] = noteElement.elements();
+                    for (let idx2: number = 0, len2: number = noteElementsArr.length; idx2 < len2; ++idx2) {
+                        let pitchElement: IXmlElement = noteElementsArr[idx2];
+                        try {
+                            if (pitchElement.name === "step") {
+                                noteStep = NoteEnum[pitchElement.value];
+                                if (noteStep === undefined) {
+                                    let errorMsg: string = ITextTranslation.translateText(
+                                        "ReaderErrorMessages/NotePitchError",
+                                        "Invalid pitch while reading note."
+                                    );
+                                    this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
+                                    throw new MusicSheetReadingException(errorMsg, undefined);
+                                }
+                            } else if (pitchElement.name === "alter") {
+                                noteAlter = parseInt(pitchElement.value, 10);
+                                if (isNaN(noteAlter)) {
+                                    let errorMsg: string = ITextTranslation.translateText(
+                                        "ReaderErrorMessages/NoteAlterationError", "Invalid alteration while reading note."
+                                    );
+                                    this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
+                                    throw new MusicSheetReadingException(errorMsg, undefined);
+                                }
+
+                            } else if (pitchElement.name === "octave") {
+                                noteOctave = parseInt(pitchElement.value, 10);
+                                if (isNaN(noteOctave)) {
+                                    let errorMsg: string = ITextTranslation.translateText(
+                                        "ReaderErrorMessages/NoteOctaveError", "Invalid octave value while reading note."
+                                    );
+                                    this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
+                                    throw new MusicSheetReadingException(errorMsg, undefined);
+                                }
+                            }
+                        } catch (ex) {
+                            Logging.log("VoiceGenerator.addSingleNote read Step: ", ex.message);
+                        }
+
+                    }
+                } else if (noteElement.name === "unpitched") {
+                    let displayStep: IXmlElement = noteElement.element("display-step");
+                    if (displayStep !== undefined) {
+                        noteStep = NoteEnum[displayStep.value.toUpperCase()];
+                    }
+                    let octave: IXmlElement = noteElement.element("display-octave");
+                    if (octave !== undefined) {
+                        noteOctave = parseInt(octave.value, 10);
+                        if (guitarPro) {
+                            noteOctave += 1;
+                        }
+                    }
+                } else if (noteElement.name === "instrument") {
+                    if (noteElement.firstAttribute !== undefined) {
+                        playbackInstrumentId = noteElement.firstAttribute.value;
+                    }
+                }
+            } catch (ex) {
+                Logging.log("VoiceGenerator.addSingleNote: ", ex);
+            }
+        }
+
+        noteOctave -= Pitch.OctaveXmlDifference;
+        let pitch: Pitch = new Pitch(noteStep, noteOctave, noteAlter);
+        let noteLength: Fraction = new Fraction(noteDuration, divisions);
+        let note: Note = new Note(this.currentVoiceEntry, this.currentStaffEntry, noteLength, pitch);
+        note.PlaybackInstrumentId = playbackInstrumentId;
+        if (!graceNote) {
+            this.currentVoiceEntry.Notes.push(note);
+        } else {
+            this.handleGraceNote(node, note);
+        }
+        if (node.elements("beam") && !chord) {
+            this.createBeam(node, note, graceNote);
+        }
+        return note;
+    }
+    private addRestNote(noteDuration: number, divisions: number): Note {
+        let restFraction: Fraction = new Fraction(noteDuration, divisions);
+        let restNote: Note = new Note(this.currentVoiceEntry, this.currentStaffEntry, restFraction, undefined);
+        this.currentVoiceEntry.Notes.push(restNote);
+        if (this.openBeam !== undefined) {
+            this.openBeam.ExtendedNoteList.push(restNote);
+        }
+        return restNote;
+    }
+    private createBeam(node: IXmlElement, note: Note, grace: boolean): void {
+        try {
+            let beamNode: IXmlElement = node.element("beam");
+            let beamAttr: IXmlAttribute = undefined;
+            if (beamNode !== undefined && beamNode.hasAttributes) {
+                beamAttr = beamNode.attribute("number");
+            }
+            if (beamAttr !== undefined) {
+                let beamNumber: number = parseInt(beamAttr.value, 10);
+                let mainBeamNode: IXmlElement[] = node.elements("beam");
+                let currentBeamTag: string = mainBeamNode[0].value;
+                if (beamNumber === 1 && mainBeamNode !== undefined) {
+                    if (currentBeamTag === "begin" && this.lastBeamTag !== currentBeamTag) {
+                        if (grace) {
+                            if (this.openGraceBeam !== undefined) {
+                                this.handleOpenBeam();
+                            }
+                            this.openGraceBeam = new Beam();
+                        } else {
+                            if (this.openBeam !== undefined) {
+                                this.handleOpenBeam();
+                            }
+                            this.openBeam = new Beam();
+                        }
+                    }
+                    this.lastBeamTag = currentBeamTag;
+                }
+                let sameVoiceEntry: boolean = false;
+                if (grace) {
+                    if (this.openGraceBeam === undefined) { return; }
+                    for (let idx: number = 0, len: number = this.openGraceBeam.Notes.length; idx < len; ++idx) {
+                        let beamNote: Note = this.openGraceBeam.Notes[idx];
+                        if (this.currentVoiceEntry === beamNote.ParentVoiceEntry) {
+                            sameVoiceEntry = true;
+                        }
+                    }
+                    if (!sameVoiceEntry) {
+                        this.openGraceBeam.addNoteToBeam(note);
+                        if (currentBeamTag === "end" && beamNumber === 1) {
+                            this.openGraceBeam = undefined;
+                        }
+                    }
+                } else {
+                    if (this.openBeam === undefined) { return; }
+                    for (let idx: number = 0, len: number = this.openBeam.Notes.length; idx < len; ++idx) {
+                        let beamNote: Note = this.openBeam.Notes[idx];
+                        if (this.currentVoiceEntry === beamNote.ParentVoiceEntry) {
+                            sameVoiceEntry = true;
+                        }
+                    }
+                    if (!sameVoiceEntry) {
+                        this.openBeam.addNoteToBeam(note);
+                        if (currentBeamTag === "end" && beamNumber === 1) {
+                            this.openBeam = undefined;
+                        }
+                    }
+                }
+            }
+        } catch (e) {
+            let errorMsg: string = ITextTranslation.translateText(
+                "ReaderErrorMessages/BeamError", "Error while reading beam."
+            );
+            this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
+            throw new MusicSheetReadingException("", e);
+        }
+
+    }
+    private handleOpenBeam(): void {
+        if (this.openBeam.Notes.length === 1) {
+            let beamNote: Note = this.openBeam.Notes[0];
+            beamNote.NoteBeam = undefined;
+            this.openBeam = undefined;
+            return;
+        }
+        if (this.currentNote === CollectionUtil.last(this.openBeam.Notes)) {
+            this.openBeam = undefined;
+        } else {
+            let beamLastNote: Note = CollectionUtil.last(this.openBeam.Notes);
+            let beamLastNoteStaffEntry: SourceStaffEntry = beamLastNote.ParentStaffEntry;
+            let horizontalIndex: number = this.currentMeasure.getVerticalContainerIndexByTimestamp(beamLastNoteStaffEntry.Timestamp);
+            let verticalIndex: number = beamLastNoteStaffEntry.VerticalContainerParent.StaffEntries.indexOf(beamLastNoteStaffEntry);
+            if (horizontalIndex < this.currentMeasure.VerticalSourceStaffEntryContainers.length - 1) {
+                let nextStaffEntry: SourceStaffEntry = this.currentMeasure.VerticalSourceStaffEntryContainers[horizontalIndex + 1].StaffEntries[verticalIndex];
+                if (nextStaffEntry !== undefined) {
+                    for (let idx: number = 0, len: number = nextStaffEntry.VoiceEntries.length; idx < len; ++idx) {
+                        let voiceEntry: VoiceEntry = nextStaffEntry.VoiceEntries[idx];
+                        if (voiceEntry.ParentVoice === this.voice) {
+                            let candidateNote: Note = voiceEntry.Notes[0];
+                            if (candidateNote.Length <= new Fraction(1, 8)) {
+                                this.openBeam.addNoteToBeam(candidateNote);
+                                this.openBeam = undefined;
+                            } else {
+                                this.openBeam = undefined;
+                            }
+                        }
+                    }
+                }
+            } else {
+                this.openBeam = undefined;
+            }
+        }
+    }
+    private handleGraceNote(node: IXmlElement, note: Note): void {
+        let graceChord: boolean = false;
+        let type: string = "";
+        if (node.elements("type")) {
+            let typeNode: IXmlElement[] = node.elements("type");
+            if (typeNode) {
+                type = typeNode[0].value;
+                try {
+                    note.Length = this.getNoteDurationFromType(type);
+                    note.Length.Numerator = 1;
+                } catch (e) {
+                    let errorMsg: string = ITextTranslation.translateText(
+                        "ReaderErrorMessages/NoteDurationError", "Invalid note duration."
+                    );
+                    this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
+                    throw new MusicSheetReadingException(errorMsg, e);
+                }
+
+            }
+        }
+        let graceNode: IXmlElement = node.element("grace");
+        if (graceNode !== undefined && graceNode.attributes()) {
+            if (graceNode.attribute("slash")) {
+                let slash: string = graceNode.attribute("slash").value;
+                if (slash === "yes") {
+                    note.GraceNoteSlash = true;
+                }
+            }
+        }
+        if (node.element("chord") !== undefined) {
+            graceChord = true;
+        }
+        let graceVoiceEntry: VoiceEntry = undefined;
+        if (!graceChord) {
+            graceVoiceEntry = new VoiceEntry(
+                new Fraction(0, 1), this.currentVoiceEntry.ParentVoice, this.currentStaffEntry
+            );
+            if (this.currentVoiceEntry.graceVoiceEntriesBefore === undefined) {
+                this.currentVoiceEntry.graceVoiceEntriesBefore = [];
+            }
+            this.currentVoiceEntry.graceVoiceEntriesBefore.push(graceVoiceEntry);
+        } else {
+            if (
+                this.currentVoiceEntry.graceVoiceEntriesBefore !== undefined
+                && this.currentVoiceEntry.graceVoiceEntriesBefore.length > 0
+            ) {
+                graceVoiceEntry = CollectionUtil.last(this.currentVoiceEntry.graceVoiceEntriesBefore);
+            }
+        }
+        if (graceVoiceEntry !== undefined) {
+            graceVoiceEntry.Notes.push(note);
+            note.ParentVoiceEntry = graceVoiceEntry;
+        }
+    }
+    private addTuplet(node: IXmlElement, tupletNodeList: IXmlElement[]): number {
+        if (tupletNodeList !== undefined && tupletNodeList.length > 1) {
+            let timeModNode: IXmlElement = node.element("time-modification");
+            if (timeModNode !== undefined) {
+                timeModNode = timeModNode.element("actual-notes");
+            }
+            let tupletNodeListArr: IXmlElement[] = tupletNodeList;
+            for (let idx: number = 0, len: number = tupletNodeListArr.length; idx < len; ++idx) {
+                let tupletNode: IXmlElement = tupletNodeListArr[idx];
+                if (tupletNode !== undefined && tupletNode.attributes()) {
+                    let type: string = tupletNode.attribute("type").value;
+                    if (type === "start") {
+                        let tupletNumber: number = 1;
+                        if (tupletNode.attribute("number")) {
+                            tupletNumber = parseInt(tupletNode.attribute("number").value, 10);
+                        }
+                        let tupletLabelNumber: number = 0;
+                        if (timeModNode !== undefined) {
+                            tupletLabelNumber = parseInt(timeModNode.value, 10);
+                            if (isNaN(tupletLabelNumber)) {
+                                let errorMsg: string = ITextTranslation.translateText(
+                                    "ReaderErrorMessages/TupletNoteDurationError", "Invalid tuplet note duration."
+                                );
+                                this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
+                                throw new MusicSheetReadingException(errorMsg, undefined);
+                            }
+
+                        }
+                        let tuplet: Tuplet = new Tuplet(tupletLabelNumber);
+                        if (this.tupletDict[tupletNumber] !== undefined) {
+                            delete this.tupletDict[tupletNumber];
+                            if (Object.keys(this.tupletDict).length === 0) {
+                                this.openTupletNumber = 0;
+                            } else if (Object.keys(this.tupletDict).length > 1) {
+                                this.openTupletNumber--;
+                            }
+                        }
+                        this.tupletDict[tupletNumber] = tuplet;
+                        let subnotelist: Note[] = [];
+                        subnotelist.push(this.currentNote);
+                        tuplet.Notes.push(subnotelist);
+                        tuplet.Fractions.push(this.getTupletNoteDurationFromType(node));
+                        this.currentNote.NoteTuplet = tuplet;
+                        this.openTupletNumber = tupletNumber;
+                    } else if (type === "stop") {
+                        let tupletNumber: number = 1;
+                        if (tupletNode.attribute("number")) {
+                            tupletNumber = parseInt(tupletNode.attribute("number").value, 10);
+                        }
+                        let tuplet: Tuplet = this.tupletDict[tupletNumber];
+                        if (tuplet !== undefined) {
+                            let subnotelist: Note[] = [];
+                            subnotelist.push(this.currentNote);
+                            tuplet.Notes.push(subnotelist);
+                            tuplet.Fractions.push(this.getTupletNoteDurationFromType(node));
+                            this.currentNote.NoteTuplet = tuplet;
+                            delete this.tupletDict[tupletNumber];
+                            if (Object.keys(this.tupletDict).length === 0) {
+                                this.openTupletNumber = 0;
+                            } else if (Object.keys(this.tupletDict).length > 1) {
+                                this.openTupletNumber--;
+                            }
+                        }
+                    }
+                }
+            }
+        } else if (tupletNodeList[0] !== undefined) {
+            let n: IXmlElement = tupletNodeList[0];
+            if (n.hasAttributes) {
+                let type: string = n.attribute("type").value;
+                let tupletnumber: number = 1;
+                if (n.attribute("number")) {
+                    tupletnumber = parseInt(n.attribute("number").value, 10);
+                }
+                let noTupletNumbering: boolean = isNaN(tupletnumber);
+
+                if (type === "start") {
+                    let tupletLabelNumber: number = 0;
+                    let timeModNode: IXmlElement = node.element("time-modification");
+                    if (timeModNode !== undefined) {
+                        timeModNode = timeModNode.element("actual-notes");
+                    }
+                    if (timeModNode !== undefined) {
+                        tupletLabelNumber = parseInt(timeModNode.value, 10);
+                        if (isNaN(tupletLabelNumber)) {
+                            let errorMsg: string = ITextTranslation.translateText(
+                                "ReaderErrorMessages/TupletNoteDurationError", "Invalid tuplet note duration."
+                            );
+                            this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
+                            throw new MusicSheetReadingException(errorMsg);
+                        }
+
+                    }
+                    if (noTupletNumbering) {
+                        this.openTupletNumber++;
+                        tupletnumber = this.openTupletNumber;
+                    }
+                    let tuplet: Tuplet = this.tupletDict[tupletnumber];
+                    if (tuplet === undefined) {
+                        tuplet = this.tupletDict[tupletnumber] = new Tuplet(tupletLabelNumber);
+                    }
+                    let subnotelist: Note[] = [];
+                    subnotelist.push(this.currentNote);
+                    tuplet.Notes.push(subnotelist);
+                    tuplet.Fractions.push(this.getTupletNoteDurationFromType(node));
+                    this.currentNote.NoteTuplet = tuplet;
+                    this.openTupletNumber = tupletnumber;
+                } else if (type === "stop") {
+                    if (noTupletNumbering) {
+                        tupletnumber = this.openTupletNumber;
+                    }
+                    let tuplet: Tuplet = this.tupletDict[this.openTupletNumber];
+                    if (tuplet !== undefined) {
+                        let subnotelist: Note[] = [];
+                        subnotelist.push(this.currentNote);
+                        tuplet.Notes.push(subnotelist);
+                        tuplet.Fractions.push(this.getTupletNoteDurationFromType(node));
+                        this.currentNote.NoteTuplet = tuplet;
+                        if (Object.keys(this.tupletDict).length === 0) {
+                            this.openTupletNumber = 0;
+                        } else if (Object.keys(this.tupletDict).length > 1) {
+                            this.openTupletNumber--;
+                        }
+                        delete this.tupletDict[tupletnumber];
+                    }
+                }
+            }
+        }
+        return this.openTupletNumber;
+    }
+    private handleTimeModificationNode(noteNode: IXmlElement): void {
+        if (this.openTupletNumber in this.tupletDict) {
+            try {
+                let tuplet: Tuplet = this.tupletDict[this.openTupletNumber];
+                let notes: Note[] = CollectionUtil.last(tuplet.Notes);
+                let lastTupletVoiceEntry: VoiceEntry = notes[0].ParentVoiceEntry;
+                let noteList: Note[];
+                if (lastTupletVoiceEntry.Timestamp === this.currentVoiceEntry.Timestamp) {
+                    noteList = notes;
+                } else {
+                    noteList = [];
+                    tuplet.Notes.push(noteList);
+                    tuplet.Fractions.push(this.getTupletNoteDurationFromType(noteNode));
+                }
+                noteList.push(this.currentNote);
+                this.currentNote.NoteTuplet = tuplet;
+            } catch (ex) {
+                let errorMsg: string = ITextTranslation.translateText(
+                    "ReaderErrorMessages/TupletNumberError", "Invalid tuplet number."
+                );
+                this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
+                throw ex;
+            }
+
+        } else if (this.currentVoiceEntry.Notes.length > 0) {
+            let firstNote: Note = this.currentVoiceEntry.Notes[0];
+            if (firstNote.NoteTuplet !== undefined) {
+                let tuplet: Tuplet = firstNote.NoteTuplet;
+                let notes: Note[] = CollectionUtil.last(tuplet.Notes);
+                notes.push(this.currentNote);
+                this.currentNote.NoteTuplet = tuplet;
+            }
+        }
+    }
+    private addTie(tieNodeList: IXmlElement[], measureStartAbsoluteTimestamp: Fraction, maxTieNoteFraction: Fraction): void {
+        if (tieNodeList !== undefined) {
+            if (tieNodeList.length === 1) {
+                let tieNode: IXmlElement = tieNodeList[0];
+                if (tieNode !== undefined && tieNode.attributes()) {
+                    let type: string = tieNode.attribute("type").value;
+                    try {
+                        if (type === "start") {
+                            let num: number = this.findCurrentNoteInTieDict(this.currentNote);
+                            if (num < 0) {
+                                delete this.openTieDict[num];
+                            }
+                            let newTieNumber: number = this.getNextAvailableNumberForTie();
+                            let tie: Tie = new Tie(this.currentNote);
+                            this.openTieDict[newTieNumber] = tie;
+                            if (this.currentNote.NoteBeam !== undefined) {
+                                if (this.currentNote.NoteBeam.Notes[0] === this.currentNote) {
+                                    tie.BeamStartTimestamp = Fraction.plus(measureStartAbsoluteTimestamp, this.currentVoiceEntry.Timestamp);
+                                } else {
+                                    for (let idx: number = 0, len: number = this.currentNote.NoteBeam.Notes.length; idx < len; ++idx) {
+                                        let note: Note = this.currentNote.NoteBeam.Notes[idx];
+                                        if (note.NoteTie !== undefined && note.NoteTie !== tie && note.NoteTie.BeamStartTimestamp !== undefined) {
+                                            tie.BeamStartTimestamp = note.NoteTie.BeamStartTimestamp;
+                                            break;
+                                        }
+                                    }
+                                    if (this.currentNote === CollectionUtil.last(this.currentNote.NoteBeam.Notes)) {
+                                        tie.BeamStartTimestamp = Fraction.plus(measureStartAbsoluteTimestamp, this.currentVoiceEntry.Timestamp);
+                                    }
+                                }
+                            }
+                        } else if (type === "stop") {
+                            let tieNumber: number = this.findCurrentNoteInTieDict(this.currentNote);
+                            let tie: Tie = this.openTieDict[tieNumber];
+                            if (tie !== undefined) {
+                                let tieStartNote: Note = tie.Start;
+                                tieStartNote.NoteTie = tie;
+                                tieStartNote.Length.Add(this.currentNote.Length);
+                                tie.Fractions.push(this.currentNote.Length);
+                                if (maxTieNoteFraction.lt(Fraction.plus(this.currentStaffEntry.Timestamp, this.currentNote.Length))) {
+                                    maxTieNoteFraction = Fraction.plus(this.currentStaffEntry.Timestamp, this.currentNote.Length);
+                                }
+                                let i: number = this.currentVoiceEntry.Notes.indexOf(this.currentNote);
+                                if (i !== -1) { this.currentVoiceEntry.Notes.splice(i, 1); }
+                                if (
+                                    this.currentVoiceEntry.Articulations.length === 1
+                                    && this.currentVoiceEntry.Articulations[0] === ArticulationEnum.fermata
+                                    && tieStartNote.ParentVoiceEntry.Articulations[ArticulationEnum.fermata] === undefined
+                                ) {
+                                    tieStartNote.ParentVoiceEntry.Articulations.push(ArticulationEnum.fermata);
+                                }
+                                if (this.currentNote.NoteBeam !== undefined) {
+                                    let noteBeamIndex: number = this.currentNote.NoteBeam.Notes.indexOf(this.currentNote);
+                                    if (noteBeamIndex === 0 && tie.BeamStartTimestamp === undefined) {
+                                        tie.BeamStartTimestamp = Fraction.plus(measureStartAbsoluteTimestamp, this.currentVoiceEntry.Timestamp);
+                                    }
+                                    let noteBeam: Beam = this.currentNote.NoteBeam;
+                                    noteBeam.Notes[noteBeamIndex] = tieStartNote;
+                                    tie.TieBeam = noteBeam;
+                                }
+                                if (this.currentNote.NoteTuplet !== undefined) {
+                                    let noteTupletIndex: number = this.currentNote.NoteTuplet.getNoteIndex(this.currentNote);
+                                    let index: number = this.currentNote.NoteTuplet.Notes[noteTupletIndex].indexOf(this.currentNote);
+                                    let noteTuplet: Tuplet = this.currentNote.NoteTuplet;
+                                    noteTuplet.Notes[noteTupletIndex][index] = tieStartNote;
+                                    tie.TieTuplet = noteTuplet;
+                                }
+                                for (let idx: number = 0, len: number = this.currentNote.NoteSlurs.length; idx < len; ++idx) {
+                                    let slur: Slur = this.currentNote.NoteSlurs[idx];
+                                    if (slur.StartNote === this.currentNote) {
+                                        slur.StartNote = tie.Start;
+                                        slur.StartNote.NoteSlurs.push(slur);
+                                    }
+                                    if (slur.EndNote === this.currentNote) {
+                                        slur.EndNote = tie.Start;
+                                        slur.EndNote.NoteSlurs.push(slur);
+                                    }
+                                }
+                                let lyricsEntries: Dictionary<number, LyricsEntry> = this.currentVoiceEntry.LyricsEntries;
+                                for (let lyricsEntry in lyricsEntries) {
+                                    if (lyricsEntries.hasOwnProperty(lyricsEntry)) {
+                                        let val: LyricsEntry = this.currentVoiceEntry.LyricsEntries[lyricsEntry];
+                                        if (!tieStartNote.ParentVoiceEntry.LyricsEntries.hasOwnProperty(lyricsEntry)) {
+                                            tieStartNote.ParentVoiceEntry.LyricsEntries[lyricsEntry] = val;
+                                            val.Parent = tieStartNote.ParentVoiceEntry;
+                                        }
+                                    }
+                                }
+                                delete this.openTieDict[tieNumber];
+                            }
+                        }
+                    } catch (err) {
+                        let errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/TieError", "Error while reading tie.");
+                        this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
+                    }
+
+                }
+            } else if (tieNodeList.length === 2) {
+                let tieNumber: number = this.findCurrentNoteInTieDict(this.currentNote);
+                if (tieNumber >= 0) {
+                    let tie: Tie = this.openTieDict[tieNumber];
+                    let tieStartNote: Note = tie.Start;
+                    tieStartNote.Length.Add(this.currentNote.Length);
+                    tie.Fractions.push(this.currentNote.Length);
+                    if (this.currentNote.NoteBeam !== undefined) {
+                        let noteBeamIndex: number = this.currentNote.NoteBeam.Notes.indexOf(this.currentNote);
+                        if (noteBeamIndex === 0 && tie.BeamStartTimestamp === undefined) {
+                            tie.BeamStartTimestamp = Fraction.plus(measureStartAbsoluteTimestamp, this.currentVoiceEntry.Timestamp);
+                        }
+                        let noteBeam: Beam = this.currentNote.NoteBeam;
+                        noteBeam.Notes[noteBeamIndex] = tieStartNote;
+                        tie.TieBeam = noteBeam;
+                    }
+                    for (let idx: number = 0, len: number = this.currentNote.NoteSlurs.length; idx < len; ++idx) {
+                        let slur: Slur = this.currentNote.NoteSlurs[idx];
+                        if (slur.StartNote === this.currentNote) {
+                            slur.StartNote = tie.Start;
+                            slur.StartNote.NoteSlurs.push(slur);
+                        }
+                        if (slur.EndNote === this.currentNote) {
+                            slur.EndNote = tie.Start;
+                            slur.EndNote.NoteSlurs.push(slur);
+                        }
+                    }
+
+                    this.currentVoiceEntry.LyricsEntries.forEach((key: number, value: LyricsEntry): void => {
+                        if (!tieStartNote.ParentVoiceEntry.LyricsEntries.containsKey(key)) {
+                            tieStartNote.ParentVoiceEntry.LyricsEntries.setValue(key, value);
+                            value.Parent = tieStartNote.ParentVoiceEntry;
+                        }
+                    });
+                    if (maxTieNoteFraction.lt(Fraction.plus(this.currentStaffEntry.Timestamp, this.currentNote.Length))) {
+                        maxTieNoteFraction = Fraction.plus(this.currentStaffEntry.Timestamp, this.currentNote.Length);
+                    }
+                    // delete currentNote from Notes:
+                    let i: number = this.currentVoiceEntry.Notes.indexOf(this.currentNote);
+                    if (i !== -1) { this.currentVoiceEntry.Notes.splice(i, 1); }
+                }
+            }
+        }
+    }
+    private getNextAvailableNumberForTie(): number {
+        let keys: string[] = Object.keys(this.openTieDict);
+        if (keys.length === 0) { return 1; }
+        keys.sort((a, b) => (+a - +b)); // FIXME Andrea: test
+        for (let i: number = 0; i < keys.length; i++) {
+            if ("" + (i + 1) !== keys[i]) {
+                return i + 1;
+            }
+        }
+        return +(keys[keys.length - 1]) + 1;
+    }
+    private findCurrentNoteInTieDict(candidateNote: Note): number {
+        let openTieDict: { [_: number]: Tie; } = this.openTieDict;
+        for (let key in openTieDict) {
+            if (openTieDict.hasOwnProperty(key)) {
+                let tie: Tie = openTieDict[key];
+                if (tie.Start.Pitch.FundamentalNote === candidateNote.Pitch.FundamentalNote && tie.Start.Pitch.Octave === candidateNote.Pitch.Octave) {
+                    return +key;
+                }
+            }
+        }
+        return -1;
+    }
+    private getTupletNoteDurationFromType(xmlNode: IXmlElement): Fraction {
+        if (xmlNode.element("type") !== undefined) {
+            let typeNode: IXmlElement = xmlNode.element("type");
+            if (typeNode !== undefined) {
+                let type: string = typeNode.value;
+                try {
+                    return this.getNoteDurationFromType(type);
+                } catch (e) {
+                    let errorMsg: string = ITextTranslation.translateText("ReaderErrorMessages/NoteDurationError", "Invalid note duration.");
+                    this.musicSheet.SheetErrors.pushMeasureError(errorMsg);
+                    throw new MusicSheetReadingException("", e);
+                }
+
+            }
+        }
+        return undefined;
+    }
+}

+ 114 - 0
src/MusicalScore/SubInstrument.ts

@@ -0,0 +1,114 @@
+import {Instrument} from "./Instrument";
+import {MidiInstrument} from "./VoiceData/Instructions/ClefInstruction";
+
+export class SubInstrument {
+    constructor(parentInstrument: Instrument) {
+        this.parentInstrument = parentInstrument;
+        this.fixedKey = -1;
+        this.name = this.parseMidiInstrument(this.parentInstrument.Name);
+        this.midiInstrumentID = SubInstrument.midiInstrument[this.name];
+        this.volume = 1.0;
+    }
+    private static midiInstrument: { [key: string]: MidiInstrument; } = {
+        "cello": MidiInstrument.Cello,
+        "violon-c": MidiInstrument.Cello,
+        "contrabass": MidiInstrument.Contrabass,
+        "kontrabass": MidiInstrument.Contrabass,
+        "clarinet": MidiInstrument.Clarinet,
+        "klarinette": MidiInstrument.Clarinet,
+        "flute": MidiInstrument.Flute,
+        "flöte": MidiInstrument.Flute,
+        "frenchhorn": MidiInstrument.French_Horn,
+        "guitar": MidiInstrument.Acoustic_Guitar_nylon,
+        "gitarre": MidiInstrument.Acoustic_Guitar_nylon,
+        "harp": MidiInstrument.Orchestral_Harp,
+        "harfe": MidiInstrument.Orchestral_Harp,
+        "oboe": MidiInstrument.Oboe,
+        "organ": MidiInstrument.Church_Organ,
+        "orgue": MidiInstrument.Church_Organ,
+        "orgel": MidiInstrument.Church_Organ,
+        "piano": MidiInstrument.Acoustic_Grand_Piano,
+        "klavier": MidiInstrument.Acoustic_Grand_Piano,
+        "piccolo": MidiInstrument.Piccolo,
+        "strings": MidiInstrument.String_Ensemble_1,
+        "streicher": MidiInstrument.String_Ensemble_1,
+        "steeldrum": MidiInstrument.Steel_Drums,
+        "trombone": MidiInstrument.Trombone,
+        "posaune": MidiInstrument.Trombone,
+        "brass": MidiInstrument.Trombone,
+        "trumpet": MidiInstrument.Trumpet,
+        "trompete": MidiInstrument.Trumpet,
+        "tpt": MidiInstrument.Trumpet,
+        "tuba": MidiInstrument.Tuba,
+        "sax": MidiInstrument.Tenor_Sax,
+        "viola": MidiInstrument.Viola,
+        "bratsche": MidiInstrument.Viola,
+        "violin": MidiInstrument.Violin,
+        "violon.": MidiInstrument.Violin,
+        "woodblock": MidiInstrument.Woodblock,
+        "alt": MidiInstrument.Synth_Voice,
+        "alto": MidiInstrument.Synth_Voice,
+        "tenor": MidiInstrument.Synth_Voice,
+        "bariton": MidiInstrument.Synth_Voice,
+        "baritone": MidiInstrument.Synth_Voice,
+        "bass": MidiInstrument.Synth_Voice,
+        "sopran": MidiInstrument.Synth_Voice,
+        "voice": MidiInstrument.Synth_Voice,
+        "recorder": MidiInstrument.Recorder,
+        "blockflöte": MidiInstrument.Recorder,
+        "banjo": MidiInstrument.Banjo,
+        "drums": MidiInstrument.Percussion,
+        "percussion": MidiInstrument.Percussion,
+        "schlagzeug": MidiInstrument.Percussion,
+        "schlagwerk": MidiInstrument.Percussion,
+        "unnamed": MidiInstrument.Acoustic_Grand_Piano,
+    };
+
+    public idString: string;
+    public midiInstrumentID: MidiInstrument;
+    public volume: number;
+    public pan: number;
+    public fixedKey: number;
+    public name: string;
+
+    private parentInstrument: Instrument;
+
+    public get ParentInstrument(): Instrument {
+        return this.parentInstrument;
+    }
+    public static isPianoInstrument(instrument: MidiInstrument): boolean {
+        return (instrument === MidiInstrument.Acoustic_Grand_Piano
+          || instrument === MidiInstrument.Bright_Acoustic_Piano
+          || instrument === MidiInstrument.Electric_Grand_Piano
+          || instrument === MidiInstrument.Electric_Piano_1
+          || instrument === MidiInstrument.Electric_Piano_2);
+    }
+    public setMidiInstrument(instrumentType: string): void {
+        this.midiInstrumentID = SubInstrument.midiInstrument[this.parseMidiInstrument(instrumentType)];
+    }
+
+    private parseMidiInstrument(instrumentType: string): string {
+        // FIXME: test this function
+        try {
+            if (instrumentType) {
+                let tmpName: string = instrumentType.toLowerCase().trim();
+                for (let key in SubInstrument.midiInstrument) {
+                    if (tmpName.indexOf(key) !== -1) {
+                        return key;
+                    }
+                }
+            }
+            if (this.parentInstrument.Name) {
+                let tmpName: string = this.parentInstrument.Name.toLowerCase().trim();
+                for (let key in SubInstrument.midiInstrument) {
+                    if (tmpName.indexOf(key) !== -1) {
+                        return key;
+                    }
+                }
+            }
+        } catch (e) {
+            console.log("Error parsing MIDI Instrument. Default to Grand Piano."); // FIXME
+        }
+        return "unnamed";
+    }
+}

+ 36 - 0
src/MusicalScore/VoiceData/Beam.ts

@@ -0,0 +1,36 @@
+import {Note} from "./Note";
+
+export class Beam {
+    private notes: Note[] = [];
+    private extendedNoteList: Note[] = [];
+
+    public get Notes(): Note[] {
+        return this.notes;
+    }
+    public set Notes(value: Note[]) {
+        this.notes = value;
+    }
+    public get ExtendedNoteList(): Note[] {
+        return this.extendedNoteList;
+    }
+    public set ExtendedNoteList(value: Note[]) {
+        this.extendedNoteList = value;
+    }
+
+    public addNoteToBeam(note: Note): void {
+        if (note !== undefined) {
+            note.NoteBeam = this;
+            this.notes.push(note);
+            this.extendedNoteList.push(note);
+        }
+    }
+}
+
+export enum BeamEnum {
+    BeamNone = -1,
+    BeamBegin = 0,
+    BeamContinue = 1,
+    BeamEnd = 2,
+    BeamForward = 3,
+    BeamBackward = 4,
+}

+ 246 - 0
src/MusicalScore/VoiceData/ChordSymbolContainer.ts

@@ -0,0 +1,246 @@
+import {Pitch} from "../../Common/DataObjects/pitch";
+import {KeyInstruction} from "./Instructions/KeyInstruction";
+import {MusicSheetCalculator} from "../Graphical/MusicSheetCalculator";
+import {AccidentalEnum} from "../../Common/DataObjects/pitch";
+export class ChordSymbolContainer {
+    private rootPitch: Pitch;
+    private chordKind: ChordSymbolEnum;
+    private bassPitch: Pitch;
+    private degree: Degree;
+    private keyInstruction: KeyInstruction;
+
+    constructor(rootPitch: Pitch, chordKind: ChordSymbolEnum, bassPitch: Pitch, chordDegree: Degree, keyInstruction: KeyInstruction) {
+        this.rootPitch = rootPitch;
+        this.chordKind = chordKind;
+        this.keyInstruction = keyInstruction;
+        this.bassPitch = bassPitch;
+        this.degree = chordDegree;
+    }
+
+    public get RootPitch(): Pitch {
+        return this.rootPitch;
+    }
+
+    public get ChordKind(): ChordSymbolEnum {
+        return this.chordKind;
+    }
+
+    public get BassPitch(): Pitch {
+        return this.bassPitch;
+    }
+
+    public get ChordDegree(): Degree {
+        return this.degree;
+    }
+
+    public get KeyInstruction(): KeyInstruction {
+        return this.keyInstruction;
+    }
+
+    public static calculateChordText(chordSymbol: ChordSymbolContainer, transposeHalftones: number): string {
+        let transposedRootPitch: Pitch = chordSymbol.RootPitch;
+        if (MusicSheetCalculator.transposeCalculator !== undefined) {
+            transposedRootPitch = MusicSheetCalculator.transposeCalculator.transposePitch(
+                chordSymbol.RootPitch,
+                chordSymbol.KeyInstruction,
+                transposeHalftones
+            );
+        }
+        let text: string = Pitch.getNoteEnumString(transposedRootPitch.FundamentalNote);
+        if (transposedRootPitch.Accidental !== AccidentalEnum.NONE) {
+            text += this.getTextForAccidental(transposedRootPitch.Accidental);
+        }
+        text += ChordSymbolContainer.getTextFromChordKindEnum(chordSymbol.ChordKind);
+        if (chordSymbol.BassPitch !== undefined) {
+            let transposedBassPitch: Pitch = chordSymbol.BassPitch;
+            if (MusicSheetCalculator.transposeCalculator !== undefined) {
+                transposedBassPitch = MusicSheetCalculator.transposeCalculator.transposePitch(
+                    chordSymbol.BassPitch,
+                    chordSymbol.KeyInstruction,
+                    transposeHalftones
+                );
+            }
+            text += "/";
+            text += Pitch.getNoteEnumString(transposedBassPitch.FundamentalNote);
+            text += this.getTextForAccidental(transposedBassPitch.Accidental);
+        }
+        if (chordSymbol.ChordDegree !== undefined) {
+            switch (chordSymbol.ChordDegree.text) {
+                case ChordDegreeText.add:
+                    text += "add";
+                    break;
+                case ChordDegreeText.alter:
+                    text += "alt";
+                    break;
+                case ChordDegreeText.subtract:
+                    text += "sub";
+                    break;
+                default:
+            }
+            text += chordSymbol.ChordDegree.value;
+            if (chordSymbol.ChordDegree.alteration !== AccidentalEnum.NONE) {
+                text += ChordSymbolContainer.getTextForAccidental(chordSymbol.ChordDegree.alteration);
+            }
+        }
+        return text;
+    }
+
+    private static getTextForAccidental(alteration: AccidentalEnum): string {
+        let text: string = "";
+        switch (alteration) {
+            case AccidentalEnum.DOUBLEFLAT:
+                text += "bb";
+                break;
+            case AccidentalEnum.FLAT:
+                text += "b";
+                break;
+            case AccidentalEnum.SHARP:
+                text += "#";
+                break;
+            case AccidentalEnum.DOUBLESHARP:
+                text += "x";
+                break;
+            default:
+        }
+        return text;
+    }
+
+    private static getTextFromChordKindEnum(kind: ChordSymbolEnum): string {
+        let text: string = "";
+        switch (kind) {
+            case ChordSymbolEnum.major:
+                break;
+            case ChordSymbolEnum.minor:
+                text += "m";
+                break;
+            case ChordSymbolEnum.augmented:
+                text += "aug";
+                break;
+            case ChordSymbolEnum.diminished:
+                text += "dim";
+                break;
+            case ChordSymbolEnum.dominant:
+                text += "7";
+                break;
+            case ChordSymbolEnum.majorseventh:
+                text += "maj7";
+                break;
+            case ChordSymbolEnum.minorseventh:
+                text += "m7";
+                break;
+            case ChordSymbolEnum.diminishedseventh:
+                text += "dim7";
+                break;
+            case ChordSymbolEnum.augmentedseventh:
+                text += "aug7";
+                break;
+            case ChordSymbolEnum.halfdiminished:
+                text += "m7b5";
+                break;
+            case ChordSymbolEnum.majorminor:
+                text += "";
+                break;
+            case ChordSymbolEnum.majorsixth:
+                text += "maj6";
+                break;
+            case ChordSymbolEnum.minorsixth:
+                text += "m6";
+                break;
+            case ChordSymbolEnum.dominantninth:
+                text += "9";
+                break;
+            case ChordSymbolEnum.majorninth:
+                text += "maj9";
+                break;
+            case ChordSymbolEnum.minorninth:
+                text += "m9";
+                break;
+            case ChordSymbolEnum.dominant11th:
+                text += "11";
+                break;
+            case ChordSymbolEnum.major11th:
+                text += "maj11";
+                break;
+            case ChordSymbolEnum.minor11th:
+                text += "m11";
+                break;
+            case ChordSymbolEnum.dominant13th:
+                text += "13";
+                break;
+            case ChordSymbolEnum.major13th:
+                text += "maj13";
+                break;
+            case ChordSymbolEnum.minor13th:
+                text += "m13";
+                break;
+            case ChordSymbolEnum.suspendedsecond:
+                text += "sus2";
+                break;
+            case ChordSymbolEnum.suspendedfourth:
+                text += "sus4";
+                break;
+            case ChordSymbolEnum.Neapolitan:
+            case ChordSymbolEnum.Italian:
+            case ChordSymbolEnum.French:
+            case ChordSymbolEnum.German:
+            case ChordSymbolEnum.pedal:
+            case ChordSymbolEnum.power:
+            case ChordSymbolEnum.Tristan:
+                break;
+            default:
+                break;
+        }
+        return text;
+    }
+}
+export class Degree {
+    constructor(value: number, alteration: AccidentalEnum, text: ChordDegreeText) {
+        this.value = value;
+        this.alteration = alteration;
+        this.text = text;
+    }
+
+    public value: number;
+    public alteration: AccidentalEnum;
+    public text: ChordDegreeText;
+}
+
+export enum ChordDegreeText {
+    add,
+    alter,
+    subtract
+}
+
+export enum ChordSymbolEnum {
+    major,
+    minor,
+    augmented,
+    diminished,
+    dominant,
+    majorseventh,
+    minorseventh,
+    diminishedseventh,
+    augmentedseventh,
+    halfdiminished,
+    majorminor,
+    majorsixth,
+    minorsixth,
+    dominantninth,
+    majorninth,
+    minorninth,
+    dominant11th,
+    major11th,
+    minor11th,
+    dominant13th,
+    major13th,
+    minor13th,
+    suspendedsecond,
+    suspendedfourth,
+    Neapolitan,
+    Italian,
+    French,
+    German,
+    pedal,
+    power,
+    Tristan
+}

+ 76 - 0
src/MusicalScore/VoiceData/Expressions/ContinuousExpressions/Slur.ts

@@ -0,0 +1,76 @@
+import {Note} from "../../Note";
+import {Fraction} from "../../../../Common/DataObjects/fraction";
+
+export class Slur {
+    constructor() {
+        // ?
+    }
+
+    private startNote: Note;
+    private endNote: Note;
+
+    public get StartNote(): Note {
+        return this.startNote;
+    }
+    public set StartNote(value: Note) {
+        this.startNote = value;
+    }
+    public get EndNote(): Note {
+        return this.endNote;
+    }
+    public set EndNote(value: Note) {
+        this.endNote = value;
+    }
+    public startNoteHasMoreStartingSlurs(): boolean {
+        if (this.startNote === undefined) { return false; }
+        for (let idx: number = 0, len: number = this.startNote.NoteSlurs.length; idx < len; ++idx) {
+            let slur: Slur = this.startNote.NoteSlurs[idx];
+            if (slur !== this && slur.StartNote === this.startNote) {
+                return true;
+            }
+        }
+        return false;
+    }
+    public endNoteHasMoreEndingSlurs(): boolean {
+        if (this.endNote === undefined) { return false; }
+        for (let idx: number = 0, len: number = this.endNote.NoteSlurs.length; idx < len; ++idx) {
+            let slur: Slur = this.endNote.NoteSlurs[idx];
+            if (slur !== this && slur.EndNote === this.endNote) {
+                return true;
+            }
+        }
+        return false;
+    }
+    public isCrossed(): boolean {
+        return (this.startNote.ParentStaffEntry.ParentStaff !== this.endNote.ParentStaffEntry.ParentStaff);
+    }
+    public isSlurLonger(): boolean {
+        if (this.endNote === undefined || this.startNote === undefined) {
+            return false;
+        }
+        let length: Fraction = Fraction.minus(this.endNote.getAbsoluteTimestamp(), this.startNote.getAbsoluteTimestamp());
+        for (let idx: number = 0, len: number = this.startNote.NoteSlurs.length; idx < len; ++idx) {
+            let slur: Slur = this.startNote.NoteSlurs[idx];
+            if (
+                slur !== this
+                && slur.EndNote !== undefined
+                && slur.StartNote !== undefined
+                && Fraction.minus(slur.EndNote.getAbsoluteTimestamp(), slur.StartNote.getAbsoluteTimestamp()).CompareTo(length) === -1
+            ) {
+                return true;
+            }
+        }
+        for (let idx: number = 0, len: number = this.endNote.NoteSlurs.length; idx < len; ++idx) {
+            let slur: Slur = this.endNote.NoteSlurs[idx];
+            if (
+                slur !== this
+                && slur.EndNote !== undefined
+                && slur.StartNote !== undefined
+                && Fraction.minus(slur.EndNote.getAbsoluteTimestamp(), slur.StartNote.getAbsoluteTimestamp()).CompareTo(length)
+            ) {
+                return true;
+            }
+        }
+        return false;
+    }
+}

+ 126 - 0
src/MusicalScore/VoiceData/Expressions/ContinuousExpressions/continuousDynamicExpression.ts

@@ -0,0 +1,126 @@
+import {PlacementEnum, AbstractExpression} from "../abstractExpression";
+import {MultiExpression} from "../multiExpression";
+import {Fraction} from "../../../../Common/DataObjects/fraction";
+
+export class ContinuousDynamicExpression extends AbstractExpression {
+    //constructor(placement: PlacementEnum, staffNumber: number, label: string) {
+    //    this.label = label;
+    //    this.placement = placement;
+    //    this.staffNumber = staffNumber;
+    //    this.startVolume = -1;
+    //    this.endVolume = -1;
+    //    this.setType();
+    //}
+    constructor(dynamicType: ContDynamicEnum, placement: PlacementEnum, staffNumber: number, label: string) {
+        super();
+        this.dynamicType = dynamicType;
+        this.label = label;
+        this.placement = placement;
+        this.staffNumber = staffNumber;
+        this.startVolume = -1;
+        this.endVolume = -1;
+        this.setType();
+    }
+
+    private static listContinuousDynamicIncreasing: string[] = ["crescendo", "cresc", "cresc.", "cres."];
+    private static listContinuousDynamicDecreasing: string[] = ["decrescendo", "decresc", "decr.", "diminuendo", "dim.", "dim"];
+    // private static listContinuousDynamicGeneral: string[] = ["subito","al niente","piu","meno"];
+    private dynamicType: ContDynamicEnum;
+    private startMultiExpression: MultiExpression;
+    private endMultiExpression: MultiExpression;
+    private startVolume: number;
+    private endVolume: number;
+    private placement: PlacementEnum;
+    private staffNumber: number;
+    private label: string;
+
+    public get DynamicType(): ContDynamicEnum {
+        return this.dynamicType;
+    }
+    public set DynamicType(value: ContDynamicEnum) {
+        this.dynamicType = value;
+    }
+    public get StartMultiExpression(): MultiExpression {
+        return this.startMultiExpression;
+    }
+    public set StartMultiExpression(value: MultiExpression) {
+        this.startMultiExpression = value;
+    }
+    public get EndMultiExpression(): MultiExpression {
+        return this.endMultiExpression;
+    }
+    public set EndMultiExpression(value: MultiExpression) {
+        this.endMultiExpression = value;
+    }
+    public get Placement(): PlacementEnum {
+        return this.placement;
+    }
+    public set Placement(value: PlacementEnum) {
+        this.placement = value;
+    }
+    public get StartVolume(): number {
+        return this.startVolume;
+    }
+    public set StartVolume(value: number) {
+        this.startVolume = value;
+    }
+    public get EndVolume(): number {
+        return this.endVolume;
+    }
+    public set EndVolume(value: number) {
+        this.endVolume = value;
+    }
+    public get StaffNumber(): number {
+        return this.staffNumber;
+    }
+    public set StaffNumber(value: number) {
+        this.staffNumber = value;
+    }
+    public get Label(): string {
+        return this.label;
+    }
+    public set Label(value: string) {
+        this.label = value;
+        this.setType();
+    }
+    public static isInputStringContinuousDynamic(inputString: string): boolean {
+        if (inputString === undefined) { return false; }
+        return (
+            ContinuousDynamicExpression.isStringInStringList(ContinuousDynamicExpression.listContinuousDynamicIncreasing, inputString)
+            || ContinuousDynamicExpression.isStringInStringList(ContinuousDynamicExpression.listContinuousDynamicDecreasing, inputString)
+        );
+    }
+    public getInterpolatedDynamic(currentAbsoluteTimestamp: Fraction): number {
+        let continuousAbsoluteStartTimestamp: Fraction = this.StartMultiExpression.AbsoluteTimestamp;
+        let continuousAbsoluteEndTimestamp: Fraction;
+        if (this.EndMultiExpression !== undefined) {
+            continuousAbsoluteEndTimestamp = this.EndMultiExpression.AbsoluteTimestamp;
+        } else {
+            continuousAbsoluteEndTimestamp = Fraction.plus(
+                this.startMultiExpression.SourceMeasureParent.AbsoluteTimestamp, this.startMultiExpression.SourceMeasureParent.Duration
+            );
+        }
+        if (currentAbsoluteTimestamp.lt(continuousAbsoluteStartTimestamp)) { return -1; }
+        if (currentAbsoluteTimestamp.lt(continuousAbsoluteEndTimestamp)) { return -2; }
+        let interpolationRatio: number =
+            Fraction.minus(currentAbsoluteTimestamp, continuousAbsoluteStartTimestamp).RealValue
+            / Fraction.minus(continuousAbsoluteEndTimestamp, continuousAbsoluteStartTimestamp).RealValue;
+        let interpolatedVolume: number = Math.max(0.0, Math.min(99.9, this.startVolume + (this.endVolume - this.startVolume) * interpolationRatio));
+        return interpolatedVolume;
+    }
+    public isWedge(): boolean {
+        return this.label === undefined;
+    }
+    private setType(): void {
+        if (ContinuousDynamicExpression.isStringInStringList(ContinuousDynamicExpression.listContinuousDynamicIncreasing, this.label)) {
+            this.dynamicType = ContDynamicEnum.crescendo;
+        } else if (ContinuousDynamicExpression.isStringInStringList(ContinuousDynamicExpression.listContinuousDynamicDecreasing, this.label)) {
+            this.dynamicType = ContDynamicEnum.diminuendo;
+        }
+    }
+}
+
+export enum ContDynamicEnum {
+    crescendo = 0,
+    diminuendo = 1
+}

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác